DAL 中的实体框架存储库模式,如何实现更新功能?

Posted

技术标签:

【中文标题】DAL 中的实体框架存储库模式,如何实现更新功能?【英文标题】:Entity Framework Repository Pattern in DAL, how to implement Update functions? 【发布时间】:2013-12-03 00:38:33 【问题描述】:

或我们的一个项目(C# Windows 服务,主要用于处理来自外部资源的数据并将这些数据读/写到 SQL Server 数据库)我们已经在 DAL 中实现了存储库模式,并且我们为每个 POCO 对象(代表一个表格行)。这些存储库类的使用由工作单元模式管理,因此所有存储库在单个工作单元内共享相同的 DbContext,但这可能与问题无关。

假设一个客户实体的存储库类实现如下:

public class CustomerRepository

    private readonly MyContext _context;
    public CustomerRepository(MyContext context)
    
        _context = context;
    

    public Customer GetCustomer(string id)
    
        return _context.Customers.Find(new object[] id);
    

    public IEnumerable<Customer> GetAllCustomers(Func<Customer,bool> predicate )
    
        return _context.Customers.Where(predicate);
    
    public IEnumerable<Customer> GetCustomerByCity(string city)
    
        return _context.Customers.Where(c=>s.City.Equals(city));
    


    public void SetAge(string id, byte age)
    
         _context.Customers.Find(new object[]  id ).Age = age;
    

    public void Update(Customer customer)
    
        _context.Entry(customer).State = EntityState.Modified;
    


现在我有几个关于更新方法的最佳实践的问题:

是否建议使用单独的方法来更新数据库中的特定字段,例如 SetAge() 方法? 或者是否建议将 Customer 对象获取到我们的 BLL(使用 GetCustomer(id)),然后在 BLL 中设置属性,如 customer.Age = 35;,然后保存此更改?

更新操作大多限于单行中的单个字段。

也许有人愿意和我分享一些现实世界的想法?

【问题讨论】:

如果您考虑对所有属性进行特定更新操作的实用性,那么答案是相当明显的。此外,考虑到您将多次访问数据库以更新同一个对象,这种方法可能不会很好地扩展。 @James 更新操作大多限于单行中的单个字段,因此这里有多个往返不是问题。就相关而言,实体框架会跟踪更改(更改保留在内存中),并且当在我们的上下文对象上调用 SaveChanges 时,记录将一次性提交到数据库。 对我来说很好,SetAge 是域逻辑,因此并不真正属于存储库层。如果你问做你正在做的事情是否有任何伤害,那么答案是否定的。但是,请考虑诸如编辑实体的影响之类的事情,即删除/重命名属性或添加新属性。您实际上所做的只是给自己不必要的额外工作,EF 足够聪明,只会更新已更改的内容。 顺便说一句,我预测您的存档有问题。引用msdn.microsoft.com/en-us/library/…:“DbContext 实例代表工作单元和存储库模式的组合”。 IDbSets 是存储库,SaveChanges 应该是工作单元的责任。我建议你将该方法移到工作单元类中 @Colin 你说的很对。 save 方法实际上是我的工作单元的一部分。从我的项目中复制粘贴时必须在这个例子中偷偷摸摸...谢谢 【参考方案1】:

有些人认为 EF 是工作单元和存储库,您应该让它为您完成工作。我正在慢慢适应这种思维方式。 EF 真的很擅长管理这一切,重写已经存在的工具是没有意义的。

想一想……

【讨论】:

哦,不,我认为我在这个实现方面走在了正确的轨道上,但这可能会改变我的思维方式。你能告诉我为什么有些教程建议这种方法(比如asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/…)吗?我对自己实现什么以及在我的应用程序的哪一层感到有点困惑...... 好的,那篇文章使用 UOW 模式来封装上下文和存储库,这样您就可以在不访问数据库的情况下进行单元测试。同样,这只是我的个人观点,但我不同意这种哲学。更改数据库架构是一件非常频繁的事情,如果您没有使用实际的测试 Sql Server(或您正在使用的任何数据库),您怎么可能知道您的数据库更改是否以完全不同的方式破坏了某些东西方法比您正在测试的方法? IMO,您需要在单元测试时访问数据库。其他人会不同意我的观点。 至于哪些层做什么,这就是我所做的......我创建了一个 MVC 应用程序作为顶层,然后在其下创建一个业务逻辑,然后在其下创建一个 EntityModel。我做了一点 T4 技巧,实际上创建了第 4 个项目,所有项目都可以访问其中包含 POCO。 BusinessLogic 真正负责对数据库的所有访问。您的 MVC 层将调用业务逻辑层,它永远不会返回 IQueryable。此时应始终为 ICollection。如果您需要更多帮助,请通过 gmail dot com 的 scottieslg 给我发电子邮件,我很乐意提供帮助。 感谢您与我分享您的详细意见,这与我想到的方法非常接近(除了在我的情况下没有表示层(它是一项服务),但我明白了。一个事情对我来说听起来很奇怪,您是在说“BusinessLogic 确实负责对数据库的所有访问”。您的方法中是否没有 DAL,或者您是否将您的 EF DbContext / DbSets 视为您的 DAL?对这一点非常好奇。 当我重新阅读我的评论时,应该说 BusinessLogic 负责访问 EF,而不是 DB。 EF 访问数据库。但是,没有专门用于数据访问的项目。我的意思是,如果你仔细想想,你的 BLL 实际上并没有进行数据访问。它只是在执行 LINQ 并让 LINQ to Sq/EF 进行数据访问。 EF 处理所有事务方面、联接、更改、添加等。您的 DAL 究竟会做什么?公开一个调用 context.Save() 的 Save 方法?何必?让英孚做它的工作。它可以比你做得更好。【参考方案2】:

我倾向于同意 Daryal 的观点。但会强调你不要安静地拥有正确的类存储库模型。理想情况下,您希望存储库是通用的。对 Full 对象进行操作并提供 DAL 服务。

 public class DAL.Repository<poco> 

    //methods for CRUD, get List, search whatever you see as right
    // eg ...add error handling....
    void add(poco p)
       context.attach(p);
    ... 
 


//  The domain/core model layer would have much of the object based business logic
 public class CORE.PocoXYZ 
    //all the get sets and methods required to manipulate the object
 


// And your Unit of Work Class
Public class DAL.UoW
    context.savechanges();



So i would suggest you perform object level Data storage in the DAL. But do all teh object manipulation in the core.  The DAL would control the save in a UoW class that managed all Repositories.

服务或 UI 层可能会将 UoW/Repository 层注入核心层。

【讨论】:

【参考方案3】: 是否建议使用单独的方法来更新数据库中的特定字段,例如 SetAge() 方法?

我认为对每个属性进行单独操作并不明智。您最终会得到封装属性的方法。此外,与其为每种类型实现方法,为什么不创建一个基础存储库来实现常见操作?您可以在基类中实现 GetAll 而不是 GetAllCustomers,并从该基存储库派生所有存储库。

或者是否建议将 Customer 对象获取到我们的 BLL(使用 GetCustomer(id)),然后在 BLL 中设置属性,如 customer.Age = 35;,然后保存这些更改?

我认为问题不在于您更改属性的位置,问题在于anemic domain model。您有一个单独的业务层,它使用存储库方法修改对象。这违反了面向对象的方法,即对象应具有属性和方法。

【讨论】:

实体框架模型是 DTO,而不是域对象。包含业务逻辑的域对象将是一个 Customer,它检查所设置的年龄是否至少为 0 @CodeCaster 它不会阻止您从生成的代码中获得域对象;我也没有在问题中看到任何表明 EF 的内容。 这是 DAL,而不是 BL。我们对OP的BL一无所知。对于 EF,请参阅问题的标签。 @daryal 我知道基类建议,但为了简单起见,将其排除在示例之外。我会阅读您指向 Fowlers 关于贫血域模型的帖子的链接。 我觉得业务层不相关,只是一个服务处理从TCP socket传入的数据,这个数据需要通过DAL到达数据库。

以上是关于DAL 中的实体框架存储库模式,如何实现更新功能?的主要内容,如果未能解决你的问题,请参考以下文章

DAL 存储库模式使用 dapper 连接

无法使用实体框架更新记录并使用存储库模式进行 ninject

具有存储库模式的实体框架,将数据插入到具有多对多关系的表中

DAL:存储库边界问题

使用带有实体框架 6 的存储库模式更新记录

多对多实体框架和存储库模式插入/更新