Контейнерное препятствие IoC для ASP.Net MVC Newb - PullRequest
1 голос
/ 22 июля 2010

Я должен признать, что я новичок в ASP.Net MVC, и в настоящее время я изучаю все лучшие практики о том, как начать свой новый проект. До сих пор я понимал концепции шаблона репозитория и единицы работы и перешел к внедрению зависимостей и инверсии контроля (IoC). Я изучал это в течение последних 2 дней и пришел к выводу, что мне нравятся 2 контейнера IoC, и они StructureMap и NInject, хотя ни один из них не идеален, и мне пока не удалось заставить StructureMap работать, хотя мне нравится свет синтаксис веса.

Вот как устроено мое приложение. Сначала у меня есть следующие интерфейсы для моего контекста данных и хранилища:

public interface IDataContext : IDisposable
{
    IRepository<T> Repository<T>() where T : class;
    void Commit();
}

public interface IRepository<T> where T : class
{
    IEnumerable<T> GetAll();
    IEnumerable<T> Find(Expression<Func<T, bool>> where);
    T Single(Expression<Func<T, bool>> where);
    void Insert(T entity);
    void Delete(T entity);
}

Тогда у меня есть реализация LinqToSql, например, так:

public class LinqToSqlDataContext : IDataContext
{
    private readonly DataContext _context;

    public LinqToSqlDataContext(DataContext context)
    {
        _context = context;
    }

    public IRepository<T> Repository<T>() where T : class
    {
        return new LinqToSqlRepository<T>(_context);
    }

    public void Commit()
    {
        _context.SubmitChanges();
    }

    public void Dispose()
    {

    }
}

public class LinqToSqlRepository<T> : IRepository<T> where T : class
{
    private readonly DataContext _context;

    public LinqToSqlRepository(DataContext context)
    {
        _context = context;
    }

    public IEnumerable<T> GetAll()
    {
        return _context.GetTable<T>();
    }

    public IEnumerable<T> Find(Expression<Func<T, bool>> where)
    {
        return _context.GetTable<T>().Where(where);
    }

    public T Single(Expression<Func<T, bool>> where)
    {
        return _context.GetTable<T>().SingleOrDefault(where);
    }

    public void Insert(T entity)
    {
        _context.GetTable<T>().InsertOnSubmit(entity);
    }

    public void Delete(T entity)
    {
        _context.GetTable<T>().DeleteOnSubmit(entity);
    }
}

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

  1. В конструкторе контроллера
  2. В атрибуте аннотации данных (который не может иметь конструктора)

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

Теперь о моих реализациях контейнера IoC. Первый NInject Мне удалось изменить мой файл Global.asax.cs следующим образом:

public class MvcApplication : NinjectHttpApplication
{
    protected override void OnApplicationStarted()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }

    protected override IKernel CreateKernel()
    {
        var kernel = new StandardKernel(new ServiceModule());

        // Gives my wrapper class access to the kernel instance
        IoCContainer.Initialize(kernel);

        return kernel;
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
        ...
    }
}

internal class ServiceModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDataContext>().To<LinqToSqlDataContext>().InRequestScope();
        Bind<DataContext>().To<MyDataContext>().InRequestScope();
    }
}

public static class IoCContainer  
{ 
    private static IKernel _kernel;

    public static void Initialize(IKernel kernel)  
    {
        _kernel = kernel;
    }

    public static T Get<T>()
    {
        return _kernel.Get<T>();
    }

    public static object Get(Type type)
    {
        return _kernel.Get(type);
    }
}

Это хорошо работает. Конструкторам контроллеров теперь автоматически устанавливаются зависимости, и в моем атрибуте аннотации данных я могу сказать:

var context = IoCContainer.Get<IDataContext>();

Мне нравится NInject, но даже с файлом Global.asax.cs, унаследованным от NinjectHttpApplication (который обрабатывает большую часть сантехники), я все еще чувствую, что я хочу удалить довольно много.

