0

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

我正在开发一个新的实体框架库来访问这些表。

一个客户有很多供应商

供应商有许多服务

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

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

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

编辑 根据建议在上面更改为 IEnumerable。

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

还有另一种我不考虑的方法吗?

4

4 回答 4

2

我会亲自公开许多简单的方法。

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

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

因此,您可以公开例如:

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

我也建议不要暴露IQueryable。如果可能,请IEnumerable改用。有关在所有含义都不为人知时使用的危险的更多详细信息,请参阅本文。IQueryable

于 2013-02-13T15:11:28.943 回答
1

一般来说,我觉得在 EF 上放置一个存储库总是一个好主意。您可以从客户端逻辑(甚至业务逻辑)中抽象出数据库逻辑。而且您提到的特定情况还可以带来另一个好处:只有在您特别需要时才能获得所需的信息(GetSupplierByCustomerID例如您提到的示例。

您可能会考虑的另一种方法是我在回答这个问题时提到的方法:Bounded Contexts。您在应用程序中的关注点分离得越多,从长远来看,它对您和您的其他程序员(尤其是当您想要对它进行单元测试时)会更好。

于 2013-02-13T15:08:30.490 回答
1

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

  1. 所有存储库都返回IEnumerables,而不是IQueryables:这使得所有数据库操作都可以在运行任何业务逻辑之前完成。
  2. 所有存储库都公开了带有可选参数的方法,可以声明包含的导航属性:这可以调用具有所需导航实体的存储库方法。
  3. 创建一个基本通用存储库并在您的每个存储库中继承它。
  4. 实施工作单元模式以共享上下文并启用事务。

来自基本存储库的示例方法签名(T是实体的类型):

 IEnumerable<T> Find(Expression<Func<T, bool>> criteria, params Expression<Func<T, object>>[] navigationList)
于 2013-02-13T15:29:30.227 回答
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

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

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

  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 }
    

(还有更多方法,但这些是最重要的)

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

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

于 2013-02-13T16:04:16.900 回答