How to configure dependency injection with ASP.NET MVC 3 RTM

…衆ロ難τιáo~ 提交于 2019-12-03 16:59:24

I was able to get StructureMap to work with ASP.NET MVC3 by creating a Dependency Resolver(IDependencyResolver) class, then registering that class in the global.asax. I have not fully tested this code. But, it has been working without any issues in two applications.

StructureMapDependencyResolver.cs

using System.Linq;
using System.Web.Mvc;
using StructureMap;

namespace SomeNameSpace
{
    public class StructureMapDependencyResolver : IDependencyResolver
    {
        private readonly IContainer container;

        public StructureMapDependencyResolver(IContainer container)
        {
            this.container = container;
        }

        public object GetService(System.Type serviceType)
        {
            try
            {
                return this.container.GetInstance(serviceType);
            }
            catch
            {
                return null;
            }
        }

        public System.Collections.Generic.IEnumerable<object> GetServices(System.Type serviceType)
        {
            return this.container.GetAllInstances<object>()
                .Where(s => s.GetType() == serviceType);
        }
    }
}

Global.asax.cs

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            DependencyResolver.SetResolver(new StructureMapDependencyResolver(InitContainer()));
            RegisterGlobalFilters(GlobalFilters.Filters);            
            RegisterRoutes(RouteTable.Routes);
        }

        private static IContainer InitContainer()
        {
            ObjectFactory.Initialize(x =>
            {
                x.Scan(y =>
                {
                    y.WithDefaultConventions();
                    y.AssembliesFromApplicationBaseDirectory();
                    y.LookForRegistries();
                });
            });

            return ObjectFactory.Container;
        }

I've figured this out thanks to the link @Michael Carman posted in a comment on his answer. I'm not sure of the etiquette here as to whether that warrants accepting his actual answer as it wasn't quite right (I've given him +1 vote) but I thought I'd post my own answer to explain exactly what the issue was.

The problem was down to a combination of my implementation of IDependencyResolver and my container configuration. Originally I had:

public class StructureMapDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        return ObjectFactory.GetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        foreach (object obj in ObjectFactory.GetAllInstances(serviceType))
        {
            yield return obj;
        }
    }
}

but I have now changed to this based on Steve Smith's blog post linked to in Jeremy Miller's blog post:

public class StructureMapDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        if (serviceType.IsAbstract || serviceType.IsInterface)
        {
            return ObjectFactory.TryGetInstance(serviceType);
        }
        else
        {
            return ObjectFactory.GetInstance(serviceType);
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        foreach (object obj in ObjectFactory.GetAllInstances(serviceType))
        {
            yield return obj;
        }
    }
}

on its own this still doesn't resolve the issue until I remove this configuration expression:

x.For<IControllerFactory>().Use<DefaultControllerFactory>();

According to the documentation TryGetInstance only returns types registered with the container and will return null if none exist. I presume the MVC 3 code relies on this behaviour to indicate that it should use its defaults, hence in my original case I had to register these defaults with my container. Tricky one!

Alvin

This works for me for both MVC and Web API..

namespace Web.Utilities.DependencyResolvers
{
    public class StructureMapResolver : IServiceLocator, IDependencyResolver
    {
        private readonly IContainer _container;

        public StructureMapResolver(IContainer container)
        {
            if (container == null)
                throw new ArgumentNullException("container");

            this._container = container;
        }

        public IDependencyScope BeginScope()
        {
            return new StructureMapResolver(this._container.GetNestedContainer());
        }

        public object GetInstance(Type serviceType, string instanceKey)
        {
            if (string.IsNullOrEmpty(instanceKey))
            {
                return GetInstance(serviceType);
            }

            return this._container.GetInstance(serviceType, instanceKey);
        }

        public T GetInstance<T>()
        {
            return this._container.GetInstance<T>();
        }

        public object GetService(Type serviceType)
        {
            return GetInstance(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return this._container.GetAllInstances(serviceType).Cast<object>();
        }

        public T GetInstance<T>(string instanceKey)
        {
            return this._container.GetInstance<T>(instanceKey);
        }

        public object GetInstance(Type serviceType)
        {
            return serviceType.IsAbstract || serviceType.IsInterface ?
                this._container.TryGetInstance(serviceType) : this._container.GetInstance(serviceType);
        }

        public IEnumerable<T> GetAllInstances<T>()
        {
            return this._container.GetAllInstances<T>();
        }

        public IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return this._container.GetAllInstances(serviceType).Cast<object>();
        }

        public void Dispose()
        {
            this._container.Dispose();
        }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!