Как настроить внедрение зависимостей с помощью ASP.NET MVC 3 RTM - PullRequest
4 голосов
/ 16 января 2011

Я обновляю веб-приложение с ASP.NET 3 Preview 1 до окончательной первоначальной версии, и меня смущает обновленный подход к внедрению зависимостей. Я использую StructureMap для этого, но это не совсем относится к моему вопросу. Ранее все, что мне нужно было сделать, было следующим:

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

Теперь кажется, что мне нужно предоставить реализации IControllerActivator, IViewPageActivator и ModelMetadataProvider, потому что в противном случае я получаю ошибку от StructureMap, потому что MVC пытается найти их с помощью средства разрешения зависимостей. Из взгляда на источник MVC, похоже, не существует общедоступных реализаций по умолчанию. Я что-то упускаю при настройке? Конечно, они должны быть настроены по соглашению?

Примеры того, что необходимо настроить и как с StructureMap будет оценен. Для справки я в настоящее время использую следующий уродливый ключ, который заставляет 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 Dependency Resolver - проблема в том, почему MVC жалуется на то, что все эти интерфейсы не настроены в контейнере.

Ответы [ 3 ]

8 голосов
/ 17 января 2011

Мне удалось заставить StructureMap работать с ASP.NET MVC3, создав класс Respendency Resolver (IDependencyResolver), а затем зарегистрировав этот класс в global.asax.Я не полностью протестировал этот код.Но он работает без проблем в двух приложениях.

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;
        }
4 голосов
/ 16 марта 2011

Я понял это благодаря ссылке @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;
        }
    }
}

, но теперь я изменил это на основе сообщения в блоге Стива Смита , на которое ссылается сообщение в блоге Джереми Миллера :

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 полагается на это поведение, чтобы указать, что он должен использовать свои значения по умолчанию, поэтому в моем исходном случае мне пришлось зарегистрировать эти значения по умолчанию в моем контейнере.Хитрый!

0 голосов
/ 04 марта 2014

Это работает для меня как для 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();
        }
    }
}
...