31

AutoFac has recently been updated for MVC 5.1 but at the time of writing I find that the documentation is lacking (especially for a simple example).

I would like to inject dependencies into MVC Controllers and register my own implementations for e.g. e-mail (actual sending vs print to output window) as a basic example.

I'm do not know if I am missing a good resource for this and I am slight concerned that because it uses the OWIN specification that the implementation may differ for MVC5.1 (ASP.NET Identity uses OWIN and there is some special attributes used to properly instantiate OWIN) so need to check I am getting things right.

I have a working example with the below setup code - is this correct and good practice for a standard MVC5.1 web app?

Bonus question: do I need to add InstancePerHttpRequest to the RegisterControllers line? i.e. builder.RegisterControllers(typeof(MvcApplication).Assembly).InstancePerHttpRequest();

(Note: I see the examples on GitHub from Autofac but cannot find an plain example appropriate to MVC5.1.)

public static void RegisterDependencies()
{
    // Register MVC-related dependencies
    var builder = new ContainerBuilder();
    builder.RegisterControllers(typeof(MvcApplication).Assembly);
    builder.RegisterModelBinders(typeof(MvcApplication).Assembly);
    builder.RegisterModule<AutofacWebTypesModule>();

    // Register e-mail service
    builder.RegisterType<EmailSenderToDebug>().As<IEmailSender>().InstancePerHttpRequest();

    builder.RegisterModelBinderProvider();

    // Set the MVC dependency resolver to use Autofac
    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}

And my controller:

public class HomeController : Controller
{

    public IEmailSender _email { get; set; }

    public  HomeController(IEmailSender es)
    {
        this._email = es;
    }
    //
    // GET: /Home/
    public ActionResult Index()
    {

        Email e = new Email();
        e.To(new MailAddress("something@something.com", "mr. something"));
        e.Subject("test");
        e.Message("hello world");
        e.From(new MailAddress("somethingElse@somethingElse.com", "mr. else"));
        this._email.SendEmail(e);
        return View();
    }
}

Thanks!

Maria Ines Parnisari
  • 16,584
  • 9
  • 85
  • 130
DanAbdn
  • 7,151
  • 7
  • 27
  • 38

1 Answers1

43

Here's what I have - feedback welcome!

Global.asax.cs:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Register Inversion of Control dependencies
        IoCConfig.RegisterDependencies();

        // Typical MVC setup
        // ....
    }
}

App_Start folder:

public class IoCConfig
{

    /// <summary>
    /// For more info see 
    /// :https://code.google.com/p/autofac/wiki/MvcIntegration (mvc4 instructions)
    /// </summary>
    public static void RegisterDependencies()
    {
        #region Create the builder
        var builder = new ContainerBuilder();
        #endregion

        #region Setup a common pattern
        // placed here before RegisterControllers as last one wins
        builder.RegisterAssemblyTypes()
               .Where(t => t.Name.EndsWith("Repository"))
               .AsImplementedInterfaces()
               .InstancePerHttpRequest();
        builder.RegisterAssemblyTypes()
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces()
               .InstancePerHttpRequest();
        #endregion

        #region Register all controllers for the assembly
        // Note that ASP.NET MVC requests controllers by their concrete types, 
        // so registering them As<IController>() is incorrect. 
        // Also, if you register controllers manually and choose to specify 
        // lifetimes, you must register them as InstancePerDependency() or 
        // InstancePerHttpRequest() - ASP.NET MVC will throw an exception if 
        // you try to reuse a controller instance for multiple requests. 
        builder.RegisterControllers(typeof(MvcApplication).Assembly)
               .InstancePerHttpRequest();

        #endregion

        #region Register modules
        builder.RegisterAssemblyModules(typeof(MvcApplication).Assembly);
        #endregion

        #region Model binder providers - excluded - not sure if need
        //builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
        //builder.RegisterModelBinderProvider();
        #endregion

        #region Inject HTTP Abstractions
        /*
         The MVC Integration includes an Autofac module that will add HTTP request 
         lifetime scoped registrations for the HTTP abstraction classes. The 
         following abstract classes are included: 
        -- HttpContextBase 
        -- HttpRequestBase 
        -- HttpResponseBase 
        -- HttpServerUtilityBase 
        -- HttpSessionStateBase 
        -- HttpApplicationStateBase 
        -- HttpBrowserCapabilitiesBase 
        -- HttpCachePolicyBase 
        -- VirtualPathProvider 

        To use these abstractions add the AutofacWebTypesModule to the container 
        using the standard RegisterModule method. 
        */
        builder.RegisterModule<AutofacWebTypesModule>();

        #endregion

        #region Set the MVC dependency resolver to use Autofac
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        #endregion

    }

 }

Then you can register modules as you see fit which is really great if you have a modularised application and you don't know what you are going to include in your project. You can nuget setup your project and no manual intervention required (I wish!)....

public class RegisterApplicationIoC : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<EmailSenderToDebug>()
               .As<IEmailSender>()
               .InstancePerHttpRequest();
    }
}
dav_i
  • 27,509
  • 17
  • 104
  • 136
DanAbdn
  • 7,151
  • 7
  • 27
  • 38
  • 26
    `InstancePerHttpRequest` was deprecated for `AutoFac 3.4.0` and `4.0.0` the replacement being `InstancePerRequest`. See [here](https://github.com/autofac/Autofac/commit/78e3ff71286fab6185ea8a89ae622825afa741a5) – John Lieb-Bauman May 09 '14 at 18:44
  • Adding `IoCConfig.RegisterDependencies();` to `Global.asax` asks me to add the respective namespace. On doing that I get the following error `A reference to 'Bootstrapper' could not be added. Adding this project as a reference would cause a circular dependency.` I know **why** of this error. What I want to know is **how** to resolve it? – phougatv Dec 02 '16 at 06:13
  • I was able to run it after moving my `IocConfig.cs` file to `App_Start` folder. Problem from my last comment was also solved. Now I want to know **how** to maintain the folder structure? What are the standards of creating folder/project structure while using autofac? Is there a way where I can place my `IocConfig` file in some other folder? Kindly provide links(if there are any). – phougatv Dec 02 '16 at 09:11