Справка по внедрению зависимостей / внедрению - PullRequest
3 голосов
/ 21 июля 2011

У меня есть следующие классы / интерфейсы:

public interface IProjectRepository
{
    IQueryably<Project> GetProjects();
}

// Depends on my EF Context
public ProjectRepository : IProjectRepository
{
    private MyDbEntities context;

    public ProjectRepository(MyDbEntities context)
    {
        this.context = context;
    }

    public IQueryable<Project> GetProjects() 
    {
        return context.Projects;
    }
}

Мой контроллер:

 // Depends on IProjectRepository
 public class ProjectsController : Controller
 {
     private IProjectRepository projectRepository;

     public ProjectsController(IProjectRepository projectRepository)
     {
         this.projectRepository = projectRepository;
     }

     public ActionResult Index()
     {
         return View(projectRepository.GetProjects());
     }
 }

Мне нужно настроить внедрение зависимостей, чтобы оно передавалось в ProjectRepository в мой контроллер И это нужно передать в моем контексте Entity Framework в репозиторий проекта.Мне нужно, чтобы контекст сущности был задан как HTTP-запрос.

Я не уверен, куда я должен поместить весь код отображения, чтобы заставить внедрение зависимостей работать. Я тоже непонять, как MVC будет работать без конструктора по умолчанию.

Может кто-нибудь помочь мне собрать все части вместе?Я использую StructureMap, но я мог легко переключиться на что-то еще, потому что я понятия не имею, что я делаю.

Ответы [ 4 ]

4 голосов
/ 21 июля 2011

Если вы используете MVC 3, чтобы сделать все правильно, вы должны использовать встроенные биты разрешения зависимостей. Я настоятельно рекомендую вам прочитать серию сообщений в блоге Брэда Уилсона (члена команды ASP.NET MVC).

Что касается конкретной реализации StructureMap, я нашел полезными следующие посты в блоге.

StructureMap и ASP.NET MVC 3 - Начало работы
StructureMap, привязки модели и внедрение зависимостей в ASP.NET MVC 3
StructureMap, фильтры действий и внедрение зависимостей в ASP.NET MVC 3
StructureMap, фильтры глобальных действий и внедрение зависимостей в ASP.NET MVC 3

В любом случае, вот код. Для начала я бы предложил установить пакет NuGet StructureMap-MVC3 .

Я не могу вспомнить, что именно он создает в файлах, но вот что в основном связано.

/ App_Start / StructuremapMvc.cs - это подключается к Application_Start и устанавливает ваш контейнер (SmIoC.Initialize()), а затем устанавливает MVC 3 DependencyResolver в ваш SmDependencyResolver

using System.Web.Mvc;
using YourAppNamespace.Website.IoC;
using StructureMap;

[assembly: WebActivator.PreApplicationStartMethod(typeof(YourAppNamespace.App_Start.StructuremapMvc), "Start")]

namespace YourAppNamespace.Website.App_Start {
    public static class StructuremapMvc {
        public static void Start() {
            var container = SmIoC.Initialize();
            DependencyResolver.SetResolver(new SmDependencyResolver(container));
        }
    }
}

/ IoC / SmDependencyResolver.cs - это ваша реализация MVC 3 IDependencyResolver. Он используется в приведенном выше коде App_Start.

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

namespace YourAppNamespace.Website.IoC
{
    public class SmDependencyResolver : IDependencyResolver
    {
        private readonly IContainer _container;

        public SmDependencyResolver(IContainer container)
        {
            _container = container;
        }

        public object GetService(Type serviceType)
        {
            if (serviceType == null)
            {
                return null;
            }

