Проблема с Autofac 2 и MVC2 с использованием HttpRequestScoped - PullRequest
0 голосов
/ 28 апреля 2010

У меня проблема с Autofac2 и MVC2. Проблема в том, что я пытаюсь разрешить ряд зависимостей, где корневая зависимость - HttpRequestScoped. Когда я пытаюсь разрешить мой UnitOfWork (который является Одноразовым), Autofac завершается ошибкой, потому что внутренний распределитель пытается добавить объект UnitOfWork во внутренний список утилизации, который является нулевым. Может быть, я регистрирую свои зависимости с неправильными временами жизни, но я пробовал много разных комбинаций без удачи. Единственное требование, которое у меня есть, заключается в том, что MyDataContext действует в течение всего запроса HttpRequest.

Я разместил демо-версию кода для , скачайте здесь .

Модули Autofac настроены в web.config

Global.asax.cs

protected void Application_Start()
{
    string connectionString = "something";

    var builder = new ContainerBuilder();

    builder.Register(c => new MyDataContext(connectionString)).As<IDatabase>().HttpRequestScoped();
    builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerDependency();
    builder.RegisterType<MyService>().As<IMyService>().InstancePerDependency();

    builder.RegisterControllers(Assembly.GetExecutingAssembly());

    _containerProvider = new ContainerProvider(builder.Build());
    IoCHelper.InitializeWith(new AutofacDependencyResolver(_containerProvider.RequestLifetime));

    ControllerBuilder.Current.SetControllerFactory(new AutofacControllerFactory(ContainerProvider));

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

AutofacDependencyResolver.cs

public class AutofacDependencyResolver
{
    private readonly ILifetimeScope _scope;

    public AutofacDependencyResolver(ILifetimeScope scope)
    {
        _scope = scope;
    }

    public T Resolve<T>()
    {
        return _scope.Resolve<T>();
    }
}

IoCHelper.cs

public static class IoCHelper
{
    private static AutofacDependencyResolver _resolver;

    public static void InitializeWith(AutofacDependencyResolver resolver)
    {
        _resolver = resolver;
    }

    public static T Resolve<T>()
    {
        return _resolver.Resolve<T>();
    }
}

UnitOfWork.cs

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly IDatabase _database;

    public UnitOfWork(IDatabase database)
    {
        _database = database;
    }

    public static IUnitOfWork Begin()
    {
        return IoCHelper.Resolve<IUnitOfWork>();
    }

    public void Commit()
    {
        System.Diagnostics.Debug.WriteLine("Commiting");
        _database.SubmitChanges();
    }

    public void Dispose()
    {
        System.Diagnostics.Debug.WriteLine("Disposing");
    } 
}

MyDataContext.cs

public interface IDatabase
{
    void SubmitChanges();
}

public class MyDataContext : IDatabase
{
    private readonly string _connectionString;

    public MyDataContext(string connectionString)
    {
        _connectionString = connectionString;
    }

    public void SubmitChanges()
    {
        System.Diagnostics.Debug.WriteLine("Submiting Changes");
    }
}

MyService.cs

public interface IMyService
{
    void Add();
}

public class MyService : IMyService
{
    private readonly IDatabase _database;

    public MyService(IDatabase database)
    {
        _database = database;
    }

   public void Add()
   {
       // Use _database.
   }
}

HomeController.cs

public class HomeController : Controller
{
    private readonly IMyService _myService;

    public HomeController(IMyService myService)
    {
        _myService = myService;
    }

    public ActionResult Index()
    {
        // NullReferenceException is thrown when trying to
        // resolve UnitOfWork here.
        // Doesn't always happen on the first attempt.
        using(var unitOfWork = UnitOfWork.Begin())
        {
            _myService.Add();

            unitOfWork.Commit();
        }

        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

Ответы [ 3 ]

3 голосов
/ 29 апреля 2010

Прежде всего, вы не должны позволять UnitOfWork зависеть от контейнера. Удалите метод BeginWork и рассмотрите это изменение для HomeController:

public class HomeController : Controller
{
    private readonly IMyService _myService;
    private readonly Func<Owned<IUnitOfWork>> _unitOfWorkFactory;

    public HomeController(IMyService myService, Func<Owned<IUnitOfWork>> unitOfWorkFactory)
    {
        _myService = myService;
        _unitOfWorkFactory = unitOfWorkFactory;
    }

    public ActionResult Index()
    {
        using(var unitOfWork = _unitOfWorkFactory())
        {
            _myService.Add();

            unitOfWork.Value.Commit();
        }

        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

Фабричные делегаты Func<> автоматически доступны в Autofac2 и предоставят вам делегата, который создает экземпляры указанного типа. Далее, поскольку мы просим Owned<IUnitOfWork>, мы получаем экземпляр, который

Под капотом есть выделил свое вложенное время жизни сфера, поэтому все его зависимости будут быть убранным, когда оно есть.

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

Owned предпочтительнее (imo: genius!) Выше при использовании ExternallyOwned, поскольку удаление экземпляра, находящегося во владении, не очистит другие зависимости, введенные в этот экземпляр. Удаление экземпляра Owned также автоматически удалит весь граф объектов для этого экземпляра.

Некоторое введение в это можно найти здесь .

Примечание: еще лучше, теперь, когда UnitOfWork свободен от контейнера, вы можете выбросить IoCHelper вещь в целом:)

2 голосов
/ 29 апреля 2010

Вам нужно инициализировать AutofacDependencyResolver с помощью ContainerProvider, а не RequestLifetime (который живет только столько времени, сколько текущий запрос - каждый раз создается новый.)

Надеюсь, что он помогает,

Ник

1 голос
/ 28 апреля 2010

В HomeController вы избавляетесь от UnitOfWork, который вам подойдет. Однако, если вы хотите контролировать, когда объект будет удален, вам нужно будет добавить ExternallyOwned к вашей регистрации для IUnitOfWork.

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerDependency().ExternallyOwned();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...