0

Autofac2とMVC2で問題が発生しています。問題は、ルート依存関係がHttpRequestScopedである一連の依存関係を解決しようとしていることです。UnitOfWork(Disposable)を解決しようとすると、内部ディスポーザがUnitOfWorkオブジェクトをnullの内部廃棄リストに追加しようとしているため、Autofacが失敗します。依存関係を間違ったライフタイムで登録しているのかもしれませんが、運が悪かったので、さまざまな組み合わせを試しました。私が持っている唯一の要件は、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();
    }
}
4

3 に答える 3

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ユーザーがインスタンスを破棄する責任があることを示します。

Owned(imo:genius!)はExternallyOwned、外部所有のインスタンスを破棄しても、そのインスタンスに挿入された他の依存関係がクリーンアップされないため、上記で使用することをお勧めします。インスタンスを破棄するOwnedと、そのインスタンスのオブジェクトグラフ全体も自動的に破棄されます。

これについてのいくつかの紹介はここにあります。

注:さらに良いことにUnitOfWork、コンテナから解放されたので、物を完全に捨てることができIoCHelperます:)

于 2010-04-29T08:06:28.533 に答える
2

AutofacDependencyResolverをContainerProviderで初期化する必要があります。RequestLifetimeではありません(現在のリクエストの間だけ存続します。毎回新しいリクエストが作成されます)。

彼の助けを願って、

ニック

于 2010-04-29T08:04:54.073 に答える
1

HomeControllerで、autofacが実行するUnitOfWorkを破棄します。ただし、オブジェクトをいつ破棄するかを制御する場合は、IUnitOfWorkの登録にExternallyOwnedを追加する必要があります。

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerDependency().ExternallyOwned();
于 2010-04-28T17:13:22.740 に答える