问题
I am using Autofac as my IoC in my WCF service. I have a situation where I want to pass an object to a nested type (ie a type that is not resolved directly, but when resolving another type). As far as I understood, passing this object as a constructor parameter is the preferred way in Autofac. Here is an example of such a situation.
The nested type:
public class EventLogger<T> : IEventLogger<T>
{
public EventLogger(IRepository<T> repository, User currentUser) { ... }
}
The type I am actually trying to resolve:
public class SomeBusinessObject
{
public SomeBusinessObject(IEventLogger<SomeLogEventType> logger, ...) { ... }
}
The registration:
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterGeneric(typeof(EventLogger<>)).As(typeof(IEventLogger<>));
builder.RegisterType<SomeBusinessObject>();
The resolving inside my WCF service operation:
var currentUser = GetUserFromServiceContext();
var bo = lifetimeScope.Resolve<SomeBusinessObject>();
How and where should I pass the current user to my logger? Should I assume that the WCF operation has to know that resolving SomeBusinessObject requires to resolve IEventLogger first and pass a resolved instance when resolving SomeBusinessObject? Something like this (pardon me if this does not work, it is just an idea):
var currentUser = GetUserFromServiceContext();
var logger = lifetimeScope.Resolve<IEventLogger<SomeLogEventType>>(new NamedParameter("currentUser", currentUser));
var bo = lifetimeScope.Resolve<SomeBusinessObject>(new NamedParameter("logger", logger));
If this is the solution, what happens if the type is nested deeper? Doesn't that defeat at least some of the purpose of dependency injection?
回答1:
IMHO, I think you're violating one of the principles of IOC in that a component should not need to know about the dependencies of it's dependencies. In your case, the container doesn't know that SomeBusinessObject
has a dependency on User
.
That being said, you may be able to leverage Autofac's Delegate Factories. You could manually register a Func<User, SomeBusinessObject>
to hide the dependency chain details from the client code:
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterGeneric(typeof(EventLogger<>)).As(typeof(IEventLogger<>));
builder.RegisterType<SomeBusinessObject>();
builder.Register<Func<User, SomeBusinessObject>>(c => {
// Autofac should be able to resolve these Func<> automatically:
var loggerFactory = c.Resolve<Func<User, IEventLogger<SomeLogEventType>>>();
var sboFactory = c.Resolve<Func<IEventLogger<SomeLogEventType>, SomeBusinessObject>>();
// Now we can chain the Funcs:
return u => sboFactory(loggerFactory(u));
});
Now in your client code, you can do:
var currentUser = GetUserFromServiceContext();
var sboFactory = lifetimeScope.Resolve<Func<User, SomeBusinessObject>>();
var bo = sboFactory(currentUser);
As an aside, I think the lamba/Func support is what makes Autofac the best IOC container. You can do some crazy powerful things if you know how to compose Funcs.
来源:https://stackoverflow.com/questions/5741761/autofac-passing-parameter-to-nested-types