在this blog post titled "Pragmatic Linq"中,Marc Gravell讨论了如何以允许在数据访问层(DAL)中使用Linq到实体的方式实现存储库模式,而不允许实现细节泄漏到DAL,并且不会很难正确测试DAL。
Mark提出的问题之一是DAL类通常包含导航属性,在大多数情况下,它不应该是公共API的一部分:
导航属性...非常快速地开始交叉聚合和/或导致懒惰行为(如果您的上下文不再存在则不太好)
传统上,可以通过创建一组用于DAL的类(具有EF导航属性)以及在DAL的公共API中使用的另一组类(没有导航属性)来解决这个问题。但是,马克不赞成这种做法:
我真的不想申报和维护“纯粹”(即与DAL分开)对象模型
他建议可以使用一组类,但将导航属性保留在存储库内部:
我目前的想法是......大多数导航属性应该标记为数据层的内部属性。这意味着您的存储库实现可以使用导航属性来构建有趣的查询,但是返回给调用者的公共API不包含它们。如果调用者从订单存储库获取并订购,并且想要了解客户的详细信息:艰难 - 去询问客户存储库。
这对我来说听起来不错 - 实际上,我花了几个小时试图以这种方式重构我的DAL。但是,在完成所有编译并开始运行/测试之后,我发现了一个问题。
我正在使用实体框架代码优先(EF6,如果它很重要)来持久化实体,并且我在我的实体类中标记为internal
我不想成为其中的任何属性我的DAL的公共API。 (我已将我的数据访问代码移动到一个单独的程序集中,因此internal
实际上意味着什么。)
但事实证明,Entity Framework会忽略任何标记为internal
的属性!例如,我有一个整数FooId
属性,我在内部使用,我不想公开,所以我将其标记为internal
。现在,如果我允许EF重新创建我的数据库,它不会在相关表上创建FooId
列。
更糟糕的是,如果导航属性标记为internal
,它现在不再在相应的表之间创建外键关系。我想它将不再支持那些导航属性的延迟加载,尽管我没有测试过。
我是否误解了马克提出的建议?还有另一种方法可以将某些属性保留在DAL内部,但EF完全支持吗?
例如,我可以创建一个基类,用于仅具有公共属性的API,然后使用内部使用(和EF)的额外属性的派生类吗?听起来它可能会起作用,但之后我会有一个命名冲突(我希望我的API使用类似“Customer”的有意义名称的类,但我也希望我的内部类称为“Customer”所以EF会创建一个明智的数据库表。我想我可以通过覆盖约定强制表名,但仍然......)。答案 0 :(得分:3)
默认情况下,Code First将在模型中包含公共属性。但您可以使用FluentAPI映射内部属性。您甚至可以通过这种方式映射私有属性(如果您查看该情况下所需的额外步骤的引用)。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Projects);
}
参考文献:
http://blog.oneunicorn.com/2012/03/26/code-first-data-annotations-on-non-public-properties/
按惯例,BTW FooID将是名为Foo 的导航属性的外键答案 1 :(得分:1)
EF并没有忽略导航属性,但它无法在没有特权的情况下反映出来。添加它会增加在受信任模式下运行EF的要求,这是另一种蠕虫。我可以通过两种方式来解决这个问题,我从未尝试过这两种方式。
在编译时编织属性。
使用此方法,您可以将internal
属性编译为dll中的public
属性。这样EF可以反映属性。但是,由于您的来源仍使用internal
,因此您知道您的业务层不会有引用这些属性的来源。
这种方法的缺点是,当您通过dll而不是项目引用它时它不起作用。
您可以使用多种技术实现此目标,例如Fody/Publicize或PostSharp。
在EntityMapper<>
班级
顾名思义,您明确地映射了您的属性,以便EF不需要反映您的类,以便能够找到属性。
理论上,EF应该能够使用Expressions.PropertyExpression
来完成所需的所有设置/获取操作。以及覆盖代理类的相关属性。在实践中,我不太确定。
答案 2 :(得分:-3)