Unity框架——创建与创建在适当的时间处理实体框架数据上下文

发布于 2024-08-24 19:05:08 字数 2126 浏览 4 评论 0原文

在 StackOverflow 的帮助下,我使用 Unity Framework 创建了链式依赖项,包括实体框架数据上下文对象:

using (IUnityContainer container = new UnityContainer())
{
    container.RegisterType<IMeterView, Meter>();
    container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>(new ContainerControlledLifetimeManager());
    container.RegisterType<IRepositoryFactory, SQLiteRepositoryFactory>();
    container.RegisterType<IRepositoryFactory, WCFRepositoryFactory>("Uploader");
    container.Configure<InjectedMembers>()
        .ConfigureInjectionFor<CommunergySQLiteEntities>(
            new InjectionConstructor(connectionString));

    MeterPresenter meterPresenter = container.Resolve<MeterPresenter>();

这在创建 Presenter 对象和显示相关视图方面效果非常好,我真的很高兴。

但是,我现在遇到的问题是实体框架对象的创建和处置的时间安排(我怀疑这适用于任何 IDisposable 对象)。像这样使用Unity,可以立即创建SQL EF对象“CommunergySQLiteEntities”,因为我已将其接口IUnitOfWork添加到MeterPresenter的构造函数中,

    public MeterPresenter(IMeterView view, IUnitOfWork unitOfWork, IRepositoryFactory cacheRepository)
    {
        this.mView = view;
        this.unitOfWork = unitOfWork;
        this.cacheRepository = cacheRepository;
        this.Initialize();
    }

当时我对此感到有点不安,因为我不想保持打开数据库连接,但我看不到使用 Unity 依赖项注入的任何其他方法。果然,当我实际尝试使用 datacontext 时,我收到此错误:

    ((System.Data.Objects.ObjectContext)(unitOfWork)).Connection 
  '((System.Data.Objects.ObjectContext)(unitOfWork)).Connection'
threw an exception of type 'System.ObjectDisposedException'
 System.Data.Common.DbConnection {System.ObjectDisposedException}

我对 IoC 原理的理解是,您在顶部设置所有依赖项,解析您的对象,然后就可以走了。但是,在这种情况下,某些子对象(例如数据上下文)不需要在创建父 Presenter 对象时进行初始化(就像您将它们传递到构造函数中一样),但 Presenter 确实需要了解当 IUnitOfWork 想要与数据库对话时使用什么类型。

理想情况下,我希望在已解析的 Presenter 中实现类似的内容:

using(IUnitOfWork unitOfWork = new NewInstanceInjectedUnitOfWorkType())
{
    //do unitOfWork stuff
}

这样 Presenter 就知道要使用什么 IUnitOfWork 实现来立即创建和处置,最好是从原始的 RegisterType 调用中获得。我是否必须在 Presenter 中放置另一个 Unity 容器,从而冒着创建新依赖项的风险?

对于 IoC 专家来说,这可能是显而易见的,但我真的很感谢您指出正确的方向。

With some kindly help from StackOverflow, I've got Unity Framework to create my chained dependencies, including an Entity Framework datacontext object:

using (IUnityContainer container = new UnityContainer())
{
    container.RegisterType<IMeterView, Meter>();
    container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>(new ContainerControlledLifetimeManager());
    container.RegisterType<IRepositoryFactory, SQLiteRepositoryFactory>();
    container.RegisterType<IRepositoryFactory, WCFRepositoryFactory>("Uploader");
    container.Configure<InjectedMembers>()
        .ConfigureInjectionFor<CommunergySQLiteEntities>(
            new InjectionConstructor(connectionString));

    MeterPresenter meterPresenter = container.Resolve<MeterPresenter>();

this works really well in creating my Presenter object and displaying the related view, I'm really pleased.

However, the problem I'm running into now is over the timing of the creation and disposal of the Entity Framework object (and I suspect this will go for any IDisposable object). Using Unity like this, the SQL EF object "CommunergySQLiteEntities" is created straight away, as I've added its interface, IUnitOfWork to the constructor of the MeterPresenter

    public MeterPresenter(IMeterView view, IUnitOfWork unitOfWork, IRepositoryFactory cacheRepository)
    {
        this.mView = view;
        this.unitOfWork = unitOfWork;
        this.cacheRepository = cacheRepository;
        this.Initialize();
    }

I felt a bit uneasy about this at the time, as I don't want to be holding open a database connection, but I couldn't see any other way using the Unity dependency injection. Sure enough, when I actually try to use the datacontext, I get this error:

    ((System.Data.Objects.ObjectContext)(unitOfWork)).Connection 
  '((System.Data.Objects.ObjectContext)(unitOfWork)).Connection'
threw an exception of type 'System.ObjectDisposedException'
 System.Data.Common.DbConnection {System.ObjectDisposedException}

My understanding of the principle of IoC is that you set up all your dependencies at the top, resolve your object and away you go. However, in this case, some of the child objects, eg the datacontext, don't need to be initialised at the time the parent Presenter object is created (as you would by passing them in the constructor), but the Presenter does need to know about what type to use for IUnitOfWork when it wants to talk to the database.

Ideally, I want something like this inside my resolved Presenter:

using(IUnitOfWork unitOfWork = new NewInstanceInjectedUnitOfWorkType())
{
    //do unitOfWork stuff
}

So the Presenter knows what IUnitOfWork implementation to use to create and dispose of straight away, preferably from the original RegisterType call. Do I have to put another Unity container inside my Presenter, at the risk of creating a new dependency?

This is probably really obvious to a IoC guru, but I'd really appreciate a pointer in the right direction.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

虫児飞 2024-08-31 19:05:08

您无需担心在创建演示者的同时初始化 ObjectContext - ObjectContext 不会保持打开的数据库连接。相反,当它实际需要与数据库通信时,即当您执行查询或提交更改时,它会根据需要打开连接,并在完成后立即关闭连接。仅当您显式打开连接时,它才会保持连接打开,这在实体框架中很少见。

如果您使用 ContainerControlledLifetimeManager 收到 ObjectDisposeException 异常,则意味着您的容器在演示者之前被释放,这是一个设计错误。目前尚不完全清楚您的环境是什么(ASP.NET?Winforms?),但是 ContainerControlledLifetimeManager 可能不适合这里,因为它作为 Singleton 实例工作。通常,当您解析类型时,您实际上会想要创建一个新的上下文实例 - 如果您使用单例,则会遇到许多问题。

所以 - 我会在这里摆脱 ContainerControlledLifetimeManager ,并确保您的容器不会过早被处置;事实上,它位于 using 块中,这表明这可能是导致 ObjectDisposeException 的原因。 (当然,您最终仍然需要处置容器 - 只是您可能正在做一些类似创建无模式表单之类的事情,该表单在控制离开 using 范围后仍保持活动状态很长时间)。

You don't need to worry about initializing an ObjectContext at the same time that the presenter is created - an ObjectContext does not hold open a database connection. Rather, it opens connections as needed, when it actually needs to talk to the database, i.e. when you execute a query or commit changes, and closes the connection again as soon as it is finished. It will only hold the connection open if you explicitly open it, which is a rare thing to be doing with Entity Framework.

If you're getting an ObjectDisposedException using the ContainerControlledLifetimeManager then it means your container is getting disposed before the presenter, which is a design error. It's not entirely clear what your environment is (ASP.NET? Winforms?), but the ContainerControlledLifetimeManager is probably not appropriate here, as it works as a Singleton instance. Normally you will actually want to create a new context instance when you resolve the type - there are many problems you can and will run into if you use a singleton instead.

So - I would get rid of the ContainerControlledLifetimeManager here, and also make sure that your container is not getting disposed too early; the fact that it's in a using block indicates that this is likely the cause of your ObjectDisposedException. (You still need to dispose the container eventually of course - it's just that you're probably doing something like creating a modeless form, which remains alive long after control leaves the using scope).

混浊又暗下来 2024-08-31 19:05:08

为什么不直接从构造函数中删除 IUnitOfWork 而是注入 Unity 容器?因此,您可以在适当的时候灵活地在代码中的任何位置调用 container.Resolve()

示例:

using(IUnitOfWork unitOfWork = container.Resolve<IUnitOfWork>()) 
{ 
    //do unitOfWork stuff 
} 

不要忘记将实例的生命周期设置为单一。

迈克尔

Why don't you just remove the IUnitOfWork from the constructor and instead inject the unity container? So you will have the flexiblity to call container.Resolve<IUnitOfWork>() anywhere in your code when appropriate.

Example:

using(IUnitOfWork unitOfWork = container.Resolve<IUnitOfWork>()) 
{ 
    //do unitOfWork stuff 
} 

Don't forget to set the lifetime of the instance to single.

Michael

却一份温柔 2024-08-31 19:05:08

我知道这个问题已经很老了,但我仍然想在这里给出我的 2 美分。

您可以正常注册 UnitOfWork 抽象,但在类中请求 Func 而不是实例。实际上,这是 Unity 的一个巧妙功能,能够解析创建对象的委托,而不是立即获取对象。

这样,您就可以按照您的意愿进行操作,即控制方法内工作单元的范围。

简而言之:

container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>();

...

public MeterPresenter(IMeterView view, Func<IUnitOfWork> unitOfWorkFactory, IRepositoryFactory cacheRepository)
{
    this.mView = view;
    this.unitOfWorkFactory = unitOfWorkFactory;
    this.cacheRepository = cacheRepository;
    this.Initialize();
}

...

using(IUnitOfWork unitOfWork = unitOfWorkFactory())
{
    //do unitOfWork stuff
}

我已经使用过它几次,并且我个人推荐它,因为您仍然可以完全控制一切,包括单元测试的模拟,同时仍然不将代码与依赖项之外的任何内容耦合。

如果您需要更复杂的逻辑,您可以创建一个 IUnitOfWorkFactory 接口并注入它,但在大多数情况下,Func委托就足够了。

I know this question is old, but I would still like to give my 2 cents here.

You can register your UnitOfWork abstraction normally, but request a Func<IUnitOfWork> in your class instead of an instance. This is a neat feature of Unity actually, being able to resolve delegates that create your object instead of getting the object right away.

This way, you can do as you wanted to, i.e. control the scope of the unit of work inside your method.

In short:

container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>();

...

public MeterPresenter(IMeterView view, Func<IUnitOfWork> unitOfWorkFactory, IRepositoryFactory cacheRepository)
{
    this.mView = view;
    this.unitOfWorkFactory = unitOfWorkFactory;
    this.cacheRepository = cacheRepository;
    this.Initialize();
}

...

using(IUnitOfWork unitOfWork = unitOfWorkFactory())
{
    //do unitOfWork stuff
}

I've used this a few times already and I personally recommend it, since you still have full control over everything, including mocking for unit testing, while still not coupling your code to anything but it's dependencies.

You could create a IUnitOfWorkFactory interface and inject that instead if you needed more complex logic, but the Func<T> delegate suffices in most cases.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文