具有许多方法的存储库或具有外键的实体

时间:2013-02-13 15:01:28

标签: c# entity-framework entity-relationship

我有一个包含客户,供应商和服务的数据库(这是一个粗略的简化,我真的有大约100个表)

我正在开发一个新的Entity Framework库来访问这些表。

  

客户有很多供应商

     

供应商有许多服务

我正在尝试决定采用哪种方法 -

A) 使用映射将客户连接到供应商,将供应商连接到服务,然后每次加载客户时,我都会获得所有供应商及其服务(以及其他加载的表格)

B) 实体之间没有映射,但在相关存储库中提供方法;例如在供应商存储库中,我将IEnumerable<Supplier> GetSupplierByCustomerID(int customerID)

编辑 根据建议将上述内容更改为IEnumerable。

这些是使用EF时的两种主要方法吗?从您的角度来看,这被认为是更好的。

我还有其他方法吗?

4 个答案:

答案 0 :(得分:2)

我会亲自揭露许多简单的方法。

  

使用映射将客户连接到供应商和供应商   到服务,然后每次我加载客户我得到他所有   供应商及其服务(以及其他加载的表格)

如果您只需要从Name获取客户的ID,那么上述解决方案将要求您加载无用且重的对象图,除非您使用延迟加载,但是您可以有一些序列化过程(3层架构?),这对你来说是个问题,因为在这种情况下你不能使用延迟加载。

所以你可以揭露例如:

Supplier GetSupplierByID(int supplierID)
IEnumerable<Supplier> GetSuppliersByCustomerID(int customerID)
...

我还建议不要公开IQueryable。如果可能,请改用IEnumerable。有关使用IQueryable时所有影响尚不清楚的危险的详细信息,请参阅this article

答案 1 :(得分:1)

总的来说,我觉得将存储库放在EF上总是一个好主意。您可以从客户端逻辑(甚至业务逻辑)中抽象出数据库逻辑。你提到你的具体情况是能够做到另一个好处:你只能在你特别要求的时候得到你想要的信息(比如你提到的GetSupplierByCustomerID例子。

您可能会考虑的另一种方法是我在this question的答案中提到的方法:有界上下文。您在应用程序中关注的问题越多,从长远来看,您和您的程序员就会越好(特别是当您想对所有程序进行单元测试时)。

答案 2 :(得分:1)

这只是我的意见,我不知道你的情况是否合适,因为它取决于你的业务需求,但我通常更喜欢第三种选择。

  1. 所有存储库都返回IEnumerable,而不是IQueryable:这样可以在运行任何业务逻辑之前完成所有数据库操作。
  2. 所有存储库都使用可选参数公开方法,以便声明包含的导航属性:这使得能够使用所需的导航实体调用存储库方法。
  3. 创建一个基本通用存储库,并在每个存储库中从中继承。
  4. 实施工作单元模式以共享上下文并启用事务。
  5. 来自基础存储库的示例方法签名(T是实体的类型):

     IEnumerable<T> Find(Expression<Func<T, bool>> criteria, params Expression<Func<T, object>>[] navigationList)
    

答案 3 :(得分:1)

当它要映射或不映射时,我会选择A.导航属性有许多优点(如Customer.Supplier),并且有很多方法可以控制延迟/急切加载。

导航属性的优点是linq查询更容易编写。你几乎不用写连接:

加入:

from supp in db.Supliers
join serv in db.Services on supp.SupplierId equals serv.SupplierId
select ...

使用导航属性

from supp in db.Supliers
from serv in supp.Services
select ...

或类似的事情:

from supp in db.Supliers
select new { supp.Name, ServicesCount = supp.Services.Count() }

和EF将弄清楚如何在SQL中进行连接。

拥有导航属性并不意味着它们总是被加载。对于延迟加载,必须满足两个条件

  1. 该属性必须定义为virtual,以使EF能够在代理类型中覆盖它,并连接到延迟加载。
  2. 上下文必须启用延迟加载。它们是默认设置,但您可以通过设置context.Configuration.LazyLoadingEnabled = false
  3. 将其关闭

    因此,这也显示了两种控制延迟加载的方法:您可以在结构上或临时启用/禁用它。

    除此之外,您可以通过两种方式控制相反的,急切的加载:

    1. 使用Include声明:

      db.Suppliers.Include(s => s.Services)
      
    2. 在投影中包含导航属性:

      from supp in db.Supliers
      from serv in supp.Services
      select new { supp.Name, serv.ServiceName }
      
    3. (有更多方法,但这些是最重要的方式)

      这适用于在您的服务或存储库中编写linq查询。正如其他人所说:不要向服务/存储库方法的使用者公开IQueryable

      最后一个重要注意事项:延迟加载只能在生命环境的范围内进行。如果处理了上下文并且解决了延迟加载导航属性,则抛出异常。同时,建议使用寿命较短的上下文实例。所以存在两难:暴露实体对象或仅暴露DTO或视图模型或类似的东西。当您公开延迟加载的实体对象时,消费者可能会无意中解决尚未加载的导航属性,并且上下文消失了。