Autofac 2和MVC2使用HttpRequestScoped的问题

时间:2010-04-28 03:19:53

标签: asp.net-mvc ioc-container autofac

我遇到了Autofac2和MVC2的问题。问题是我正在尝试解决一系列依赖项,其中根依赖项是HttpRequestScoped。当我尝试解析我的UnitOfWork(可以是Disposable)时,Autofac会失败,因为内部处理程序正在尝试将UnitOfWork对象添加到null的内部处置列表中。也许我正在以错误的生命周期注册我的依赖项,但我尝试了许多不同的组合而没有运气。我唯一的要求是MyDataContext持续整个HttpRequest。

我发布了download here代码的演示版。

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 个答案:

答案 0 :(得分:3)

首先,你不应该让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实例也将自动处理该实例的整个对象图。

可以找到here的一些介绍。

注意:甚至更好,现在UnitOfWork从容器中解放出来,你可以完全抛弃IoCHelper的东西:)

答案 1 :(得分:2)

您需要使用ContainerProvider初始化AutofacDependencyResolver,而不是RequestLifetime(只有当前请求才会生效 - 每次都会创建一个新请求。)

希望他的帮助,

尼克

答案 2 :(得分:1)

在HomeController中,您正在处理autofac将为您执行的UnitOfWork。但是,如果您希望控制何时处理对象,则需要在注册IUnitOfWork时添加ExternallyOwned。

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerDependency().ExternallyOwned();