Затем я посмотрел на StructureMap. StructureMap не поставляется со своим собственным встроенным в ControllerFactory, но его довольно просто сгенерировать. Я временно поместил его в свой файл Global.asax.cs во время тестирования. Вот окончательное содержимое файла:

public class MvcApplication : HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
        ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());

        // Configure structure map
        ObjectFactory.Initialize(x =>
        {
            x.For<IDataContext>()
                .HttpContextScoped()
                .Use<LinqToSqlDataContext>();

            x.For<DataContext>()
                .HttpContextScoped()
                .Use<MyDataContext>();
        });
    }

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

    public static void RegisterRoutes(RouteCollection routes)
    {
        ...
    }
}

public class StructureMapControllerFactory : DefaultControllerFactory 
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        try
        {
            var controllerType = base.GetControllerType(requestContext, controllerName);
            return ObjectFactory.GetInstance(controllerType) as IController;
        }
        catch (Exception e)
        {
            return base.CreateController(requestContext, controllerName);
        }
    }
}

Я также должен иметь возможность получить экземпляр в контексте данных в моем атрибуте аннотации данных, сказав:

var context = ObjectFactory.GetInstance<IDataContext>();

Мне определенно кажется, что в синтаксисе это выглядит намного легче. Однако, когда я запускаю свое приложение, оно не работает.

Мне было интересно следующее:

  1. Является ли мой шаблон правильным.
  2. Какой контейнер IoC является лучшим и простым в использовании. Если NInject, то я делаю это правильно, а если StructureMap, то как я могу исправить полученную ошибку.

Я знаю, что это много, но я был бы очень признателен, если бы кто-то мог помочь. Спасибо.

Ответы [ 3 ]

1 голос
/ 23 июля 2010

Вот хорошее видео о настройке ASP.NET MVC с SM из серии MVC Storefront.

В моем проекте я использую Autofac и храню всю мою логику контейнера в файле Bootsrapper.cs. Тогда в моем global.asax.cs это просто один вызов для настройки IoC и свойство для получения доступа к контейнеру

private static IContainerProvider _containerProvider;

public IContainerProvider ContainerProvider 
{ 
    get { return _containerProvider; } 
}

protected void Application_Start ()
{
  // snip..

  _containerProvider = Bootstrapper.ConfigureAutofac ();

  // snip..
}

Я не планирую уходить от LinqToSQL или Autofac, поэтому я не стал абстрагироваться ни от одного из этих компонентов.

0 голосов
/ 23 июля 2010

Мое первое сканирование говорит о том, что вы сравниваете, насколько легко расположение службы в SM против NI.Это неправильный вопрос - Ninject намеренно не раскрывает это каким-то образом - он пытается заставить вас упасть в Яму Успеха (и СМ тоже должен - люди не читают документы, пока, к сожалению, слишком поздно).Посмотрите на сервисное местоположение против внедрения зависимостей с помощью поисковой системы.

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

Лично я с нетерпением жду чтения и возможности рекомендовать (я не сомневаюсь, что это будет важная книга, которая будет чрезвычайнохорошо написано) Внедрение зависимостей в действии (его пока нет на бумаге).А пока я рекомендую прочитать все посты Ninject и все посты Марка Симанна здесь.

0 голосов
/ 23 июля 2010

Примите это с крошкой соли, но IoC может быть немного излишним, если вы только начинаете привыкать к MVC.Если бы я только начинал, я не думаю, что поначалу беспокоился бы об этом.Даже приложение NerdDinner, которое является стандартным учебным пособием для начинающих, не использует IoC.Черт возьми, он даже не использует классы View. Он просто обращается к контексту прямо в представлении.

Я не вижу ссылки на ошибку в вашем посте, но если бы это был я, я бы просто пропустил IoCа пока просто сконцентрируйся на изучении MVC.

...