Autofac properties on request - c#

I have a service that depends on other services for example
OrderProvider(IOrderService service) {
}
That is a direct dependant so having it in the constructor is fine.
There are some other methods that require other services, so I have been handling these with properties, for example, I may need to get the Stock for an Order:
private IStockService _stockService;
public IStockService StockService { get { return _stockService ?? (_stockService = new StockService()); } }
Stock GetStock(string orderNumber) {
return StockService.Get(orderNumber);
}
As you can see, in my old way of doing things the property was only instantiated when requested.
Now I have moved to autofac I would like to set up a similar method, i.e. If a request is only for an Order then only the OrderProvider and the OrderService will be instantiated, but if they request the Stock then all 3 will be instantiated.
I really hope that makes sense.

Autofac allows you to request a Lazy<T> closed over the desired type for scenarios like yours that require delayed instantiation. The first time the Lazy<T>'s value is accessed is when the actual instance will be created.
Example Code
private Lazy<IStockService> _lazyStockService;
public IStockService StockService
{
get { return _lazyStockService.Value; }
}
public OrderProvider( IOrderService service, Lazy<IStockService> lazyStockService )
{
_service = service;
_lazyStockService = lazyStockService;
}
Here's a link to Autofac's docs on this topic
Here's a link to the docs for Lazy<T>

Related

Dependency Injection in Model classes (entities)

