I am trying to convert a solution using EntLib into using AppFabric caching. By help of a few extension methods this is a fairly pain-free process.
Extension methods I use:
public static bool Contains(this DataCache dataCache, string key)
{
return dataCache.Get(key) != null;
}
public static object GetData(this DataCache dataCache, string key)
{
return dataCache.Get(key);
}
But there are two features of EntLib I find difficult to convert. Namely "Count" (counting number of keys in cache) and "Flush" (removing all data from cache). Both could be solved if I could iterate the keys in cache.
There is a method called ClearRegion(string region), but that required me to specify a region name on all Get/Put/Add-methods I use, which would require some manual error-prone work.
Is there any way to get a list of keys in cache?
Is there a default region name I can use?
How can I flush the cache when I haven't used a region name?
See my previous answer for my speculation as to how the cache works internally when you don't specify a region, and how you can get the count of objects that aren't in a named region.
We can build a Flush method using the same technique:
public void Flush (this DataCache cache)
{
foreach (string regionName in cache.GetSystemRegions())
{
cache.ClearRegion(regionName)
}
}
As I said there, I think named regions are probably the way to go - it seems to me that using them solves more problems than it creates.
If anyone will have problems in future (like me) - here is the full code for clearing cache.
private static DataCacheFactory _factory;
private const String serverName = "<machineName>";
private const String cacheName = "<cacheName>";
static void Main(string[] args)
{
Dictionary<String, Int32> cacheHostsAndPorts = new Dictionary<String, Int32> { { serverName, 22233 } };
Initialize(cacheHostsAndPorts);
DataCache cache = _factory.GetCache(cacheName);
FlushCache(cache);
Console.WriteLine("Done");
Console.ReadLine();
}
private static void FlushCache(DataCache cache)
{
foreach (string regionName in cache.GetSystemRegions())
{
cache.ClearRegion(regionName);
}
}
public static void Initialize(Dictionary<String, Int32> cacheHostsAndPorts)
{
var factoryConfig = new DataCacheFactoryConfiguration
{
Servers = cacheHostsAndPorts.Select(cacheEndpoint => new DataCacheServerEndpoint(cacheEndpoint.Key, cacheEndpoint.Value))
};
_factory = new DataCacheFactory(factoryConfig);
}
Related
I have created a Repository on top of Entity Framework Core, but have some issues with how it's done.
This is an example:
public class StockPricesRepository : IStockPricesRepository
{
StockPricesDbContext _stockPricesDbContext;
ILogger _logger;
public StockPricesRepository(StockPricesDbContext stockPricesDbContext, ILogger logger)
{
_stockPricesDbContext = stockPricesDbContext;
_logger = logger;
}
public void Add(StockPrice stockPrice)
{
_stockPricesDbContext.Add(stockPrice);
_stockPricesDbContext.SaveChanges();
}
public void AddOrUpdate(StockPrice stockPrice)
{
if (!Exists(stockPrice))
_stockPricesDbContext.Add(stockPrice);
else
_stockPricesDbContext.Update(stockPrice);
_stockPricesDbContext.SaveChanges();
}
private bool Exists(StockPrice stockPrice)
{
StockPrice existingStockPrice = Get(stockPrice.Ticker, stockPrice.Exchange, stockPrice.Date, stockPrice.DataProvider);
return (existingStockPrice != null);
}
public StockPrice Get(string ticker, string exchange, DateTime date, string providerName)
{
StockPrice stockPrice = null;
stockPrice =
(from sp in _stockPricesDbContext.StockPrices
where (
(sp.Ticker == ticker) &
(sp.Exchange == exchange) &
(sp.Date == date) &
(sp.DataProvider == providerName))
select sp).AsNoTracking().FirstOrDefault();
return stockPrice;
}
}
}
The StockPricesDbContext is injected into the constructur using dependency injection like this:
services.AddDbContext<StockPricesDbContext>(options => options.UseSqlServer(connectionString));
The problem with the current design is that I get some issues with multiple calls into the repository within the lifetime of the StockPricesDbContext object (even if I have AsNoTracking() on the Get method):
System.InvalidOperationException: 'The instance of entity type 'StockPrice' cannot be tracked because another instance with the same key value for {'Date', 'Ticker', 'Exchange', 'DataProvider'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
To avoid this problem I would maybe prefer to have each of the repository methods embedded in a using block like this:
using (var db = new StockPricesDbContext())
{
}
The problem with this approach is that I don't get the StockPricesDbContext from dependency injection and the connection string from AddDbContext is lost.
One workaround for that might be to get the connection string from the constructor of the repository like this:
private string _connectionString;
public StockPricesRepository(StockPricesDbContext stockPricesDbContext, ILogger logger)
{
_stockPricesDbContext = stockPricesDbContext;
_connectionString = stockPricesDbContext.Database.GetDbConnection().ConnectionString;
_logger = logger;
}
I would also need to add this constructor in StockPricesDbContext:
public StockPricesDbContext(string connectionString)
{
_connectionString = connectionString;
}
and this would be the OnConfiguring method:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
if (_connectionString != null)
optionsBuilder.UseSqlServer(_connectionString);
}
}
Then I could use it in the methods like this:
using (var db = new StockPricesDbContext(_connectionString))
{
}
It kind of works, but seems a bit "unclean".
Has anyone else come across a good pattern for repositories built on top of Entity Framework Core?
The common mistake that causes this error is having several async queries that have not been committed.
There is no problem saving multiple records in EF though.
There are multiple approaches to avoid your issue.
Using AddRange(),UpdateRange(),DeleteRange()
AddRange() can add list of objects to your db and you don't need to call .Add() more than once. Similarly you can update or delete a list of objects
EFCore.BulkExtensions
Bulk Extensions in EFCore
Both are extending DbContext with Bulk operations and have the same syntax call:
context.BulkInsert(stockPriceList);
context.BulkUpdate(stockPriceList);
context.BulkDelete(stockPriceList);
context.BulkInsertOrUpdate(stockPriceList);
I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.
I searched a lot and still couldn't find a solid solution for this. Suppose you have methods in your application. This methods use "System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration" to access some setting in the web.config. If you try to test these methods, your tests will fail because your test project doesn't have web.config.
What is the best way to solve this problem. For projects with simple config file, I usually use a method like this as facade method.
public class Config
{
public static String getKeyValue(String keyName)
{
if (keyName == String.Empty) return String.Empty;
String result = "";
System.Configuration.Configuration rootWebConfig1 =
System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
if (rootWebConfig1.AppSettings.Settings.Count > 0)
{
System.Configuration.KeyValueConfigurationElement reportEngineKey =
rootWebConfig1.AppSettings.Settings[keyName];
if (reportEngineKey != null)
{
result = reportEngineKey.Value;
}
}
return result;
}
}
Every time I tried to set the path for OpenWebConfiguration( ), I got the error "The relative virtual path is not allowed"
To make that scenario more testable, I usually take the approach of making a "settings manager" of my own, and giving it an interface. So for example:
public interface IConfig
{
string GetSettingValue(string settingName);
}
Then I can have my "real" implementation:
public sealed class Config : IConfig
{
public string GetSettingValue(string settingName)
{
// your code from your getKeyValue() method would go here
}
}
Then my code that uses it would take in an instance of this (this is an example of the Dependency Inversion Principal):
public void DoStuff(IConfig configuration)
{
string someSetting = configuration.GetSettingValue("ThatThingINeed");
// use setting...
}
So now for my production code, I can call DoStuff and pass in an instance of Config.
When I need to test, I can use a mocking tool (Moq, JustMock, RhinoMocks, etc) to create a fake IConfig that returns a known value without hitting the actual .config file, or you can do it without a mocking framework by making your own mocks (and store them in your test project).
public class ConfigMock : IConfig
{
private Dictionary<string, string> settings;
public void SetSettingValue(string settingName, string value)
{
settings[settingName] = value;
}
public string GetSettingValue(string settingName)
{
return settings[settingName];
}
}
and
[Test]
public void SomeExampleTest()
{
var config = new ConfigMock();
config.SetSettingValue("MySetting", "SomeValue");
var underTest = new MyClass();
underTest.DoStuff(config);
}
The easiest way to do this is to use a mocking library such as moq. It takes a bit of time to figure it out, but once you do you can abstract away most of your plumbing to return the values you need for repeatable, consistent testing.
I've created my own cache manager for a web site I'm developing and I was looking to find the best way to clear the cache under certain circumstances.
I found many articles saying the proper way to clear the cache is to call HttpRuntime.Close()
However, in my unit tests setup I call the encapsulated function HttpRuntime.Close() and the cache is NOT being cleared out.
I expected it to perform something similar to
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache)
{
HttpRuntime.Cache.Remove(cacheItem.Key.ToString());
}
The foreach loop works great in my encapsulated function, but the Close() never works right.
Am I misunderstanding the purpose of HttpRuntime.Close() or is there something more sinister going on here?
Don't use Close, it does more than the docs say. And the docs also say not to use it while processing normal requests...
This is the reflected source of Close():
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
public static void Close() {
if (_theRuntime.InitiateShutdownOnce()) {
SetShutdownReason(ApplicationShutdownReason.HttpRuntimeClose, "HttpRuntime.Close is called");
if (HostingEnvironment.IsHosted) {
HostingEnvironment.InitiateShutdown();
} else {
_theRuntime.Dispose();
}
}
}
Also, you cannot iterate over a collection and remove items from it at the same time, as this renders the enumeration invalid.
So, try this instead, which doesn't change what it loops over:
List<string> toRemove = new List<string>();
foreach (DictionaryEntry cacheItem in HttpRuntime.Cache) {
toRemove.Add(cacheItem.Key.ToString());
}
foreach (string key in toRemove) {
HttpRuntime.Cache.Remove(key);
}
That being said, really, you should try to use cache dependencies to have the invalid cache entries cleared automatically for you, and then all this becomes unnecessary.
I understand the issue with enumeration but for some reason the Cache doesn't seem to have a problem removing an item while walking through the list.
If you drill down to the detail implementation, you will find the Enumerator is created by CacheSingle.CreateEnumerator, a new Hashtable instance is created for enumeration.
That's why you can do the remove in a foreach loop.
you could simply implement your own Cache class, check the below one:
public sealed class YourCache<T>
{
private Dictionary<string, T> _dictionary = new Dictionary<string, T>();
private YourCache()
{
}
public static YourCache<T> Current
{
get
{
string key = "YourCache|" + typeof(T).FullName;
YourCache<T> current = HttpContext.Current.Cache[key] as YourCache<T>;
if (current == null)
{
current = new YourCache<T>();
HttpContext.Current.Cache[key] = current;
}
return current;
}
}
public T Get(string key, T defaultValue)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException("key should not be NULL");
T value;
if (_dictionary.TryGetValue(key, out value))
return value;
return defaultValue;
}
public void Set(string key, T value)
{
if (key == null)
throw new ArgumentNullException("key");
_dictionary[key] = value;
}
public void Clear()
{
_dictionary.Clear();
}
}
you could call items from cache or even clear them using the following:
// put something in this intermediate cache
YourCache<ClassObject>.Current.Set("myKey", myObj);
// clear this cache
YourCache<ClassObject>.Current.Clear();