当它要映射或不映射时,我会选择 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 或视图模型或类似的东西。当您公开启用延迟加载的实体对象时,消费者可能会无意中处理尚未加载的导航属性,并且上下文已消失。