I am little bit confused in understanding of the HttpCache and Singleton approaches.
My application uses Asp.net MVC and the scenario is that, I have some List of Data that will never change and some data that might rarely change.
I have developed and deployed application using a Singleton repository for this type of data.
It performs great. The only issue is that when a rare case occur, i have to restart IIS to take effect.
What is the best solution.?
Singleton implementation
public class SingletonRepository : ISingletonRepository
{
private static SingletonRepository singleInstance;
private readonly IStateRepository stateRepo;
private readonly ICountryRepository countryRepo;
private readonly ITDPaymentModeRepository paymentModeRepo;
private readonly ITDPlanRepository planRepo;
private readonly ITDOrderTypeRepository orderTypeRepo;
private readonly IKeywordRepository keywordRepo;
private readonly IAgencyRepository agencyRepo;
private readonly IList<AT_STATE> lstState;
private readonly IList<AT_COUNTRY> lstCountry;
private readonly IList<TDPaymentMode> lstPaymentMode;
private readonly IList<TDPlan> lstPlan;
private readonly IList<TDOrderType> lstOrderType;
private readonly IList<Keyword> lstKeyword;
private readonly IList<Agency_MST> lstAgency;
private SingletonRepository()
{
stateRepo = new StateRepository();
countryRepo = new CountryRepository();
paymentModeRepo = new TDPaymentModeRepository();
planRepo = new TDPlanRepository();
orderTypeRepo = new TDOrderTypeRepository();
keywordRepo = new KeywordRepository();
agencyRepo = new AgencyRepository();
lstState = stateRepo.GetAll().Where(x => x.CountryId == 101).ToList();
lstCountry = countryRepo.GetAll().ToList();
lstPaymentMode = paymentModeRepo.GetAll().ToList();
lstPlan = planRepo.GetAll().ToList();
lstOrderType = orderTypeRepo.GetAll().ToList();
lstKeyword = keywordRepo.GetAll().ToList();
lstAgency = agencyRepo.GetAll().ToList();
//lstState = stateRepo.GetAll().Take(20).ToList();
//lstCountry = countryRepo.GetAll().Take(20).ToList();
//lstPaymentMode = paymentModeRepo.GetAll().Take(20).ToList();
//lstPlan = planRepo.GetAll().Take(20).ToList();
//lstOrderType = orderTypeRepo.GetAll().Take(20).ToList();
//lstKeyword = keywordRepo.GetAll().Take(20).ToList();
//lstAgency = agencyRepo.GetAll().Take(20).ToList();
}
public static SingletonRepository Instance()
{
return singleInstance ?? (singleInstance = new SingletonRepository());
}
public IList<AT_STATE> GetState()
{
return this.lstState;
}
public IList<AT_COUNTRY> GetCountry()
{
return this.lstCountry;
}
public IList<TDPaymentMode> GetPaymentMode()
{
return this.lstPaymentMode;
}
public IList<TDPlan> GetPlan()
{
return this.lstPlan;
}
public IList<TDOrderType> GetOrderType()
{
return this.lstOrderType;
}
public IList<Keyword> GetKeyword()
{
return this.lstKeyword;
}
public IList<Agency_MST> GetAgency()
{
return this.lstAgency;
}
}
}
The purpose of using the singleton pattern generally is not for static data storage. You should use a singleton when you only want one object instance to be able to perform certain actions. It may be fast, but as you can see, when the data changes, you need to reset the heap to get the new data (as you say, by restarting IIS).
HttpCache (more specifically, the ObjectCache which Http caching uses by default), stores the data in the same place as the heap: in Random Access Memory. So, it is just as fast as static data stored in a class or instance on the heap. The difference is that you can set up the cache to periodically go stale, so that it will get new data when the data changes. You can even set up SqlCacheDependencies so that the cache is made stale whenever your database's state changes.
Another advantage of cache is that it more efficiently utilizes your server's RAM resources. With your singleton, no matter what, this data will always be occupying memory, even when the data is not being used. With cache, the server only stores the data in memory when it is being used. The disadvantages with cache are that occasionally, a user here and there will get a slower response when they request data after the cache has expired. However, after they do, other users will benefit from the data being cached for a time.
You can actually reload the singleton by creating a reload method such as this:
public static void ReloadSingletonRepositoryInstance()
{
singleInstance = null;
SingletonRepository sr = SingletonRepository.Instance;
}
Hope that helps.
Related
When setting up a MartenDB datastore in ASP.Net Core, you normally put code like this in your Startup.cs:
services.AddMarten(o =>
{
o.Connection(configuration.GetConnectionString("MyDatabase"));
o.AutoCreateSchemaObjects = AutoCreate.All;
o.Serializer(new JsonNetSerializer { EnumStorage = EnumStorage.AsString });
});
This allows you to then inject IDocumentSession and IDocumentStore into your various classes for working with that database.
Now what do you do if you have to connect to a second database? I looked at the ISessionFactory but it is not apparent that you can change the connection string from here. Do you need to manually create and register a new DocumentStore?
To answer my own question, I wound up creating a custom DocumentStore and ISessionFactory for each database I wanted to connect to, and then injecting the custom SessionFactory.
Here's the code (only showing one instance of each class for the sake of brevity. Just replace Db1 with Db2 for the second version of each class):
The custom DocumentStore:
public class Db1Store : DocumentStore
{
public Db1Store(StoreOptions options) : base(options)
{
}
}
The custom SessionFactory:
public class Db1SessionFactory : ISessionFactory
{
private readonly Db1Store store;
public Db1SessionFactory(Db1Store store)
{
this.store = store;
}
public IQuerySession QuerySession()
{
return store.QuerySession();
}
public IDocumentSession OpenSession()
{
return store.OpenSession();
}
}
The service registration (this replaces the services.AddMarten call):
services.AddSingleton(p =>
{
var options = new StoreOptions();
options.Connection(configuration.GetConnectionString("DB1"));
options.AutoCreateSchemaObjects = AutoCreate.All;
options.Serializer(new JsonNetSerializer { EnumStorage = EnumStorage.AsString });
return new Db1Store(options);
});
services.AddSingleton<Db1SessionFactory>();
Then you inject the Db1SessionFactory instance into your class, and run a query like this:
var result = await db1SessionFactory.QuerySession().Query<MyAwesomeTable>().ToListAsync();
Downsides:
I would prefer to inject the QuerySession or DocumentSession, but I can't see a way to do that without moving to Autofac or a similar DI Container that supports named instances.
I am not sure what downsides there will be creating these QuerySession/DocumentSessions in this manner. It may be a bad tradeoff.
i have serialization problem with session(as i described here) so i used static Dictionary instead of session asp.net mvc
public static Dictionary<string, object> FlightDict;
FlightDict.Add("I_ShoppingClient", client);
in this case user will override their values?are there any problem with that
because they says with static variable users data can be overrided
Yes, you can change the static variables in the site, But You need to use this to change the data but that is not enough you need to lock this data until you have done.
public static Dictionary<string, object> CacheItems
{
get{ return cacheItems; }
set{ cacheItems= value; }
}
How to Lock?
The approach you need to use to lock all actions of add or remove until you done is:
private static Dictionary<string, object> cacheItems = new Dictionary<string, object>();
private static object locker = new object();
public Dictionary<string, object> CacheItems
{
get{ return cacheItems; }
set{ cacheItems = value;}
}
YourFunction()
{
lock(locker)
{
CacheItems["VariableName"] = SomeObject;
}
}
for manipulating the data on application state you need to use the global lock of it Application.Lock(); and Application.UnLock();. i.e
Application.Lock();
Application["PageRequestCount"] = ((int)Application["PageRequestCount"])+1;
Application.UnLock();
Last: Avoid Application State and use the Static Variable to Manage the Data across the Application for Faster Performance
Note: you can add one lock at the time only so remove it before you are trying to change it
Keep in Mind : The static variables will be shared between requests. Moreover they will be initialized when application starts, so if the AppDomain, thus application gets restarted, their values will be reinitialized.
I twist myself around a workable solution to use several databases in RavenDB for an ASP.Net MVC app using Castle Windsor for the wiring.
This is the current installer
public class RavenInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDocumentStore>().Instance(CreateDocumentStore()).LifeStyle.Singleton,
Component.For<IDocumentSession>().UsingFactoryMethod(GetDocumentSesssion).LifeStyle.PerWebRequest
);
}
static IDocumentStore CreateDocumentStore()
{
var store = new DocumentStore { ConnectionStringName = "RavenDb_CS9" };
store.Initialize();
IndexCreation.CreateIndexes(typeof(Users).Assembly, store);
return store;
}
static IDocumentSession GetDocumentSesssion(IKernel kernel)
{
var store = kernel.Resolve<IDocumentStore>();
return store.OpenSession();
}
}
The above works perfect but only for one Database.
I can't find the proper thinking how to handle another database. The whole chain starts with a domain service asking for an IDocumentSession. Then the flow is as specified in the above installer. But where/how do I ask for a "SessionToDb1" or a "SessionToDb2"?
The important is of course what connection string to use (where the DB property is specified) but also what indexes to create in respective DB / DocumentStore.
Did anyone accomplish this using Windsor? Am I thinking/attacking it wrong here?
Thanks!
Because you have:
Component.For<IDocumentSession>()
.UsingFactoryMethod(GetDocumentSesssion)
.LifeStyle.PerWebRequest
Your GetDocumentSession method is going to be called any time you inject an IDocumentSession. This is good.
When working with multiple databases, you need to pass the database name as a parameter to OpenSession. So, you need some way to resolve which database you would like to connect to based on the current web request.
You need to modify the GetDocumentSession method to implement whatever custom logic you are going to use. For example, you may want to look at a cookie, asp.net session item, current thread principal, or some other criteria. The decision is custom to your application, all that matters is somehow you open the session with the correct database name.
I've run into this problem before with nhibernate.
I found the best solution is to create a SessionManager class which wraps the Creation of the document store and the Session..
So I.E.
public interface ISessionManager
{
void BuildDocumentStore();
IDocumentSession OpenSession();
}
public interface ISiteSessionManager : ISessionManager
{
}
public class SiteSessionManager : ISiteSessionManager
{
IDocumentStore _documentStore;
public SiteSessionManager()
{
BuildDocumentStore();
}
public void BuildDocumentStore()
{
_documentStore = new DocumentStore
{
Url = "http://localhost:88",
DefaultDatabase = "test"
};
_documentStore.Initialize();
IndexCreation.CreateIndexes(typeof(SiteSessionManager).Assembly, _documentStore);
}
public IDocumentSession OpenSession()
{
return _documentStore.OpenSession();
}
}
// And then!.
Container.Register(Component.For<ISiteSessionManager>().Instance(new SiteSessionManager()).LifestyleSingleton());
// And then!.
public class FindUsers
{
readonly ISiteSessionManager _siteSessionManager;
public FindUsers(ISiteSessionManager siteSessionManager)
{
_siteSessionManager = siteSessionManager;
}
public IList<User> GetUsers()
{
using (var session = _siteSessionManager.OpenSession())
{
// do your query
return null;
}
}
}
Rinse and repeat for multiple databases.!
I have a specification that validates codes. It looks like the following:
public ClassificationSpecification : ISpecification<Classification> {
HashSet<string> codes;
// constructor elided
public IsSatisfiedBy(Classification classification) {
return codes.Contains(classification.Code);
}
}
The valid codes come out of a Classification table in a database. My question is, which is the better constructor for dependency injection?
public CodeSpecification(IEnumerable<string> codes) {
this.codes = new HashSet<string>(codes);
}
or
public CodeSpecification(IRepository<Classification> repository) {
this.codes = new HashSet<string>(repository.Select(x => x.Code));
}
And the all important question: why?
Your second constructor does real work (via the repository) and this is a bad idea. See http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
You need to decide if it's valid to load all the values (pass in the values you need to the constructor), or you want to look them up on each call (pass in the repository in the constructor and store it).
I would use this constructor:
private readonly IRepository<Classification> _repository;
public CodeSpecification(IRepository<Classification> repository)
{
_repository = repository;
}
Then, find the valid codes when your class is actually called upon:
public bool IsSatisfiedBy(Classification classification)
{
var _repository.Any(x => x.Code == classification.Code);
}
This ensures that no real work is done until it is needed, separating your application's initialization from its runtime. It also ensures that you are always working with fresh data in IsSatisfiedBy; caching the values in the constructor introduces a period of time where the codes in the repository might change.
If the number of codes is large, and the repository won't execute the Any efficiently, you may still want to implement caching. It is a good idea to follow the same advice and not cache until the first call to IsSatisfiedBy:
private readonly HashSet<string> _codes;
private readonly object _codesSync = new object();
public bool IsSatisfiedBy(Classification classification)
{
if(_codes == null)
{
lock(_codesSync)
{
if(_codes == null)
{
_codes = new HashSet<string>(_repository.Select(x => x.Code));
}
}
}
return _codes.Contains(classification.Code);
}
Constructors in dependency-injected objects are infrastructure elements; domain logic of any kind should generally be deferred until the object is invoked by another.
I need to create an application,that should contain two storage,one is persistent storage and another one is cache storage.After loading, the application should check the username and password with the cache storage data if it is empty then it should check with the persistent storage.How to accomplish this task?Is there any separate concept of cache or we have create the persistent as cache.please help me.
You can use RecordStore which is also persistent, or RuntimeStore which is shared between all apps but is non persistent.
Alternatively you can use some custom storage class to implement cache functionality,
storing, updating values in that class, sharing it as a field of Application class:
class Cache {
String mName = null;
String mPhone = null;
}
public class CacheApp extends UiApplication {
Cache mCache = null;
public static void main(String[] args) {
CacheApp app = new CacheApp();
app.enterEventDispatcher();
}
public CacheApp() {
initCache();
CacheScr scr = new CacheScr();
pushScreen(scr);
}
private void initCache() {
mCache = new Cache();
mCache.mName = "Name";
mCache.mPhone = "Phone";
}
}
class CacheScr extends MainScreen {
public CacheScr() {
CacheApp app = (CacheApp) UiApplication.getUiApplication();
String name = app.mCache.mName;
String phone = app.mCache.mPhone;
}
}
Coldice is correct, however I fail to see why one would use a store separate from PersistentStore (or RecordStore) for data that must endure and may be shared, and RuntimeStore for data which is shared but not durable. This just seems to be adding complexity to normal application transient storage.