【发布时间】:2010-04-17 17:50:14
【问题描述】:
我正在阅读有关 Entity Framework 4.0 的内容,我想知道为什么我应该创建一个复杂类型而不是一个新实体(表)以及它们之间的关系?
【问题讨论】:
标签: c# .net entity-framework .net-4.0
我正在阅读有关 Entity Framework 4.0 的内容,我想知道为什么我应该创建一个复杂类型而不是一个新实体(表)以及它们之间的关系?
【问题讨论】:
标签: c# .net entity-framework .net-4.0
完美的例子是地址。使用复杂类型的地址比新实体更容易处理。对于复杂类型,您不必处理主键。考虑访问地址有多少常见类型的实体将具有地址(业务单位、人员、地点)。想象一下填充许多人的地址并需要为每个人设置一个密钥。对于复杂类型,您只需访问它们类型的内部属性即可。这是一个示例的 MSDN 链接。 http://msdn.microsoft.com/en-us/library/bb738613.aspx
【讨论】:
这个问题已经有一段时间了,但我还是要添加一个答案,希望下一个可怜的呜咽知道他在做什么。
复杂类型不支持延迟加载,至少在 EF 4.3 中不支持。我们以地址情况为例。您有一个包含 15 列的 Person 表,其中 5 列包含某些个人的地址信息。它有 50k 条记录。您为具有复杂类型 Address 的表创建实体 Person。
如果您需要数据库中所有个人的姓名列表,您可以这样做
var records = context.Persons;
其中还包括地址,将 5*50k 值无缘无故地添加到您的列表中,并且有明显的延迟。您可以选择仅使用
以匿名类型加载所需的值var records = from p in context.Persons
select new {
LastName = p.LastName,
FirstName = p.FirstName,
}
这适用于这种情况,但是如果您需要一个更全面的列表,例如 8 个非地址列,您需要在匿名类型中添加每个列,或者只使用第一种情况并返回加载无用的地址数据。
这是关于匿名类型的事情:虽然它们在单个方法中非常有用,但它们会迫使您在类或类子类中的其他地方使用动态变量,这否定了 Visual Studio 的一些重构工具并让您可以运行 -时间错误。理想情况下,您希望在您的方法之间循环实体,因此这些实体应该携带尽可能少的包袱。这就是延迟加载如此重要的原因。
当谈到上面的例子时,地址信息真的应该在一个自己的表中,并有一个完整的实体覆盖它。附带的好处是,如果您的客户要求为某人提供第二个地址,您只需在 Person 中添加一个额外的地址引用,即可将其添加到您的模型中。
如果与上面的示例不同,您实际上几乎在每个查询中都需要地址数据,并且确实希望在 Person 表中包含这些字段,那么只需将它们添加到 Person 实体即可。您将不再有整洁的地址前缀,但它不是完全可以失眠的东西。
但等等,还有更多!
复杂类型是一种特殊情况,是普通 EF 实体的平滑景观上的一个凸起。您项目中的那些可能没有资格从您的实体基类继承,因此无法将它们通过处理您的一般实体的方法。
假设您有一个名为 EntityModel 的实体基类,它定义了一个属性 ID。这是所有实体对象的键,因此您现在可以创建
class EntityModelComparer<T> : IEqualityComparer<T> where T : EntityModel
然后您可以将其与 Distinct() 一起使用以过滤来自任何类型 T 的 IQueryable 的重复项,其中 T 是一个实体类。复杂类型不能从 EntityModel 继承,因为它没有 ID 属性,但这很好,因为无论如何您都不会在其上使用 distinct。
接下来,您会遇到一种情况,您需要某种方式来通过任何实体并执行操作。也许您想在 UI 上动态列出实体的属性并让用户对它们执行查询。因此,您构建了一个可以为特定类型实例化的类,并让它处理整个事情:
public class GenericModelFilter<T> : where T : EntityModel
哦,等等,您的复杂类型不是 EntityModel 类型。现在您必须使实体继承树复杂化以适应复杂类型或摆脱 EntityModel 契约并降低可见性。
接下来,您向您的类添加一个基于用户选择的方法,该方法可以创建一个表达式,您可以使用该表达式与 linq 一起过滤任何实体类
Expression<Func<T, bool>> GetPredicate() { ... }
所以现在你可以这样做:
personFilter = new GenericModelFilter<Person>();
companyFilter = new GenericModelFilter<Company>();
addressFilter = new GenericModelFilter<Address>(); //Complex type for Person
...
var query = from p in context.Persons.Where(personFilter.GetPredicate())
join c in context.Companies.Where(companyFilter.GetPredicate()) on p.CompanyID = c.ID
select p;
这对所有实体对象都一样...除了具有特殊需求的地址。您不能像在 Company 中那样加入它。您可以从 Person 导航到它,但是如何在其上应用该 Expression 并最终以 Person 结束?现在,您必须花点时间找出这个特殊情况,以建立一个在其他任何地方都可以轻松工作的简单系统。
这种模式在项目的整个生命周期中不断重复。我是根据经验说话吗?我希望我没有。复杂的类型不断阻止你的进步,就像一个行为不端的学生在课堂后面,没有添加任何本质。帮自己一个忙,选择实际的实体对象。
【讨论】:
基于领域驱动设计概念,聚合根可以有一个或多个内部对象作为其部分。在这种情况下,内部对象 - 在 Aggregate Root 的边界内 - 没有任何 KEY。父密钥将应用于它们或以某种方式应用。您的答案又回到了将所有部分保留在聚合根中的好处,这使您的模型更加健壮和简单。
【讨论】: