如何使用ASP.NET MVC 3 RTM配置依赖项注入

时间:2011-01-16 17:10:40

标签: dependency-injection asp.net-mvc-3 structuremap

我正在将一个Web应用程序从ASP.NET 3 Preview 1升级到RTM,我对更新的依赖注入方法感到困惑。我正在使用StructureMap,但这与我的问题无关。以前我需要做的就是如下:

x.For<IControllerFactory>().Use<DefaultControllerFactory>();
x.For<IServiceLocator>().Use(MvcServiceLocator.Current);

现在看来我需要提供IControllerActivator,IViewPageActivator和ModelMetadataProvider的实现,因为否则我从StructureMap收到错误,因为MVC尝试使用依赖项解析器来定位它们。从MVC源看,似乎没有公共默认实现。我在设置这些内容时遗漏了什么?当然这些应该按惯例配置吗?

需要配置什么以及如何使用StructureMap的例子。作为参考,我目前使用以下丑陋的kludge迫使MVC使用其内部默认值:

x.For<IControllerFactory>().Use<DefaultControllerFactory>();
x.For<IDependencyResolver>().Use(() => DependencyResolver.Current);                
x.For<IControllerActivator>().Use(() => null);
x.For<IViewPageActivator>().Use(() => null);
x.For<ModelMetadataProvider>().Use(ModelMetadataProviders.Current);

编辑:为了清楚我有一个工作的依赖性解析器的StructureMap实现 - 问题是为什么MVC抱怨所有这些接口没有在容器中配置。

3 个答案:

答案 0 :(得分:8)

通过创建依赖性解析器(IDependencyResolver)类,然后在global.asax中注册该类,我能够使StructureMap与ASP.NET MVC3一起使用。我还没有完全测试这段代码。但是,它在两个应用程序中一直没有任何问题。

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;
        }

答案 1 :(得分:4)

我已经想到了这一点,感谢@Michael Carman发表评论他的回答。我不确定这里的礼节是否保证接受他的实际答案,因为它不太正确(我给他+1票)但我想我会发布自己的答案来解释究竟是什么问题是。

问题在于我的IDependencyResolver和我的容器配置的实现。原来我有:

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;
        }
    }
}

但我现在根据与Steve Smith's blog post相关联的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;
        }
    }
}

它本身仍然无法解决问题,直到我删除此配置表达式:

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

根据文档,TryGetInstance仅返回向容器注册的类型,如果不存在则返回null。我假设MVC 3代码依赖于这种行为来表明它应该使用它的默认值,因此在我原来的情况下我必须用我的容器注册这些默认值。整蛊一个!

答案 2 :(得分:0)

这适用于MVC和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();
        }
    }
}