Autofac property injection
I am in the process of changing my Asp.Net MVC3 project to use Autofac for service injection into my controllers. So far this has been pretty straightforward. My services all have a Telerik OpenAccess db property which I inject through the constructors (In a service base class). And my controllers all have constructor properties for services which also get injected.
I have a class called AuditInfo which encapsulates auditable properties of a controller:
public class AuditInfo
{
public string RemoteAddress { get; set; }
public string XForwardedFor { get; set; }
public Guid UserId { get; set; }
public string UserName { get; set; }
}
My OpenAccess db property in my service classes needs to have an instance of this class injected in to it in order to use as auditing information in various 开发者_StackOverflow社区database calls.
The problem is that this is not a class that can be instantiated at Application_Start because at least two properties of it, RemoteAddress and XForwardedFor are populated at the earliest stage of OnActionExecuting, i.e. once the Request variables exist.
Therefore, I instantiate this in the OnActionExecuting method of my BaseController class as such:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
db.AuditInfo = AuditInfo;
}
public AuditInfo AuditInfo
{
get
{
return new AuditInfo()
{
RemoteAddress = this.Request.ServerVariables["REMOTE_ADDR"],
XForwardedFor = this.Request.ServerVariables["X_FORWARDED_FOR"],
UserId = this.UserId,
UserName = this.UserName
};
}
}
So - my problem/questions are:
- I don't like this direct reach in to the OpenAccess db property in OnActionExecuting.
- I'd like this AuditInfo basically to be injected in to any AuditInfo property anywhere
- I don't think I can use constructor injection for AuditInfo because Services depend on db - controllers depend on services - db depends on AuditInfo BUT AuditInfo is not available until a controller is instantiated and received its first request. => circular dependency...
How would I setup autofac to inject AuditInfo in to any class that has it as a property? Or is there a better way of sidestepping the circular dependency and using some form of lambda/lazy constructor properties?
Is it at all concerning that AuditInfo gets re-initialized potentially unnecessarily at every request even though a lot of requests may be part of the same session and not have different ip address/user info?
Thanks
It turns out Autofac's MVC Integration can resolve an HttpRequestBase
for you. So you don't need to reference HttpContext.Current.Request
directly.
Autofac's implementation uses HttpContext.Current
behind the scenes. This works because the MVC framework sets HttpContext.Current
before your code (or Autofac's) runs. So there's no circular dependency - the Request "naturally exists" on HttpContext.Current.Request
just as much as in your controller. (This question kind of explains how)
So you could do an IAuditInfoFactory
as Steven suggests but demand an HttpRequestBase
in its constructor instead of using HttpContext.Current
if it makes you feel better about not referencing static variables.
Also, there's no circular dependency and you could constructor-inject the AuditInfo
if you want:
builder.Register(c => c.Resolve<IAuditInfoFactory>().CreateNew())
.As<AuditInfo>()
.InstancePerHttpRequest();
The answer is: Use a factory.
Inject an IAuditInfoFactory
into the type that needs it, and create an implementation like this:
public class HttpRequestAuditInfoFactory : IAuditInfoFactory
{
// Service for requesting information about the current user.
private readonly ICurrentUserServices user;
public HttpRequestAuditInfoFactory(ICurrentUserServices user)
{
this.user = user;
}
AuditInfo IAuditInfoFactory.CreateNew()
{
var req = HttpContext.Current.Request;
return new AuditInfo()
{
RemoteAddress = req.ServerVariables["REMOTE_ADDR"],
XForwardedFor = req.ServerVariables["X_FORWARDED_FOR"],
UserId = this.user.UserId,
UserName = this.user.UserName
};
}
}
You can register that class as follows:
builder.RegisterType<HttpRequestAuditInfoFactory>()
.As<IAuditInfoFactory>()
.SingleInstance();
Now you can inject
精彩评论