I am building an ASP.NET Core MVC application with Entity Framework Code-First.
I implemented a simple repository pattern, providing basic CRUD operations for all the model classes I have created.
I chose to follow all the recommendations provided in docs and DI is one of these.
In ~~.NET 5~~ (6 years later update: .net 5 was the alpha name of .net core 1.0) dependency injection works very well for any class that we do not directly instantiate (e.g.: controllers, data repositories, ...).
We simply inject them via the constructor, and register the mappings in the Startup class of the application :
// Some repository class
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
}
// In startup.cs :
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyRepository, MyRepository>();
The problem is that in some of my model classes, I would like to inject some of the dependencies I have declared.
But I think that I cannot use the constructor injection pattern because model classes are often explicitly instantiated. Therefore, I would need to provide myself with the dependencies, which I can't.
So my question is: is there another way than constructor injection to inject dependencies, and how? I was for example thinking of an attribute pattern or something like that.
As I already explained in a comment, when creating an object using new, there is nothing from the dependency injection framework that is involved in the process. As such, it’s impossible for the DI framework to magically inject things into that object, it simply doesn’t know about it.
Since it does not make any sense to let the DI framework create your model instances (models are not a dependency), you will have to pass in your dependencies explicitly if you want the model to have them. How you do that depends a bit on what your models are used for, and what those dependencies are.
The simple and clear case would be to just have your model expect the dependencies on the constructor. That way, it is a compile time error if you do not provide them, and the model has access to them right away. As such, whatever is above, creating the models, is required to have the dependencies the model type needs. But at that level, it’s likely that this is a service or a controller which has access to DI and can request the dependency itself.
Of course, depending on the number of dependencies, this might become a bit complicated as you need to pass them all to the constructor. So one alternative would be to have some “model factory” that takes care of creating the model object. Another alternative would also be to use the service locator pattern, passing the IServiceCollection to the model which can then request whatever dependencies it needs. Note that is generally a bad practice and not really inversion of control anymore.
Both these ideas have the issue that they modify the way the object is created. And some models, especially those handled by Entity Framework, need an empty constructor in order for EF to be able to create the object. So at that point you will probably end up with some cases where the dependencies of your model are not resolved (and you have no easy way of telling).
A generally better way, which is also a lot more explicit, would be to pass in the dependency where you need it, e.g. if you have some method on the model that calculates some stuff but requires some configuration, let the method require that configuration. This also makes the methods easier to test.
Another solution would be to move the logic out of the model. For example the ASP.NET Identity models are really dumb. They don’t do anything. All the logic is done in the UserStore which is a service and as such can have service dependencies.
The pattern often used in domain driven design (rich domain model to be specific) is to pass the required services into the method you are calling.
For example if you want to calculate the vat, you'd pass the vat service into the CalculateVat method.
In your model
public void CalculateVat(IVatCalculator vatCalc)
{
if(vatCalc == null)
throw new ArgumentNullException(nameof(vatCalc));
decimal vatAmount = vatcalc.Calculate(this.TotalNetPrice, this.Country);
this.VatAmount = new Currency(vatAmount, this.CurrencySymbol);
}
Your service class
// where vatCalculator is an implementation IVatCalculator
order.CalculateVat(vatCalculator);
Finally your service can inject another services, like a repository which will fetch the tax rate for a certain country
public class VatCalculator : IVatCalculator
{
private readonly IVatRepository vatRepository;
public VatCalculator(IVatRepository vatRepository)
{
if(vatRepository == null)
throw new ArgumentNullException(nameof(vatRepository));
this.vatRepository = vatRepository;
}
public decimal Calculate(decimal value, Country country)
{
decimal vatRate = vatRepository.GetVatRateForCountry(country);
return vatAmount = value * vatRate;
}
}
I know my answer is late and may not exactly what you're asking for, but I wanted to share how I do it.
First of all: If you want to have a static class that resolves your dependencies this is a ServiceLocator and it's Antipattern so try not to use it as you can.
In my case I needed it to call MediatR inside of my DomainModel to implement the DomainEvents logic.
Anyway, I had to find a way to call a static class in my DomainModel to get an instance of some registered service from DI.
So I've decided to use the HttpContext to access the IServiceProvider but I needed to access it from a static method without mention it in my domain model.
Let's do it:
1- I've created an interface to wrap the IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- Then I've created a static class to be my ServiceLocator access point
public static class ServiceLocator
{
private static IServiceProviderProxy diProxy;
public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");
public static void Initialize(IServiceProviderProxy proxy)
{
diProxy = proxy;
}
}
3- I've created an implementation for the IServiceProviderProxy which use internally the IHttpContextAccessor
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
private readonly IHttpContextAccessor contextAccessor;
public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public T GetService<T>()
{
return contextAccessor.HttpContext.RequestServices.GetService<T>();
}
public IEnumerable<T> GetServices<T>()
{
return contextAccessor.HttpContext.RequestServices.GetServices<T>();
}
public object GetService(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetService(type);
}
public IEnumerable<object> GetServices(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetServices(type);
}
}
4- I should register the IServiceProviderProxy in the DI like this
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- Final step is to initialize the ServiceLocator with an instance of IServiceProviderProxy at the Application startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
As a result now you can call the ServiceLocator in your DomainModel classes "Or and needed place" and resolve the dependencies that you need.
public class FakeModel
{
public FakeModel(Guid id, string value)
{
Id = id;
Value = value;
}
public Guid Id { get; }
public string Value { get; private set; }
public async Task UpdateAsync(string value)
{
Value = value;
var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
await mediator.Send(new FakeModelUpdated(this));
}
}
The built-in model binders complain that they cannot find a default ctor. Therefore you need a custom one.
You may find a solution to a similar problem here, which inspects the registered services in order to create the model.
It is important to note that the snippets below provide slightly different functionality which, hopefully, satisfies your particular needs. The code below expects models with ctor injections. Of course, these models have the usual properties you might have defined. These properties are filled in exactly as expected, so the bonus is the correct behavior when binding models with ctor injections.
public class DiModelBinder : ComplexTypeModelBinder
{
public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders)
{
}
/// <summary>
/// Creates the model with one (or more) injected service(s).
/// </summary>
/// <param name="bindingContext"></param>
/// <returns></returns>
protected override object CreateModel(ModelBindingContext bindingContext)
{
var services = bindingContext.HttpContext.RequestServices;
var modelType = bindingContext.ModelType;
var ctors = modelType.GetConstructors();
foreach (var ctor in ctors)
{
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
if (parameters.All(p => p != null))
{
var model = ctor.Invoke(parameters);
return model;
}
}
return null;
}
}
This binder will be provided by:
public class DiModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
return new DiModelBinder(propertyBinders);
}
return null;
}
}
Here's how the binder would be registered:
services.AddMvc().AddMvcOptions(options =>
{
// replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider
var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
var binderIndex = options.ModelBinderProviders.IndexOf(provider);
options.ModelBinderProviders.Remove(provider);
options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider());
});
I'm not quite sure if the new binder must be registered exactly at the same index, you can experiment with this.
And, at the end, this is how you can use it:
public class MyModel
{
private readonly IMyRepository repo;
public MyModel(IMyRepository repo)
{
this.repo = repo;
}
... do whatever you want with your repo
public string AProperty { get; set; }
... other properties here
}
Model class is created by the binder which supplies the (already registered) service, and the rest of the model binders provide the property values from their usual sources.
HTH
Is there another way than constructor injection to inject dependencies, and how?
The answer is "no", this cannot be done with "dependency injection". But, "yes" you can use the "service locator pattern" to achieve your end-goal.
You can use the code below to resolve a dependency without the use of constructor injection or the FromServices attribute. Additionally you can new up an instance of the class as you see fit and it will still work -- assuming that you have added the dependency in the Startup.cs.
public class MyRepository : IMyRepository
{
public IMyDependency { get; } =
CallContextServiceLocator.Locator
.ServiceProvider
.GetRequiredService<IMyDependency>();
}
The CallContextServiceLocator.Locator.ServiceProvider is the global service provider, where everything lives. It is not really advised to use this. But if you have no other choice you can. It would be recommended to instead use DI all the way and never manually instantiate an object, i.e.; avoid new.
I'm simply adding some supplemental information here to the answers provided that can help.
IServiceProvider was provided in the accepted answer, but not the important IServiceProvider.CreateScope() method. You can use it to create scopes as necessary that you added through ConfigureServices.
I'm not sure if IServiceProvider is actually a Service Locator pattern behind the scenes or not, but it's how you create scopes as far as I know. At least in the case if it is a Service Locator pattern, it's the official one for today in .NET, and so it's not compounded by the problems of writing your own Service Locator, which I also agree is anti-pattern.
Example, Startup.cs/ConfigureServices and Configure:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SomeDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("Databases").GetSection("SomeDb")["ConnectionString"]);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}, ServiceLifetime.Scoped);
services.AddMvcCore().AddNewtonsoftJson();
services.AddControllersWithViews();
}
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
{
...
IServiceScope scope = provider.CreateScope();
SomeDbContext context = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
SomeModelProxyClass example = new SomeModelProxyClass(context);
await example.BuildDefaults(
Configuration.GetSection("ProfileDefaults").GetSection("Something"),
Configuration.GetSection("ProfileDefaults").GetSection("SomethingSomething"));
scope.Dispose();
}
The above is for doing some default interactions on Startup, maybe if you need to build some default records in your database on a first usage, just as an example.
Ok so let's get to your repository and dependency though, will they work?
Yep!
Here's a test in my own CRUD project, I made a simple minimalist implementation of your IMyDependency and IMyRepository like so, then added them scoped as you did to Startup/ConfigureServices:
public interface IMyRepository
{
string WriteMessage(string input);
}
public interface IMyDependency
{
string GetTimeStamp();
}
public class MyDependency : IMyDependency
{
public MyDependency()
{
}
public string GetTimeStamp()
{
return DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
}
}
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public string WriteMessage(string input)
{
return input + " - " + _myDependency.GetTimeStamp();
}
}
Here ContextCRUD is a Model class from my own project not derived from Scaffold-DbContext tooling like my other database classes, it's a container of logic from those scaffold Model classes, and so I put it in the namespace Models.ProxyModels to hold its own business logic for doing CRUD operations so that the Controllers are not gummed up with logic that should be in the Model:
public ContextCRUD(DbContext context, IServiceProvider provider)
{
Context = context;
Provider = provider;
var scope = provider.CreateScope();
var dep1 = scope.ServiceProvider.GetService<IMyRepository>();
string msg = dep1.WriteMessage("Current Time:");
scope.Dispose();
}
Debugging I get back the expected results in msg, so it all checks out.
The calling code from the Controller for reference, just so you can see how IServiceProvider is passed from upstream by constructor injection in the Controller:
[Route("api/[controller]")]
public class GenericController<T> : Controller where T: DbContext
{
T Context { get; set; }
ContextCRUD CRUD { get; set; }
IConfiguration Configuration { get; set; }
public GenericController(T context, IConfiguration configuration, IServiceProvider provider)
{
Context = context;
CRUD = new ContextCRUD(context, provider);
Configuration = configuration;
}
...
You can do it, check out [InjectionMethod] and container.BuildUp(instance);
Example:
Typical DI constructor (NOT NEEDED IF YOU USE InjectionMethod) public
ClassConstructor(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }
This attribute causes this method to be called to setup DI.
[InjectionMethod] public void Initialize(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }

Passing a unique value to all classes using Dependency Injection?

The following code shows the flow I’m currently trying to implement within a WCF service. The service on startup calls the Bootstrapper class which uses Unity to register and resolve the required types. The Gateway class contains the public method which then kicks off the main flow of processing a message (there are many more levels to the code than is shown below).
public static class Bootstrapper
{
public static IGateway InitializeGateway()
{
IUnityContainer resolver = new UnityContainer();
resolver.RegisterType<IGateway, Gateway>();
resolver.RegisterType<ITranslator, Translator>();
resolver.RegisterType<IFormatter, IFormatter>();
return resolver.Resolve<IGateway>();
}
}
public class Gateway : IGateway
{
private readonly ITranslator translator;
private readonly IFormatter formatter;
public Gateway(ITranslator translator, IFormatter formatter)
{
this.translator = translator;
this.formatter = formatter;
}
public string ProcessMessage(string requestMessage)
{
// Create a new GUID for use in main flow for logging
Guid messageGuid = Guid.NewGuid();
requestMessage = this.translator.TranslateMessage(requestMessage);
requestMessage = this.formatter.FormatMessage(requestMessage);
return requestMessage;
}
}
Now what I’m trying to achieve is take the GUID (created for each message) and pass this down within the flow of the service such that each class has a reference to it for logging purposes.
I have tried to find a way of using DI and constructor injection but don’t know if this can be done as the GUID is created on receipt of a message by the gateway (after the bootstrapper call). What I’m trying to get away from is passing the GUID into each method as a parameter.
Any suggestions?
Instead of rolling your own solution to this problem with DI, I would recommend you use the thread-static property Trace.CorrelationManager.ActivityId for this purpose.
Take a look at this article on WCF End-To-End Tracing.

Autofac shared objects require different registrations per controller but InstancePerApiControllerType won't work

As detailed in InstancePerApiControllerType not working, I am unable to use the InstancePerApiControllerType to configure my solution. The answer provided there works so long as I am directly injecting a ConnectionContext into the controller, or otherwise know that a class is only used by a specific controller. Unfortunately that is not the case in my situation:
ControllerA -> EngineA -> RepositoryA -> GenericEntityAccessor
ControllerB -> EngineB -> RepositoryB -> GenericEntityAccessor
The issue is when we come in through ControllerA, GenericEntityAccessor needs "string A" and from ControllerB it needs "string B".
Of course, the real situation is a little more complicated and there are some bad practices such as code that directly "news"-up a ConnectionContext (it's legacy code). I'm currently exploring providing another component that provides the connection string that is injected via Autofac and configured in the controller using Lazy, but the bad practices are causing problems there also (i.e. once I start to change things in the interface, all the dominoes start to fall over and I end up 15 classes later wondering how I got there).
Are there any patterns, techniques, etc. that address this type of thing? I can't imagine it's all that uncommon.
UPDATE:
To provide a few more specifics, since I'm having some trouble getting this to work, in general we have the following hierarchy, showing which scopes I've applied
Controller -> InstancePerApiRequest()
I*Repository -> ?
I*Manager -> ?
I*Builder -> ?
I*Adapter -> ?
ISqlServerConnectionContext -> ?
IConnectionContextCache -> InstancePerApiRequest()
I've got a number of components that directly take ISqlServerConntectionContext and I'm trying to provide it like so:
container.Register(c =>
{
var connectionContextCache = c.Resolve<IConnectionContextCache>();
var connection = (ISqlServerConnectionContext)connectionContextCache.CurrentConnectionContext;
return connection;
}).As<ISqlServerConnectionContext>().InstancePerDependency();
Unfortunately at that point I'm getting a null for CurrectConnectionContext. My guess at this point is I've got some component that isn't rooted from the controller and I'm currently going through the dependencies manually attempting to find it (AFAIK the isn't a way for my to find out which object triggered Autofac to attempt to provide the ISqlServerConnectionContext when I'm debugging).
UPDATE 2:
It turns out I did have some issues where I was registering things improperly, and creating a dependency on ISqlServerConnectionContext for DocumentController, even though it did not have one (this was created through the delegate for something it did depend on).
Now I've got a circular reference that I'm pretty sure I've created myself in the registrations:
container.Register(x =>
{
if (x.IsRegistered<HttpRequestMessage>())
{
var httpRequestMethod = x.Resolve<HttpRequestMessage>();
var tokenHelper = x.Resolve<ITokenHelper>();
var token = tokenHelper.GetToken(httpRequestMethod);
return token ?? new NullMinimalSecurityToken();
}
return new NullMinimalSecurityToken();
}).As<IMinimalSecurityToken>().InstancePerApiRequest();
container.Register(c =>
{
var connectionContextCache = c.Resolve<IConnectionContextCache>();
var token = c.Resolve<IMinimalSecurityToken>();
var connection = (ISqlServerConnectionContext)connectionContextCache.CurrentConnectionContext;
connection.Token = token;
return connection;
}).As<ISqlServerConnectionContext>().InstancePerApiRequest();
The problem is ISqlServerConnectionContext has a property of type IMinimalSecurityToken which is optional, and definitely not used when the ISqlServerConnectionContext is being used to look up IMinimalSecurityToken, which depends on ISqlServerConnectionContext through ITokenHelper.
UPDATE 3:
For completeness, in order to solve my circular reference problem I needed to use named services, and use a SqlServerConnectionContext that did not have the IMinimalSecurityToken property set for the IOAuthTokenManager registration. Now I'm getting the dreaded
No scope with a Tag matching 'AutofacWebRequest' is visible
error, but I think that warrants a new question if I'm not able to solve it.
container.Register(c =>
{
var productId = WellKnownIdentifierFactory.Instance.GetWellKnownProductIdentifier(WellKnownProductIdentifiers.RESTSearchService);
var connectionString = ConfigurationManager.AppSettings[AppSettingsNames.DatabaseConnection];
var newConnectionContext = new SqlServerConnectionContext(connectionString) { ProductID = productId };
newConnectionContext.Open();
return newConnectionContext;
}).Named<ISqlServerConnectionContext>("OAuthTokenConnectionContext").InstancePerApiRequest();
container.Register(c => new SqlServerBuilderFactory(c.ResolveNamed<ISqlServerConnectionContext>("OAuthTokenConnectionContext"))).Named<IBuilderFactory>("OAuthTokenBuilderFactory").InstancePerApiRequest();
container.Register(c =>new OAuthTokenManager(c.ResolveNamed<IBuilderFactory>("OAuthTokenBuilderFactory"))).As<IOAuthTokenManager>().InstancePerApiRequest();
This can be solved using AutoFac's support for object graph lifetime scoping.
Cache the current SqlServerConnectionContext in an object scoped to the lifetime of your controller.
Within the SqlServerConnectionContext factory type, once the connection is created assign it to the backing field of the current lifetime-scoped cache
Any types scoped within the lifetimes scope of a controller can then access the connection associated with that controller through the cache
The only complexities I can think of are:
If the controller is not actually the root of a lifetime scope for all types with a dependency on a specific connection. I.e. if they fall outside the lifetime of the controller.
If any of the dependencies are registered as single instance. In which case they will not be able to resolve the Cache as it is currently implemented as it is PerApiRequest.
For example:
public interface ISqlServerConnectionContextCache
{
ISqlServerConnectionContext CurrentContext { get; set; }
}
public class SqlServerConnectionContextScopeCache : ISqlServerConnectionContextCache
{
public ISqlServerConnectionContext CurrentContext { get; set; }
}
public interface ISqlServerConnectionContextFactory
{
ISqlServerConnectionContext Create();
}
// The factory has the cache as a dependancy
// This will be the first use of the cache and hence
// AutoFac will create a new one at the scope of the controller
public class SqlServerConnectionContextFactory : ISqlServerConnectionContextFactory
{
private string _connectionString;
private ISqlServerConnectionContextCache _connectionCache;
public SqlServerConnectionContextFactory(ISqlServerConnectionContextCache connectionCache,
string connectionString)
{
_connectionCache = connectionCache;
_connectionString = connectionString;
}
public ISqlServerConnectionContext Create()
{
var connectionContext = new SqlServerConnectionContext(_connectionString);
connectionContext.Open();
_sqlServerConnectionContextProvider.CurrentContext = connectionContext;
return connectionContext;
}
}
public class MyController : ApiController
{
private ISqlServerConnectionContext _sqlServerConnectionContext;
public MyController(Func<string, ISqlServerConnectionContextFactory> connectionFactory)
{
_sqlServerConnectionContext = connectionFactory("MyConnectionString");
}
}
// As the cache is lifetime scoped it will receive the single instance
// of the cache associated with the current lifetime scope
// Assuming we are within the scope of the controller this will receive
// the cache that was initiated by the factory
public class MyTypeScopedByController
{
public MyTypeScopedByController(ISqlServerConnectionContextCache connectionCache)
{
var sqlServerConnectionContext = connectionCache.CurrentContext;
}
}
// AutoFac wiring
builder.RegisterType<SqlServerConnectionContextScopeCache>()
.As<ISqlServerConnectionContextCache>()
.InstancePerApiRequest();
builder.RegisterType<SqlServerConnectionContextFactory>()
.As<ISqlServerConnectionContextFactory>()
.InstancePerDependency();

Autofac WCF Integration - Resolve dependencies based on request data

How can I configure an Autofac container so it resolves the dependencies of a WCF service based on properties values of the operation-parameter (request object)?
For example, given this data contract...
[DataContract]
public class MyRequest
{
[DataMember]
public bool MyBool { get; set; }
}
this WCF service...
public class MyWcfService : IWcfService
{
private IService m_service;
public MyWcfService(IService service)
{
m_service = service;
}
public virtual MyResponse Operation(MyRequest request) { }
}
and these dependencies...
public interface IService { }
public class TypeA : IService { }
public class TypeB : IService { }
I would like the container to resolve TypeA if MyBool equals true and TypeB otherwise. Is that feature available? Should I approach the problem differently?
Constraints:
Avoiding the Autofac.Extras.Multitenant package is a plus.
Keeping the signature of the service constructor unchanged is also desired. (See my answer below)
There are a few ways to achieve this. One of the ways is to use IIndex<K,V>. It's built-in "lookup" feature that chooses between service implementations based on a key. You can find more info on Autofac's wiki page. An example code could look like:
// Register your dependency with a key, for example a bool flag
builder.RegisterType<TypeA>().Keyed<IService>(true);
builder.RegisterType<TypeB>().Keyed<IService>(false);
// Your service could look like:
public class MyWcfService
{
private readonly IIndex<bool, IService> _services;
// Inject IIndex<Key,Value> into the constructor, Autofac will handle it automatically
public MyWcfService(IIndex<bool, IService> services)
{
_services = services;
}
public virtual void Operation(MyRequest request)
{
// Get the service that you need by the key
var service = _services[request.MyBool];
}
}
Another approach is to use Metadata feature. More information on wiki page.
Option 1 - Using Autofac:
The Autofac instance provider that creates your service instance does not use or pass along the operation's message. Here's the latest implementation of the method in Autofac. Notice the message parameter is unused.
public class AutofacInstanceProvider : IInstanceProvider
{
// lots of code removed...
public object GetInstance(InstanceContext instanceContext, Message message)
{
if (instanceContext == null)
{
throw new ArgumentNullException("instanceContext");
}
var extension = new AutofacInstanceContext(_rootLifetimeScope);
instanceContext.Extensions.Add(extension);
return extension.Resolve(_serviceData);
}
}
So to get the behavior you want with existing Autofac code, you'll need to inject the dependency into your class using something other than constructor injection, which is #Alexandr Nikitin's solution. This is reasonable, but I agree with the comment "not loving it".
Option 2 - A Custom IInstanceProvider:
Writing a custom WCF IInstanceProvider is a reasonable option, but it will be a lot of code.
The good news is that the code in Autoface.Integration.WCF is a nice example and you could plug your implementation into Autofac.
The bad news is that Autofac.Integration.WCF code doesn't itself use dependency injection. For example AutofacDependencyInjectionServiceBehavior directly calls var instanceProvider = new AutofacInstanceProvider(_rootLifetimeScope, _serviceData). As a result you'll you have to implement a replacement for AutofacInstanceProvider, AutofacDependencyInjectionServiceBehavior, AutofacHostFactory, and probably more. Then you'll need to create an extension for the AutofacInstanceContext to contain the information read from the message. Its a lot of code.
If you are going to do the custom IInstanceProvider I suggest reading up on Carlos Figueira's blog:
WCF Extensibility – IInstanceProvider - for good background
WCF Extensibility – Message Inspectors - Search for the section that starts with WCF Message objects can only be “consumed once". You'll need to follow these rules when inspecting the message.

Object factory which Creates objects that require dependencies

Currently in code i have used an object factory to return me a processor based of a string tag, which has severed its purpose up until now.
using Core;
using Data;
public static class TagProcessorFactory
{
public static ITagProcessor GetProcessor(string tag)
{
switch (tag)
{
case "gps0":
return new GpsTagProcessor();
case "analog_manager":
return new AnalogManagerTagProcessor();
case "input_manager":
return new InputManagerTagProcessor();
case "j1939":
return new J1939TagProcessor(new MemcachedProvider(new[] { "localhost" }, "DigiGateway"), new PgnRepository());
default:
return new UnknownTagProcessor();
}
}
}
Calling Code
var processor = TagProcessorFactory.GetProcessor(tag.Name);
if (!(processor is UnknownTagProcessor))
{
var data = processor.Process(unitId, tag.Values);
Trace.WriteLine("Tag <{0}> processed. # of IO Items => {1}".FormatWith(tag.Name, data.Count()));
}
as you can see one of my items has dependencies and im trying to execute testing code and i want to pass in mock repositories and cache providers but i can seem to think of a way to do this.
Is this a bad design or anyone have any ideas to fix it to make my factory testable?
Thanks
Since you are using Autofac, you can take advantage of the lookup relationship type:
public class Foo
{
private readonly IIndex<string, ITagProcessor> _tagProcessorIndex;
public Foo(IIndex<string, ITagProvider> tagProcessorIndex)
{
_tagProcessorIndex = tagProcessorIndex;
}
public void Process(int unitId, Tag tag)
{
ITagProcessor processor;
if(_tagProcessorIndex.TryGetValue(tag.Name, out processor))
{
var data = processor.Process(unitId, tag.Values);
Trace.WriteLine("Tag <{0}> processed. # of IO Items => {1}".FormatWith(tag.Name, data.Count()));
}
}
}
See the TypedNamedAndKeysServices wiki article for more information. To register the various processors, you would associate each with its key:
builder.RegisterType<GpsTagProcessor>().Keyed<ITagProcessor>("gps0");
builder.RegisterType<AnalogManagerTagProcessor>().Keyed<ITagProcessor>("analog_manager");
builder.RegisterType<InputManagerTagProcessor>().Keyed<ITagProcessor>("input_manager");
builder
.Register(c => new J1939TagProcessor(new MemcachedProvider(new[] { "localhost" }, new PgnRepository()))
.Keyed<ITagProcessor>("j1939");
Notice we don't register UnknownTagProcessor. That was a signal to the caller of the factory that no processor was found for the tag, which we express using TryGetValue instead.
Using something like StructureMap you could use the ObjectFactory which, when configured would return you a named concrete instance.
http://structuremap.net/structuremap/index.html
I suggest you look through another SO post. It solves several problems at once, including how to replace contructor values - without a mess. Specifically, the parameters to the constructor simply become static fields of a "Context" class, which are read by the constructor of the interior class.

Categories