            try
            {
                return _container.GetInstance(serviceType);
            }
            catch
            {
                return null;
            }
        }

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

/ IoC / SmIoC.cs - здесь вы настраиваете свой контейнер ... также используется в коде App_Start.

namespace YourAppNamespace.Website.IoC
{
    public static class SmIoC
    {
        public static IContainer Initialize()
        {
            ObjectFactory.Initialize(x =>
                        {
                            x.For<IProjectRepository>().Use<ProjectRepository>();
                            //etc...
                        });

            return ObjectFactory.Container;
        }
    }
}

Теперь все подключено ... (я думаю ;-), но у вас еще есть кое-что сделать. Внутри вашего Global.asax мы должны убедиться, что вы утилизируете все, что относится к области действия HttpContext.

protected void Application_EndRequest()
{
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}

Таким образом, вы должны быть в состоянии добиться внедрения зависимости через внедрение конструктора, что является правильным способом выполнения каких-либо действий.

0 голосов
/ 21 июля 2011

Если вы настроены на использование StructureMap, здесь - это учебное пособие по настройке, которое вам, вероятно, понадобится.

Некоторые другие инфраструктуры внедрения зависимостей поставляются с настраиваемыми фабриками контроллеров, которые будут делать этодля тебя. Ninject (например, внедрение зависимости с открытым исходным кодом) имеет расширение, которое вы можете использовать, которое содержит это поведение.См. здесь , например.И здесь с расширением.

Вы также можете использовать Unity IOC , который является еще одним популярным фреймворком для внедрения зависимостей, с помощью которого, насколько мне известно, вам придется создаватьнастраиваемая фабрика контроллеров (как со структурной картой) для достижения этого поведения.См. здесь для примера.

Вы также можете изучить все другие структуры внедрения зависимостей, чтобы увидеть, какую поддержку вы можете получить с каждой из них.

РЕДАКТИРОВАТЬ: Я надеюсь, что я объясняю это правильно, но вот некоторая справочная информация.

MVC использует фабрику контроллеров, которая отвечает за создание экземпляров соответствующих контроллеров, необходимых при выполнении запроса.По умолчанию он инициализирует контроллер, вызывая его конструктор без параметров.

Чтобы создать инфраструктуру для внедрения параметров конструктора, необходимо создать собственную фабрику, которая может разрешать параметры конструктора.Вот тут-то и появляются контейнеры внедрения зависимостей: по сути, контейнер DI (если он настроен правильно) знает, как разрешить эти зависимости, и ваша пользовательская фабрика будет использовать ее для запроса зарегистрированных зависимостей и передачи их конструктору контроллера.

0 голосов
/ 21 июля 2011

При использовании StructureMap у меня обычно было бы что-то вроде этого в моем контроллере:

private static IProjectRepository GetProjectRepository()
{
    var retVal = ObjectFactory.TryGetInstance<IProjectRepository>() 
                 ?? new ProjectRepository();
    return retVal;
}

Если TryGetInstance возвращает ноль (потому что для этого типа ничего не было установлено), по умолчанию будет указан конкретный тип, который вы укажете.

Теперь у вас есть загрузчик где-то вроде этого:

public static class StructureMapBootStrapper
{
    public static void InitializeStructureMap()
    {
        ObjectFactory.Initialize(x =>
        {
            x.For<IProjectRepository>().Use<ProjectRepository>();
        }
    }
}

Теперь вы вызываете этот загрузчик в вашем событии Global.asax Application_Start:

    protected void Application_Start()
    {
        StructureMapBootStrapper.InitializeStructureMap();
    }

Сейчас в тестепроект, когда вы хотите внедрить фиктивный репозиторий, вы можете просто сделать это:

    [TestMethod]
    public void SomeControllerTest()
    {
        StructureMap.ObjectFactory.Inject(
           typeof(IProjectRepository),
           new MockProjectRepository());

        // ... do some test of your controller with the mock
    }
0 голосов
/ 21 июля 2011

Все работают практически одинаково. Исторически у всех были установщики-инжекторы (настраивалось свойство, которое затем заполнялось), но у большинства теперь есть инъекция в конструктор. На карте структуры самый простой способ сделать это - использовать атрибут: [StructureMap.DefaultConstructor].

Как только вы добавите атрибут, объекты, которые вы поместили в свою «карту», ​​должны внедриться без какой-либо дополнительной работы. Если вы не можете использовать атрибуты, рассмотрите возможность использования установщика.

На сайте структуры карты есть файл: http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm

...