I have a number of filter classes that implement an IFilter interface (defining the filter logic). Using constructor injection, for each filter implementation I want to pass an interface defining filter settings. Consider the following example:

My concrete implementation of ITimeSpanFilterSettings however requires a settingsKey to retreive the settings from the database. However I cannot register the my implementation of
ITimeSpanFilterSettings with a static settingsKey.

Is it possible to resolve all IFilter implementations and specifying a settingsKey which should be used to instantiate the ITimeSpanFilterSettings implementations?

1 Answer
1

It appears to me that there are a couple of factors at play. It may be my misunderstanding of the question, or it may be some question "abbreviation," so please bear with me.

First let's talk about the resolution of the TimeSpanFilter getting an ITimeSpanFilterSettings object. We'll get to the parameterization of the settings object later, for now let's just talk about getting the settings to the filter.

If you have the setup as described, I'm inferring that you have an ISomethingFilterSettings interface corresponding to every IFilter implementation. You have TimeSpanFilter and ITimeSpanFilterSettings; if you had a DateTimeFilter you would have an IDateTimeFilterSettings.

Given that, there isn't anything special you need to do. Register your various types with the ContainerBuilder and magic happens.

That kind of thing is pretty handy if you just have the one incoming parameter for your settings object. If there are multiple parameters, you can use that incoming context parameter in the lambda to do some resolution on the fly, too:

However, if you have too many parameters that could get a little messy, so you can also register a dependency using a parameter lambda such that only the one parameter you specify will be injected manually and the rest will be done automatically:

Additional complexity: You mentioned in the comments on this answer that you're getting your settings information based on the view the user has selected. You will need to update your system to put that settings key somewhere that Autofac can get to it.

Given that you mentioned a "view," I assume you mean ASP.NET MVC or something similar. One place to put request-level values like that is in HttpContext.Items. This may require some re-design in your system.

For example, if the dependency has to come in as a constructor/property on your controller, then you probably need to have some mechanism to populate the value in HttpContext before the controller gets instantiated. Maybe there's an attribute on your controller, maybe there's an IHttpModule that sits in the pipeline and has the map of URL-to-settings, maybe it's something else. That's not really something we can handle in this question (otherwise we'd just be writing the whole product here, right? and I'm not really up for that...).

Once it's in that central location like HttpContext.Items you could put that into the lambda registration:

An alternative to making an attribute or IHttpModule would be to call DependencyResolver.Current.GetService<IFilter>() from inside your controller after you set the HttpContext.Items value. I don't like service location, though, and many consider it to be an "anti-pattern," so avoid it if you can.

A note about caching: It sounds like your configuration value is actually coming from a database somewhere - a more expensive call compared to reading AppSettings. You can put the database call right in the registration, but you may have some interesting performance challenges if you resolve a bunch of these. The lambdas in both cases execute every time resolution happens - the parameter values aren't cached for you, and unless you're registering your object with something other than an InstancePerDependency lifetime (the default), then Autofac isn't caching the created objects. That could mean a lot of unintended database calls. Figuring out the caching on that (as needed) is an exercise left to the reader.

(Note in that last example I used InstancePerHttpRequest as the scope - that means you'll get caching for one web request.)

And one thing about design: It's an opinion, but generally I try to avoid "parameterized resolution," as it were. That is, "I want a general IFilter, but it needs to be totally tailored to this specific situation." It sounds like that's what you have here. In those situations, I find that while I may need to use a common base-level interface like IFilter, I will also try to have interfaces that are specific to my needs.

public interface ICustomSituationFilter : IFilter

I'll then use those custom interfaces as my dependencies rather than trying to push everything down to be generic. It allows me to more easily separate that "configuration" notion out of the controller (which shouldn't be configuring the incoming dependencies necessarily) and push that into my registrations - I don't need to pop things into HttpContext.Items or any sort of shared location because the only place that knows about the settings is the actual dependency registration. You may want to consider changing your design to break that tie between "choosing the view" and "which configuration settings are used" if you can. It'll make your life easier.

Thank you for your in-depth explanation of registering lambdas as dependencies for RegisterType<>(). The problem however is that I do not store the settingskey in AppSettings or something else that can be resolved inside this lambda expression. The problem is that this settingKey is resolved at runtime, depending on which view is being accessed by the user. For now I ended creating a new Lifetimescope in which I add new registrations for my settings (e.g. I resolve ITimeSpanSettings using a concrete settings key. Inside the BeginLifeTimeScope I added the following:
–
ToniNov 6 '12 at 12:09

builder.Register(context => new TimeSpanSettings("NormallyMySettingsKeyIsAVariable").As(ITimeSpanSettings); Maybe there is a more elegant solution which does not require a dependency to ILifetimeScope.
–
ToniNov 6 '12 at 12:10

This is the sort of thing you should probably put in your question. It's hard to guess at what the complexities of your system are if you don't share them.
–
Travis IlligNov 6 '12 at 16:16

I added some notes that I hope will help address the items you mentioned. I'm not sure I can provide much more than that without actually just writing the system. If it still doesn't make sense, you may want to consider decomposing the problem into smaller questions - you know now how to register some dynamically resolving things with Autofac, but if you don't know how to, say, work with HttpContext.Items, that might be another separate question rather than continuing this one.
–
Travis IlligNov 6 '12 at 16:33