2013-02-13 39 views
0

我有一个客户,供应商数据库和服务(这是一个粗略的简化,我真的有大约100表)库有许多方法或实体使用外键

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

一个客户有很多供应商

供应商有很多服务

我试图决定采用哪一种方法 -

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

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

编辑根据建议更改为IEnumerable。

这些是使用EF的两种主要方法吗?从你的角度来看哪个更好。

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

+0

使用Linq和存储过程来得到你需要的东西 – CR41G14 2013-02-13 15:03:03

+0

哪种架构?我的意思是,一旦从数据库中检索到实体就被序列化了吗? – ken2k 2013-02-13 15:04:53

+1

@ CR41G14我想避免存储过程,有许多具有相同表格布局的数据库。因此,从软件调用更适合于我的域 – tom 2013-02-13 15:04:57

回答

1

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

另一种方法你可能会考虑我在this question的回答中提到的那个:有界的上下文。在你的应用程序中,关注点越分散,对你和你的程序员来说,长远来看就会越好(特别是当你想要单元测试这一切)。

+0

更改为IEnumerable – tom 2013-02-13 15:48:03

2

我会personnally暴露出很多简单的方法。

使用映射连接的客户到供应商和供应商 的服务,然后我打开我得到他所有的 供应商,其服务(和其他表加载)客户

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

所以,你可以暴露例如:

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

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

+0

旧的答案。但我喜欢这种方法。保持资料库苗条,以每个表为基础,并利用应用程序层中的多个(即使是异步)调用将数据结合在一起。 – pimbrouwers 2018-01-31 16:00:44

1

这只是我的看法,我不知道你的情况是否正确,因为这取决于你的业务需求,但我通常更喜欢第三种选择。

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

样品的方法signiture(T是实体的类型):

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

更改为IEnumerable – tom 2013-02-13 15:47:37

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或查看模型或类似的东西。当您公开延迟加载启用的实体对象时,消费者可能无意中寻址尚未加载的导航属性,并且上下文消失。