【问题标题】:How to keep Unit of Work abstraction from leaking如何防止工作单元抽象泄漏
【发布时间】:2014-03-02 17:03:01
【问题描述】:

我正在学习良好的设计实践,并发现了工作单元设计模式以及存储库。这让我可以让应用程序逻辑不知道持久性细节,但是我在处理一些极端情况时遇到了一些困难。

首先,在 IUnitOfWork 接口后面,我使用了一个使用 EF 访问我的数据的实现。对于我的查询,我使用规范设计模式来创建客户端可以使用的查询对象。

所以,到目前为止,这个东西几乎从数据源中抽象出来了,但这里是交易: 想象一下我想用 AsNoTracking 选项检索一些数据。我应该如何告诉我的 IUnitOfWork 实现我不需要跟踪结果对象?

此外,EF 并不真正支持批量 CUD,也不支持未来查询,但有一个扩展支持。我如何以客户端代码与我持久保存对象的方式完全不可知的方式支持这一点?我可能想使用平面文件,因为所有代码都在乎。

TL;DR; 如何防止我的 UnitOfWork 抽象泄漏到我的客户中?有人处理过吗?

【问题讨论】:

    标签: c# entity-framework data-access-layer


    【解决方案1】:

    其中大部分将通过注入一个存储库来完成,该存储库的实现会提供那些未跟踪的对象或对平面文件进行读/写操作。

    概念存储库所做的很多事情都与 UoW 交叉。回购实现最终有很多这样的:

    public class MyRepository : IWhateverRepository
    {
        // ...
        public Customer GetCustomerByName(string customerName)
        {
            using (var db = new MyContext())
            {
                var customer = db.Customers.Where(cust => cust.Name == customerName).SingleOrDefault();
                return customer;
            }
        }
    
        public Customer StoreCustomer(Customer customer)
        {
            using (var db = new MyContext())
            {
                db.Entry(customer).State = customer.ID == 0 ? EntityState.Added : EntityState.Modified;
                db.SaveChanges();
            }
        }
    }
    

    如您所见,UoW 的使用并不多。我认为使用存储库时 EF 的大部分用处在于不必手动编写 SQL。

    但是,当没有存储库抽象并且业务逻辑直接与实体和上下文一起工作时,UoW 运行良好。例如,在 UI MVC 服务层场景中,您可能让 MVC 控制器指示服务层创建新发票:

    IMyService svc = new MyServiceLayer();
    IInvoice invoice = svc.CreateInvoiceForCustomer(customer, items, paymentInfo);
    InvoiceViewModel invoiceVm = Map(invoice);
    return RedirectToAction("Display", "Invoice", invoiceVm);
    

    您可以想象服务层可能会执行以下操作:

    public ServiceResponse<Invoice> CreateInvoiceForCustomer(Customer customer, IEnumerable<InvoiceItem> items, PaymentInfo paymentInfo)
    {
        using (var db = new MyContext())
        {
            db.Entry(customer).State = customer.ID == 0 : EntityState.Added : EntityState.Modified;
    
            var invoice = new Invoice
                {
                    Customer = customer,
                    InvoiceDate = _timeService.Now(),
                    PaymentInfo = paymentInfo
                };
            foreach (var item in items)
                invoice.Items.Add(items);
    
            db.Invoices.Add(invoice);
            db.SaveChanges();
    
            return new SeviceResponse<Invoice> 
                {
                    IsSuccess = true,
                    Data = invoice
                };
        }
    
    }
    

    在此示例中,UoW 模式充当事务的替代品。您无需弄脏手即可获得原子性。

    UoW 的真正强大之处在于有状态系统,例如 WPF,您可以在其中拥有一个长期存在的上下文,用于处理在不同线程上异步执行的大型命令队列。

    【讨论】:

    • 在我的情况下,我使用的是 Winforms,我需要访问多个存储库并保持它们之间的数据同步,因此我可以查询多个存储库并同步我的 Presenter 上的保存。您所说的是我可以在存储库中完成所有这些“NoTracking”部分,我认为这是正确的。虽然这并没有给我一个直接的方法来完成问题的“批处理”部分,但我想我可以从那个存储库中返回一个带有值(甚至是一个元组)的 DTO。只是想知道世界其他地方是如何处理它的。
    • 如果您想让存储库参与 UnitOfWork,那么您必须在该抽象级别使用您自己的 UoW 实现。另一种方法是将存储库排除在外(或者您是否在进行领域驱动设计?)。如果您不需要将实体存储在平面文件中,那么没有太多理由在您的设计中考虑到它。其实这是编码的另一个“规则”——Y.A.G.N.I. - 你不需要它(所以不要写它)。
    • 我确实有一个工作单元的抽象。现在唯一的实现是对 EF 隐藏一个 DBContext 对象。无论如何,我想你是对的。我不需要它。我只需要防止持久层的细节泄漏到其他层。无论如何,这是一个学习 TDD 概念、与 MVP、EF 等正确分层的宠物项目。谢谢你的帮助 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-15
    • 2020-10-20
    • 2011-09-30
    • 2010-12-20
    • 2022-11-09
    相关资源
    最近更新 更多