2010-04-28 143 views
0

我遇到Autofac2和MVC2的问题。问题是我试图解决一系列依赖关系是HttpRequestScoped的依赖关系。当我尝试解决我的UnitOfWork(这是一次性)时,Autofac失败,因为内部垃圾回收器试图将UnitOfWork对象添加到内部处置列表中,该列表为空。也许我在错误的生命周期中注册了我的依赖关系,但我尝试了许多不同的组合,但没有运气。我唯一的要求是MyDataContext持续整个HttpRequest。Autofac 2和MVC2使用HttpRequestScoped问题

我已经发布了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(); 
    } 
} 

回答

1

在HomeController中您要处理的的UnitOfWork这autofac会为你做。但是,如果您希望控制对象何时处置,则需要将ExternallyOwned添加到您的IUnitOfWork注册中。

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

这是它!谢谢! – 2010-04-29 01:16:36

2

您需要初始化与ContainerProvider,而不是RequestLifetime你AutofacDependencyResolver(只要它只是生活作为当前的请求 - 一个新创建的每个时间)

希望他的帮助,

尼克

+0

感谢您的帮助Nick。对于我的Resolve方法,我是否仍然在AutofacDependencyResolver中使用RequestLifeTime.Resolve,还是应该使用ApplicationContainer.Resolve?我认为它仍然是RequestLifeTime.Resolve。 – 2010-04-29 12:08:32

+0

是的,RequestLifetime是唯一的。 – 2010-04-30 06:22:08

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 is(imo:genius!)above above using ExternallyOwned由于处置外部拥有的实例不会清除注入该实例的其他依赖关系。处置一个Owned实例也会自动处理该实例的整个对象图。

对此的一些介绍可以找到here

注:甚至更​​好,现在UnitOfWork是从容器自由,你可以扔掉了​​事情完全:)

+1

感谢您的详细解答彼得!这太棒了!我真的不想使用IoCHelper(服务定位器)。 – 2010-04-29 12:29:04

+0

真棒加法,好思维! :) – 2010-04-30 06:22:55