实体框架复杂类型与创建新实体

时间:2010-04-17 17:50:15

标签: c# .net entity-framework .net-4.0

我正在阅读Entity Framework 4.0,我想知道为什么要创建复杂类型而不是新实体(表)以及它们之间的关系?

3 个答案:

答案 0 :(得分:19)

完美的例子是一个地址。对于地址使用复杂类型比新实体更容易处理。对于复杂类型,您不必处理主键。考虑访问地址有多少常见类型的实体具有地址(业务单位,人员,地点)。想象一下,填充许多人的地址,并需要为每个人设置一个密钥。对于复杂类型,您只需访问它们键入的内部属性即可。这是一个示例的MSDN链接。 http://msdn.microsoft.com/en-us/library/bb738613.aspx

答案 1 :(得分:12)

这个问题已经存在了一段时间了,但无论如何我都要补充一个答案,希望下一个可怜的呜咽知道他在做什么。

复杂类型不支持延迟加载,至少在EF 4.3中不支持。我们以地址情况为例。您有一个包含15列的Person表,其中5列包含某些个人的地址信息。它有5万条记录。您可以为具有复杂类型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;

这对所有实体对象都有效...除了具有特殊需求的地址。你不能像公司一样加入。您可以从Person导航到它,但是如何在其上应用该表达式并最终仍然以Person结尾?现在你需要花点时间找出这个特殊情况,这个系统可以在其他任何地方轻松使用。

这种模式在整个项目的生命周期中重复出现。我是从经验中说话吗?我希望我没有。复杂的类型会阻止你的进步,就像课后的一个行为不端的学生一样,没有添加任何本质的东西。帮自己一个忙,选择实际的实体对象。

答案 2 :(得分:0)

基于域驱动设计概念,聚合根可以将一个或多个内部对象作为其部分。在这种情况下,内部对象 - 在聚合根的边界内 - 没有任何KEY。父键将应用于它们或以某种方式这样。您的答案回归到将所有部件保留在Aggregate root中,使您的模型更加健壮和简单。

相关问题