Is there like a rule of thumb about when to go either over the other?
Im curious because i have a dilemma here, where i have a very frequently used method, return a custom class type that is rather large. Im wondering if it wouldnt be cheaper to hold an instance of this custom class in a field, and then in the method just change and return it every time, rather than creating a whole new object as would be the case if i had a new class instance created in the method every time.
The main difference between returning a newly created instance of a class and returning a field is enormous: in the latter case, the instance is shared because all clients receive a pointer to the same instance. That means: whenever any of the clients (or the original creator of the instance) changes something in the instance, all clients will now see all those changes. In the former case, all instances are different, and changes affect only the current client.
So take a really close look at the requirements and find out which of the behaviors is required. Getting them wrong can have devastating effects.
What you seem to ask for is a so called lazy field. You declare it as System.Lazy<> like so
private System.Lazy<YourClass> mYourClassInstance;
...
mYourClassInstance = new System.Lazy<YourClass>(() => new YourClass());
and then you can get the instance via
mYourClassInstance.Value
anywhere you want but the value (instance) is only created once.
I can register a single registration item with instanceCreator context (aka Func<T>), but there doesn't seem to be the same allowance with a RegisterAll.
TL;DR - Find the accepted answer and look at update 2 (or skip down to Update 3 on this question)
This is what I want to do:
container.RegisterAll<IFileWatcher>(
new List<Func<IFileWatcher>>
{
() => new FileWatcher(
#".\Triggers\TriggerWatch\SomeTrigger.txt",
container.GetInstance<IFileSystem>()),
() => new FileWatcher(
#".\Triggers\TriggerWatch\SomeOtherTrigger.txt",
container.GetInstance<IFileSystem>())
});
I tried adding an extension based on a previous Stack Overflow answer for multiple registrations, but it seems that last one in wins:
public static class SimpleInjectorExtensions
{
public static void RegisterAll<TService>(this Container container,
IEnumerable<Func<TService>> instanceCreators)
where TService : class
{
foreach (var instanceCreator in instanceCreators)
{
container.RegisterSingle(typeof(TService),instanceCreator);
}
container.RegisterAll<TService>(typeof (TService));
}
}
I'm also curious why there is a need for RegisterAll to exist in the first place. This is the first dependency injection container out of 5 that I've used that makes the distinction. The others just allow you to register multiple types against a service and then load them all up by calling Resolve<IEnumerable<TService>> (autofac) or GetAllInstances<TService> (both SimpleInjector and Ninject).
Update
For more clarity, I'm trying to build a list of items that I can pass to a composite that handles each of the individual items. It suffers from the same problem as the above since it falls into a group of tasks that all get registered to be run based on schedules, triggers, and events (Rx). To remove the register all for a moment and rip out some of the other stuff:
container.Register<ITask>(() => new FileWatchTask(
container.GetInstance<IFileSystem>(),
container.GetInstance<IMessageSubscriptionManagerService>(),
configuration,
container.GetAllInstances<IFileWatcher>()));
You can see that I am grabbing all instances of the previously registered file watchers.
What I need to know is a simple workaround for this issue and when it will be implemented (or if not, why it won't be). I will also accept that this is not possible given the current limitations of Simple Injector's design. What I will not accept is that I need to change and adapt my architecture to meet the limitations of a tool.
Update 2
Let's talk about OCP (Open Closed Principle aka the O in SOLID) and the impression I'm getting in how SimpleInjector breaks this particular principle in some cases.
Open Closed Principle is just that, open for extension, but closed for modification. What this means is that you can alter the behavior of an entity without altering its source code.
Now let's shift to an example that is relevant here:
var tasks = container.GetAllInstances<ITask>();
foreach (var task in tasks.OrEmptyListIfNull())
{
//registers the task with the scheduler, Rx Event Messaging, or another trigger of some sort
task.Initialize();
}
Notice how clean that is. To be able to do this though, I need to be able to register all instances of an interface:
container.RegisterAll<ITask>(
new List<Func<ITask>>{
() => new FileWatchTask(container.GetInstance<IFileSystem>(),container.GetInstance<IMessageSubscriptionManagerService>(),configuration,container.GetAllInstances<IFileWatcher>()),
() => new DefaultFtpTask(container.GetInstance<IFtpClient>(),container.GetInstance<IFileSystem>()),
() => new DefaultImportFilesTask(container.GetInstance<IFileSystem>())
}
);
Right? So the lesson here is that this is good and meeting OCP. I can change the behavior of the task runner simply by adding or removing items that are registered. Open for extension, closed for modification.
Now let's focus on trying to do it the way suggested in the answer below (prior to the second update, which finally answers this question), which the author is giving the impression to be a better design.
Let's start with what the answer from the maintainer mentions is good design for registration. The viewpoint that I'm getting is that I have to make a sacrifice to my code to somehow make the ITask more flexible to work with SimpleInjector:
container.Register<ITask<SomeGeneric1>(() => new FileWatchTask(container.GetInstance<IFileSystem>(),container.GetInstance<IMessageSubscriptionManagerService>(),configuration,container.GetAllInstances<IFileWatcher>()));
container.Register<ITask<SomeGeneric2>(() => new DefaultFtpTask(container.GetInstance<IFtpClient>(),container.GetInstance<IFileSystem>()));
container.Register<ITask<SomeGeneric3>(() => new DefaultImportFilesTask(container.GetInstance<IFileSystem>()));
Now let's see how that makes our design change:
var task1 = container.GetInstances<ITask<SomeGeneric1>();
task1.Initialize();
var task2 = container.GetInstances<ITask<SomeGeneric2>();
task2.Initialize();
var task3 = container.GetInstances<ITask<SomeGeneric3>();
task3.Initialize();
Ouch. You can see how every time I add or remove an item from the container registration, I now need to also update another section of code. Two places of modification for one change, I'm breaking multiple design issues.
You might say why am I asking the container for this? Well this is in the startup area, but let's explore if I wasn't.
So I will use constructor injection to illustrate why this is bad. First let's see my example as construction injection.
public class SomeClass {
public SomeClass(IEnumerable<ITask> tasks){}
}
Nice and clean.
Now, let's switch back to my understanding of the accepted answer's view (again prior to update 2):
public class SomeClass {
public SomeClass(ITask<Generic1> task1,
ITask<Generic2> task2,
ITask<Generic3> task3
) {}
}
Ouch. Everytime I have to edit multiple areas of code, and let's not even get started at how poor this design is.
What's the lesson here? I'm not the smartest guy in the world. I maintain (or try to maintain :)) multiple frameworks and I don't try to pretend I know more than or better than others. My sense of design might be skewed or I might be limiting others in some unknown way that I have not even thought of yet. I'm sure the author means well when he gives advice on design, but in some cases it may come across annoying (and a little condescending), especially for those of us that know what we are doing.
Update 3
So the question was answered in Update 2 from the maintainer. I was trying to use RegisterAll because it hadn't occurred to me that I could just use Register<IEnumerable<T>> (and unfortunately the documentation didn't point this out). It seems totally obvious now, but when people are making the jump from other IoC frameworks, they are carrying some baggage with them and may miss this awesome simplification in design! I missed it, with 4 other DI containers under my belt. Hopefully he adds it to the documentation or calls it out a little better.
From your first example (using the List<Func<IFileWatcher>>) I understand that you want to register a collection of transient filewatchers. In other words, every time you iterate the list, a new file watcher instance should be created. This is of course very different than registering a list with two (singleton) filewatchers (the same instances that are always returned). There's however some ambiguity in your question, since in the extension method you seem to register them as singleton. For the rest of my answer, I'll assume you want transient behavior.
The common use case for which RegisterAll is created, is to register a list of implementations for a common interface. For instance an application that has multiple IEventHandler<CustomerMoved> implementations that all need to be triggered when a CustomerMoved event got raised. In that case you supply the RegisterAll method with list of System.Type instances, and the container is completely in control of wiring those implementations for you. Since the container is in control of the creation, the collection is called 'container-controlled'.
The RegisterAll however, merely forward the creation back to the container, which means that by default the list results in the creation of transient instances (since unregistered concrete types are resolved as transient). This seems awkward, but it allows you to register a list with elements of different lifestyles, since you can register each item explicitly with the lifestyle of choice. It also allows you to supply the RegisterAll with abstractions (for instance typeof(IService)) and that will work as well, since the request is forwarded back to the container.
Your use case however is different. You want to register a list of elements of the exact same type, but each with a different configuration value. And to make things more difficult, you seem to want to register them as transients instead of singletons. By not-passing the RegisterAll a list of types, but an IEnumerable<TService> the container does not create and auto-wire those types , we call this a 'container-uncontrolled' collection.
Long story short: how do we register this? There are multiple ways to do this, but I personally like this approach:
string[] triggers = new[]
{
#".\Triggers\TriggerWatch\SomeTrigger.txt",
#".\Triggers\TriggerWatch\SomeOtherTrigger.txt"
};
container.RegisterAll<IFileWatcher>(
from trigger in triggers
select new FileWatcher(trigger,
container.GetInstance<IFileSystem>())
);
Here we register a LINQ query (which is just an IEnumerable<T>) using the RegisterAll method. Every time someone resolves an IEnumerable<IFileWatcher> it returns that same query, but since the select of that query contains a new FileWatcher, on iteration new instances are always returned. This effect can be seen using the following test:
var watchers = container.GetAllInstances<IFileWatcher>();
var first1 = watchers.First();
var first2 = watchers.First();
Assert.AreNotEqual(first1, first2, "Should be different instances");
Assert.AreEqual(first1.Trigger, first2.Trigger);
As this test shows, we resolve the collection once, but every time we iterate it (.First() iterates the collection), a new instance is created, but both instances have the same #".\Triggers\TriggerWatch\SomeTrigger.txt" value.
So as you can see, there is not limitation that prevents you from doing this effectively. However, you might need to think differently.
I'm also curious why there is a need for RegisterAll to exist in the
first place.
This is a very explicit design decision. You are right that most other containers just allow you to do a bunch of registrations of the same type and when asked for a collection, all registrations are returned. Problem with this is that it is easy to accidentally register a type again and this is something I wanted to prevent.
Further more, all containers have different behavior of which registration is returned when requesting for a single instance instead of requesting the collection. Some return the first registration others return the last. I wanted to prevent this ambiguity as well.
Last but not least, please note that registering collections of items of the same type should usually be an exception. In my experience 90% of the time when developers want to register multiple types of the same abstraction, there is some ambiguity in their design. By making registering collections explicit, I hoped to let this stick out.
What I will not accept is that I need to change and adapt my
architecture to meet the limitations of some tool.
I do agree with this. Your architecture should be leading, not the tools. You should chose your tools accordingly.
But please do note that Simple Injector has many limitations and most of those limitations are chosen deliberately to stimulate users to have a clean design. For instance, every time you violate one of the SOLID principles in your code, you will have problems. You will have problems keeping your code flexible, your tests readable, and your Composition Root maintainable. This in fact holds for all DI containers, but perhaps even more for Simple Injector. This is deliberate and if the developers are not interested in applying the SOLID principles and want a DI container that just works in any given circumstance, perhaps Simple Injector is not the best tool for the job. For instance, applying Simple Injector to a legacy code base can be daunting.
I hope this gives some perspective on the design of Simple Injector.
UPDATE
If you need singletons instead, this is even simpler. You can register them as follows:
var fs = new RealFileSystem();
container.RegisterSingle<IFileSystem>(fs);
container.RegisterAll<IFileWatcher>(
new FileWatcher(#".\Triggers\TriggerWatch\SomeTrigger.txt", fs),
new FileWatcher(#".\Triggers\TriggerWatch\SomeOtherTrigger.txt", fs)
);
UPDATE 2
You explicitly asked for RegisterAll<T>(Func<T>) support to lazily create a collection. In fact there already is support for this, just by using RegisterSingle<IEnumerable<T>>(Func<IEnumerable<T>>), as you can see here:
container.RegisterSingle<IEnumerable<IFileWatcher>>(() =>
{
return
from
var list = new List<IFileWatcher>
{
new FileWatcher(#".\Triggers\TriggerWatch\SomeTrigger.txt", container.GetInstance<IFileSystem>()),
new FileWatcher(#".\Triggers\TriggerWatch\SomeOtherTrigger.txt", container.GetInstance<IFileSystem>())
};
return list.AsReadOnly();
});
The RegisterAll<T>(IEnumerable<T>) is in fact a convenient overload that eventually calls into RegisterSingle<IEnumerable<T>>(collection).
Note that I explicitly return a readonly list. This is optional, but is an extra safety mechanism that prevents the collection from being altered by any application code. When using RegisterAll<T> collections are automatically wrapped in a read-only iterator.
The only catch with using RegisterSingle<IEnumerable<T>> is that the container will not iterate the collection when you call container.Verify(). However, in your case this would not be a problem, since when an element of the collection fails to initialize the call to GetInstance<IEnumerable<IFileWatcher>> will fail as well and with that the call to Verify().
UPDATE 3
I apologize if I gave to the impression that I meant your design is wrong. I have no way of knowing this. Since you explicitly asked about why some features where missing, I tried my best to explain the rationale behind this. That doesn't mean however that I think your design is bad, since there is no way for me of knowing.
let's switch back to what that would look like with the maintainer's view of good design
I'm not sure why you think that this is my view on good design? Having a SomeClass with a constructor that need to be changed every time you add a task in the system is definitely not a good design. We can safely agree on this. That breaks OCP. I would never advice anyone to do such thing. Besides having a constructor with many arguments is a design smell at least. The next minor release of Simple Injector even adds a diagnostic warning concerning types with too many dependencies since this often is an indication of a SRP violation. But again see how Simple Injector tries to ‘help’ developers here by providing guidance.
Still however, I do promote the use of generic interfaces, and that’s a case that the Simple Injector design is optimized for especially. An ITask interface is a good example of this. In that case, the ITask<T> will often be an abstraction over some business behavior you wish to execute, and the T is a parameter object that holds all parameters of the operation to execute (you can see it as a message with a message handler). This however is only useful when a consumer needs to execute an operation with a specific set of parameters (a specific version of T), for instance it wants to execute ITask<ShipOrder>. Since you are executing a batch of all tasks without supplying parameter, a design based on ITask<T> would probably be awkward.
But let's assume for a second that it is appropriate. Let's assume this, so I can explain how Simple Injector is optimized in this case. At the end of this update, I’ll show you how Simple Injector might still be able to help in your case, so hold your breath. In your code sample, you register your generic tasks as follows:
container.Register<ITask<SomeGeneric1>(() => new FileWatchTask(container.GetInstance<IFileSystem>(),container.GetInstance<IMessageSubscriptionManagerService>(),configuration,container.GetAllInstances<IFileWatcher>()));
container.Register<ITask<SomeGeneric2>(() => new DefaultFtpTask(container.GetInstance<IFtpClient>(),container.GetInstance<IFileSystem>()));
container.Register<ITask<SomeGeneric3>(() => new DefaultImportFilesTask(container.GetInstance<IFileSystem>()));
This is a rather painful way of registering all tasks in the system, since every time you change a constructor of a task implementation, you'll have to change this code. Simple Injector allows you to auto-wire types by looking at their constructor. In other words, Simple Injector allows you to simplify this code to the following:
container.Register<ITask<SomeGeneric1>, FileWatchTask>();
container.Register<ITask<SomeGeneric2>, DefaultFtpTask>();
container.Register<ITask<SomeGeneric3>, DefaultImportFilesTask>();
This already is much more maintainable, results in better performance and allows you to do add other interesting scenarios later on such as context based injection (since Simple Injector is in control of the whole object graph). This is the advised way of registering things in Simple Injector (prevent the use of a Func if possible).
Still, when having a architecture where a task is the center element, you would probably add new task implementations quite regularly. This will result in having dozens of registration lines and having to go back to this code to add a line every time you add a task. Simple Injector however has a batch registration feature that allows you to shrink this back to one single line of code:
// using SimpleInjector.Extensions;
container.RegisterManyForOpenGeneric(typeof(ITask<>), typeof(ITask<>).Assembly);
By calling this line, the container will search for all ITask<T> implementations that are located in the interface’s assembly and it will register them for you. Since this is done at runtime using reflection, the line does not have to be altered when new tasks are added to the system.
And since you're talking about the OCP, IMO Simple Injector has great support for the OCP. At some points it even beats all other frameworks out there. When I think about OCP, I particularly think about one specific pattern: the decorator pattern. The decorator pattern is a very important pattern to use when applying the OCP. Cross-cutting concerns for instance should not be added by changing some piece of business logic itself, but can best be added by wrapping classes with decorators. With Simple Injector, a decorator can be added with just a single line of code:
// using SimpleInjector.Extensions;
container.RegisterDecorator(typeof(ITask<>), typeof(TransactionTaskDecorator<>));
This ensures that a (transient) TransactionTaskDecorator<T> is wrapped around all ITask<T> implementations when they got resolved. Those decorators are integrated in the container’s pipeline, which means that they can have dependencies of their own, can have initializers, and can have a specific lifestyle. And decorators can be stacked easily:
container.RegisterDecorator(typeof(ITask<>), typeof(TransactionTaskDecorator<>));
container.RegisterDecorator(typeof(ITask<>), typeof(DeadlockRetryTaskDecorator<>));
This wraps all tasks in a transaction decorator and wraps that transaction decorator again in a deadlock retry decorator. And you can even apply decorators conditionally:
container.RegisterDecorator(typeof(ITask<>), typeof(ValidationTaskDecorator<>),
context => ShouldApplyValidator(context.ServiceType));
And if your decorator has a generic type constraint, Simple Injector would automatically apply the decorator when the generic type constraints match, nothing you have to do about this. And since Simple Injector generates expression trees and compiles them down to delegates, this is all a one-time cost. That doesn’t mean it’s for free, but you’ll pay only once and not per resolve.
There's no other DI library that makes adding decorators as easy and flexible as Simple Injector does.
So this is where Simple Injector really shines, but that doesn't help you much :-). Generic interfaces don't help you in this case, but still, even in your case, you might be able make your registration more maintainable. If you have many task implementations in the system (that is, much more than three), you might be able to automate things like this:
var taskTypes = (
from type in typeof(ITask).Assemby.GetTypes()
where typeof(ITask).IsAssignableFrom(type)
where !type.IsAbstract && !type.IsGenericTypeDefinition
select type)
.ToList();
// Register all as task types singleton
taskTypes.ForEach(type => container.Register(type, type, Lifestyle.Singleton));
// registers a list of all those (singleton) tasks.
container.RegisterAll<ITask>(taskTypes);
Alternatively, with Simple Injector 2.3 and up, you can pass in Registration instances directly into the RegisterAll method:
var taskTypes =
from type in typeof(ITask).Assemby.GetTypes()
where typeof(ITask).IsAssignableFrom(type)
where !type.IsAbstract && !type.IsGenericTypeDefinition
select type;
// registers a list of all those (singleton) tasks.
container.RegisterAll(typeof(ITask),
from type in taskTypes
select Lifestyle.Singleton.CreateRegistration(type, type, container));
This does assume however that all those task implementations have a single public constructor and all constructor arguments are resolvable (no configuration values such as int and string). If this is not the case, there are ways to change the default behavior of the framework, but if you want to know anything about this, it would be better to move that discussion to a new SO question.
Again, I’m sorry if I have annoyed you, but I rather annoy some developers than missing the opportunity in helping a lot others :-)
When it comes to designing classes and "communication" between them, I always try to design them in such way that all object construction and composing take place in object constructor. I don't like the idea of object construction and composition taking place from outside, like other objects setting properties and calling methods on my object to initialize it. This especially gets ugly when multiple object try to do thisto your object and you never know in what order your props\methods will be executed.
Unforunatly I stumbl on such situations quite often, especially now with the growing popularity of dependecy injection frameworks, lots of libraries and frameworks rely on some kind of external object initialization, and quite often require not only constructor injection on our object but property injection too.
My question are:
Is it ok to have objects that relly on some method, or property to be called on them after which they can consider them initialzied?
Is ther some kind of pattern for situations when your object acting is receiver, and must support multiple interfaces that call it, and the order of these calls does matter? (something better than setting flags, like ThisWasDone, ThatWasCalled)
Is it ok to have objects that relly on some method, or property to be called on them after which they can consider them initialzied?
No. Init methods are a pain since there is no guarantee that they will get called. A simple solution is to switch to interfaces and use factory or builder pattern to compose the implementation.
#Mark Seemann has written a article about it: http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling.aspx
Is there some kind of pattern for situations when your object acting is receiver, and must support multiple interfaces that call it, and the order of these calls does matter? (something better than setting flags, like ThisWasDone, ThatWasCalled)
Builder pattern.
I think it is OK, but there are implications. If this is an object to be used by others, you need to ensure that an exception is thrown any time a method or property is set or accessed and the initialization should have been called but isn't.
Obviously it is much more convenient and intuitive if you can take care of this in the constructor, then you don't have to implement these checks.
I don't see anything wrong in this. It may be not so convinient, but you can not ALWAYS use initialization in ctor, like you can not alwats drive under green light. These are dicisions that you made based on your app requirements.
It's ok. Immagine if your object, for example, need to read data from TCP stream or a file that ciuld be not present or corrupted. Raise an exception from ctor is baaad.
It's ok. If you think, for example, about some your DSL language compiler, it can looks like:
A) find all global variables and check if there mem allocation sum sutisfies your device requierements
B) parse for errors
C) check for self cycling
And so on...
Hoe this helps.
Answering (1)
Why not? An engine needs the driver because this must enter the key for the car, and later power-on. Will a car do things like detecting current speed if engine is stopeed? Or Will the car show remaining oil without powering-on it?
Some programming goals won't be able to have their actors initialized during its object construction, and this isn't because it's a non-proper way of doing things but because it's the natural, regular and/or semantically-wise way of representing its whole behavior.
Answering (2)
A decent class usage documentation will be your best friend. Like answer to (1), there're some things in this world that should be done in order to get them done rightly, and it's not a problem but a requirement.
Checking objects' state using flags isn't a problem too, it's a good way of adding reliability to your object models, because its own behaviors and consumers of them will be aware about if things got done as expected or not.
First of all, Factory Method.
public class MyClass
{
private MyClass()
{
}
public Create()
{
return new MyClass();
}
}
Second of all, why do you not want another class creating an object for you? (Factory)
public class MyThingFactory
{
IThing CreateThing(Speed speed)
{
if(speed == Speed.Fast)
{
return new FastThing();
}
return new SlowThing();
}
}
Third, why do multiple classes have side effects on new instances of your class? Don't you have declarative control over what other classes have access to your object?
If you take a look at Tom Schultz's Blog, you'll see that he says that if you instance your own Context objects (such as the CommerceContext object), an instance of the SiteConfigReadOnlyFreeThreaded class created in memory as well, and you can't do anything to destroy it. If you do this enough times, you'll eventually get warnings in your Application Log. Here's what the warning looks like:
The Commerce Server runtime has detected that more than # instances of the SiteConfigReadOnlyFreeThreaded object have been created. Creating many SiteConfigReadOnlyFreeThreaded instances will negatively affect the performance of the site. Please refer to the Commerce Server documentation for the recommended use of the SiteConfigReadOnlyFreeThreaded object.
You'll also see that Tom says to use the the Current property of the Context objects to avoid this error, much like this:
ContentSelector cso = CommerceContext.Current.TargetingSystem.SelectionContexts["advertising"].GetSelector();
Doing so re-uses the same singleton instance to avoid re-creating the SiteConfigReadOnlyFreeThreaded object every time you instance a new CommerceContext class.
With me so far? Good :)
Here's what I'm really trying to do: Get a list of all of the Page Groups set up in the Marketing section of Commerce Server. As far as my knowledge goes, here's the only way to do it:
using (MarketingContext ctx = MarketingContext.Create("MyCommerceSite", "MyMarketingAuthorizationStore", AuthorizationMode.NoAuthorization))
{
PageGroup[] pageGroups = ctx.PageGroups.GetAllPageGroups();
}
As you can see, I'm creating a MarketingContext class, which also creates a SiteConfigReadOnlyFreeThreaded in memory as well, each time it's called (which happens to be frequently).
Is there a way to get the list of all page groups configured without instancing an entirely new MarketingContext object each time I want to do it?
I did some digging, and found the following:
By default, Microsoft sets the threshold for these warnings even cropping up in the error log to 100. It turns out that these warnings are absolutely benign if they are staying under 100 in count consistently.
In my case, I had set the threshold for showing errors to 2, just to show every instance that cropped up, regardless of whether it was a valid concern or not. I've sense upped the limit back to 100, and I haven't seen any adverse affects thus far.
I am writing a Console Application in C# in which I want to cache certain items for a predefined time (let's say 1 hour). I want items that have been added into this cache to be automatically removed after they expire. Is there a built-in data structure that I can use? Remember this is a Console App not a web app.
Do you actually need them removed from the cache at that time? Or just that future requests to the cache for that item should return null after a given time?
To do the former, you would need some sort of background thread that was periodically purging the cache. This would only be needed if you were worried about memory consumption or something. If you just want the data to expire, that would be easy to do.
It is trivial to create such a class.
class CachedObject<TValue>
{
DateTime Date{get;set;}
TimeSpan Duration{get;set;}
TValue Cached{get;set;}
}
class Cache : Dictionary<TKey,TValue>
{
public new TValue this(TKey key)
{
get{
if (ContainsKey(key))
{
var val = base.this[key];
//compare dates
//if expired, remove from cache, return null
//else return the cached item.
}
}
set{//create new CachedObject, set date and timespan, set value, add to dictionary}
}
Its already in the BCL. Its just not where you expect to find it: You can use System.Web.Caching from other kinds of applications too, not only in ASP.NET.
This search on google links to several resources about this.
I don't know of any objects in the BCL which do this, but I have written similar things before.
You can do this fairly easily by just including a System.Threading.Timer inside of your caching class (no web/winforms dependencies), and storing an expiration (or last used) time on your objects. Just have the timer check every few minutes, and remove the objects you want to expire.
However, be watchful of events on your objects. I had a system like this, and was not being very careful to unsubscribe from events on my objects in the cache, which was preventing a subtle, but nasty memeory leak over time. This can be very tricky to debug.
Include an ExpirationDate property in the object that you will be caching (probably a wrapper around your real object) and set it to expire in an hour in its constructor. Instead of removing items from the collection, access the collection through a method that filters out the expired items. Or create a custom collection that does this automatically. If you need to actually remove items from the cache, your custom collection could instead purge expired items on every call to one of its members.