简体   繁体   English

编写WPF应用程序时,我应该将哪个DbContext注册到Unity容器中?

[英]Which lifetime-manager do I register my DbContext into Unity container when writing a WPF application?

I am writing a new C# application on the top of Prism 6.3 framework using the well-known MVVM design pattern. 我正在使用众所周知的MVVM设计模式在Prism 6.3框架的顶部编写一个新的C#应用​​程序。 I am using Unity IoC container to help me manage my dependencies. 我正在使用Unity IoC容器来帮助我管理依赖项。

I am using Entity Framework Core to interact with the database. 我正在使用Entity Framework Core与数据库进行交互。 However, I don't want to tightly couple my application to Entity Framework Core, so I implemented Repository and UnitOfWork patterns to make it easy for me to swap out the Entity Framework Core implementation if needed. 但是,我不想将应用程序紧密耦合到Entity Framework Core,所以我实现了RepositoryUnitOfWork模式,以便在需要时可以方便地换出Entity Framework Core实现。

My repository implementation provides a method called Save() which calls EF Core's SaveChanges() method. 我的存储库实现提供了一个名为Save()的方法,该方法调用EF Core的SaveChanges()方法。 The repositories are injected into my business-service so that my business-service expose one method to do a single task. 信息库被注入到我的业务服务中,以便我的业务服务公开一种执行单个任务的方法。 For example, if I want to create a new order, I would call the Create(orderViewModel) method which internally calls the Add() and the Save() method on the OrderRepository . 例如,如果我想创建一个新的订单,我会打电话的Create(orderViewModel)在内部调用该方法Add()Save()的方法OrderRepository

Additionally, the UnitOfWork provides Save() , BeginTransaction() , Commit() and Rollback() methods which allow me control the transaction behavior. 另外, UnitOfWork还提供了Save()BeginTransaction()Commit()Rollback()方法,这些方法使我可以控制事务行为。 In another words it will give me the flexibility to either commit or rollback the SQL transaction when needed. 换句话说,它将为我提供在需要时提交或回滚SQL事务的灵活性。

To explain my use case better, here is an example of how I would add new order to my database directly using the business-service without transaction or unit-of-work. 为了更好地解释我的用例,这里有一个示例,说明如何直接使用业务服务将新订单添加到数据库中,而无需事务或工作单元。

OrdersService.Create(orderViewModel); // this will call the `Add` and the `Save()` methods on the OrderRepository;

Here is another example which demonstrate how I would add a new order and order-items to my database using the business-services while using unit-of-work to start transaction and control the transaction. 这是另一个示例,演示了如何在使用工作单元启动事务并控制事务的同时使用业务服务向数据库中添加新订单和订单项。

using(var transaction = UnitOfWork.BeginTransaction())
{
    try 
    {
        var order = OrdersService.Create(orderViewModel);
        OrdersService.CreateRange(order.Id, orderItemsViewModel);
        transaction.Commit();
    } 
    catch(Exception e)
    {
        Log.Add(e);
        transaction.RollBack();
    }
}

In the second example above, even-though the OrdersService.Save and OrdersService.SaveRange each call the SaveChanges() method the data are not committed to the database since I am wrapping them with a transaction. 在上面的第二个示例中,即使OrdersService.SaveOrdersService.SaveRange都调用SaveChanges()方法,但由于我将它们包装在事务中,因此数据未提交到数据库。

Question : what LifeTimeManager should I register the DbContext , IUnitOfWork and each of my repositories with? 问题 :我应该向哪个LifeTimeManager注册DbContextIUnitOfWork和每个存储库?

In a web environment, I would register everything using PerRequestLifetimeManager then during the request I am reusing the same DbContext and everything works fine and the DbContext is disposed at the end of the http request. 在Web环境中,我将使用PerRequestLifetimeManager注册所有PerRequestLifetimeManager然后在请求期间重用相同的DbContext并且一切正常,并且DbContext放置在http请求的末尾。 But not sure how to register everything in a WPF application where I can still use transaction to control everything while allowing the repository to call the SaveChanges() 但是不确定如何在WPF应用程序中注册所有内容,在这里我仍然可以使用事务来控制所有内容,同时允许存储库调用SaveChanges()

