我有一个包含客户,供应商和服务的数据库(这是一个粗略的简化,我真的有大约100个表)
我正在开发一个新的Entity Framework库来访问这些表。
客户有很多供应商
供应商有许多服务
我正在尝试决定采用哪种方法 -
A) 使用映射将客户连接到供应商,将供应商连接到服务,然后每次加载客户时,我都会获得所有供应商及其服务(以及其他加载的表格)
B)
实体之间没有映射,但在相关存储库中提供方法;例如在供应商存储库中,我将IEnumerable<Supplier> GetSupplierByCustomerID(int customerID)
编辑 根据建议将上述内容更改为IEnumerable。
这些是使用EF时的两种主要方法吗?从您的角度来看,这被认为是更好的。
我还有其他方法吗?
答案 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)
这只是我的意见,我不知道你的情况是否合适,因为它取决于你的业务需求,但我通常更喜欢第三种选择。
IEnumerable
,而不是IQueryable
:这样可以在运行任何业务逻辑之前完成所有数据库操作。来自基础存储库的示例方法签名(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中进行连接。
拥有导航属性并不意味着它们总是被加载。对于延迟加载,必须满足两个条件
virtual
,以使EF能够在代理类型中覆盖它,并连接到延迟加载。context.Configuration.LazyLoadingEnabled = false
。因此,这也显示了两种控制延迟加载的方法:您可以在结构上或临时启用/禁用它。
除此之外,您可以通过两种方式控制相反的,急切的加载:
使用Include
声明:
db.Suppliers.Include(s => s.Services)
在投影中包含导航属性:
from supp in db.Supliers
from serv in supp.Services
select new { supp.Name, serv.ServiceName }
(有更多方法,但这些是最重要的方式)
这适用于在您的服务或存储库中编写linq查询。正如其他人所说:不要向服务/存储库方法的使用者公开IQueryable
。
最后一个重要注意事项:延迟加载只能在生命环境的范围内进行。如果处理了上下文并且解决了延迟加载导航属性,则抛出异常。同时,建议使用寿命较短的上下文实例。所以存在两难:暴露实体对象或仅暴露DTO或视图模型或类似的东西。当您公开延迟加载的实体对象时,消费者可能会无意中解决尚未加载的导航属性,并且上下文消失了。