Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 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:

  1. I don't like this direct reach in to the OpenAccess db property in OnActionExecuting.
  2. I'd like this AuditInfo basically to be injected in to any AuditInfo property anywhere
  3. 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

like image 459
t316 Avatar asked Oct 05 '11 16:10

t316


People also ask

What is Autofac dependency injection?

Autofac is an open-source dependency injection (DI) or inversion of control (IoC) container developed on Google Code. Autofac differs from many related technologies in that it sticks as close to bare-metal C# programming as possible.

How do you inject a property?

In property injection, we need to pass object of the dependent class through a public property of the client class. Let's use the below example for the implementation of the Property Injection or even it is called Setter Injection as value or dependency getting set in property.

What is the use of Autofac?

Autofac is an IoC container for Microsoft . NET 4.5, Silverlight 5, Windows Store apps, and Windows Phone 8 apps. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity.

What is Property injection C#?

Property injection is a type of dependency injection where dependencies are provided through properties. Visit the Dependency Injection chapter to learn more about it. Let's understand how we can perform property injection using Unity container. Consider the following example classes. Example: C#


2 Answers

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();
like image 199
default.kramer Avatar answered Sep 28 '22 12:09

default.kramer


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

like image 39
Steven Avatar answered Sep 28 '22 14:09

Steven