If needed here is my EntityRepository implementation 如果需要,这是我的EntityRepository实现

public class EntityRepository<TEntity, TKeyType> : IRepository<TEntity, TKeyType>
    where TEntity : class
    where TKeyType : struct
{
    protected readonly DbContext Context;
    protected readonly DbSet<TEntity> DbSet;

    public EntityRepository(DbContext context)
    {
        Context = context;
        DbSet = context.Set<TEntity>();
    }

    public TEntity Get(TKeyType id)
    {
        return DbSet.Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return DbSet.ToList();
    }

    public bool Any(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Any(predicate);
    }

    public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Where(predicate);
    }

    public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.SingleOrDefault(predicate);
    }

    public virtual TEntity Add(TEntity entity)
    {
        var record = Context.Add(entity);
        record.State = EntityState.Added;

        return entity;
    }

    public virtual IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
    {
        Context.AddRange(entities);

        return entities;
    }

    public void Remove(TEntity entity)
    {
        Context.Remove(entity).State = EntityState.Deleted;
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        Context.RemoveRange(entities);
    }

    public void Update(TEntity entity)
    {
        DbSet.Attach(entity);
        var record = Context.Entry(entity);
        record.State = EntityState.Modified;
    }

    public IQueryable<TEntity> Query()
    {
        return DbSet;
    }

    public void Save()
    {
        Context.SaveChanges();
    }
}

And here is my unit of work implementation 这是我的工作单元实施

public sealed class UnitOfWork : IUnitOfWork
{
    private bool IsDisposed = false;
    private readonly DbContext Context;

    public IOrderRepository Orders { get; private set; }
    public IOrderItemRepository OrderItems { get; private set; }

    public UnitOfWork(DbContext context)
    {
        Context = context;
        Orders = new OrderRepository(context);
        OrderItems = new OrderItemRepository(context);
    }

    public int Save()
    {
        Context.SaveChanges();

        return 0;
    }

    public void Dispose()
    {
        Dispose(true);
    }

    public IDatabaseTransaction BeginTransaction()
    {
        return new EntityDatabaseTransaction(Context);
    }

    private void Dispose(bool disposing)
    {
        if (IsDisposed)
        {
            return;
        }

        if (disposing)
        {
            Context.Dispose();
        }

        IsDisposed = true;
    }
}

Transient (an instance per view) lifetime would be the way to go if your DI doesn't support scoping, but then you would need to abstract away your DbContext being passed through into the repo's and unitOfWork, otherwise new instances of the DbContext will be passed in there. 如果您的DI不支持作用域,则将采用瞬态(每个视图一个实例)生存期,但是您将需要抽象化传递给仓库和unitOfWork的DbContext,否则将使用DbContext的新实例通过那里。 On construction of the page, a new instance is created, and on moving away from that view, that DBContext should be disposed of. 在构建页面时,将创建一个新实例,并在离开该视图时应丢弃该DBContext。 UnitOfWork would follow the same path as you wouldn't want a UnitOfWork spanning multiple instances of a DBContext. UnitOfWork遵循的路径与您不希望UnitOfWork跨越DBContext的多个实例的路径相同。 See http://blogs.microsoft.co.il/gilf/2010/02/07/entity-framework-context-lifetime-best-practices/ . 请参阅http://blogs.microsoft.co.il/gilf/2010/02/07/entity-framework-context-lifetime-best-practices/ Otherwise, if your DI has the concept of container hierarchies, and you're able to create a container scope per view, then a singleton would work in this instance and you wouldn't need any abstractions mentioned above and would be quite a bit easier to work with. 否则,如果您的DI具有容器层次结构的概念,并且能够为每个视图创建容器范围,则在这种情况下可以使用单例,并且您不需要上面提到的任何抽象,这样会容易得多跟...共事。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM