如何将继承添加到EntityDataModel(EF 4)中定义的对象?

时间:2010-09-24 16:09:51

标签: inheritance entity-framework-4 identity-column table-per-type

我在EF模型Person< - User中定义了一个简单的2对象继承。

Person是基本实体/类,它不是抽象的,它包含firstname,lastname,email等字段。

User是派生的实体/类,包含username,lastlogin,islockedout等字段。

数据库使用每个类型的表模式,因此有一个表用于Person,另一个表用于User。两者都使用相同的主键字段PersonID。 PersonID是IDENTITY列,但在EDM中,PersonID的StoreGeneratedPattern设置为None。

因为这是每个类型的表继承schama,所以可能有一个Person行/对象没有相应的User行/对象。这样系统就可以包含非用户的数据。但是,一个人最终可能成为用户,这就是我遇到问题的地方。

我有一个针对此场景的测试用例,系统会找到一个想要变成用户的Person。我尝试在新的User对象上设置PersonID字段,将其添加到ObjectContext,并保存更改。发生的情况是数据库创建一个新的Person行以及新的User行,忽略我为PersonID设置的值。

我该如何解决这个问题?我是否必须创建sprocs来处理EDM crud操作?

2 个答案:

答案 0 :(得分:2)

这是你的问题:

  

我有一个针对此场景的测试用例,系统找到一个想要变成用户的人员

对象无法在C#或我知道的任何其他基于类的OOPL中更改类型。 EF不会改变这一点。

您需要更改设计。这不是OO。去年我wrote about this

  

在设计良好的对象关系映射时,您必须克服的一个心理障碍是倾向于主要以面向对象的术语或关系术语来思考,以适合您的个性。但是,良好的对象关系映射包含了良好的对象模型和良好的关系模型。例如,假设您有一个包含People表的数据库,以及Employees和Customers的相关表。一个人可能在所有三个表中都有记录。现在,从严格的关系角度来看,您可以为员工构建数据库VIEW,为客户构建另一个数据库VIEW,这两者都包含People表中的信息。当使用一个VIEW或另一个时,您可以暂时将个人视为“只是”员工或“只是”客户,即使您知道他们两者都是。所以来自这个世界观的人可能会想要进行OO映射,其中Employee和Customer都是Person的(直接)子类。但这不适用于我们拥有的数据;由于单个人同时拥有员工和客户记录(并且由于没有Person实例可以同时具有Employee和Customer的具体子类型),因此Person和Employee之间的OO关系需要composition而不是继承,并且类似于人与客户。

答案 1 :(得分:0)

我能够解决这个问题,尽管我对解决方案并不满意。我最终编写了存储过程来映射Person和User的插入,更新和删除操作(因为它们在同一个Entity集中,所有都必须与所有3个sprocs映射)。 User的insert sproc接受PersonID参数,并使用它来决定是创建新的Person行,还是附加到现有的Person行。

问题是PersonID必须映射为sproc输入参数和Result Column绑定。当db必须生成一个新的PersonID时,我们必须使用结果绑定将其取回,以便其他表为引用PersonID的外键列获取正确的值。除了....您不能同时将User.PersonID映射为输入参数和结果列绑定。这会导致运行时异常“无法确定依赖操作的有效排序。由于外键约束,模型要求或存储生成的值,可能存在依赖关系。”

我最终向User表(以及相应的概念模型)添加了一个新列,这是一个可以为空的int(我称之为BasePK)。在EDM中,我为此属性保护了getter和setter,以将它们隐藏在公共接口之外。然后,我将BasePK映射为sproc输入参数,并将PersonID映射为结果列绑定,以便在生成标识值时接收标识值。 User构造函数的一个重载如下:

public User(int personID):base() {     this.BasePK = personID; }

所以现在,每当我想将现有的Person转变为User时,我只需要使用这个重载来构造用户。

int personID = [搜索人员并返回他/她的PersonID的一些方法]; var User = new User(personID);

当ObjectContext插入实体时,它通过BasePK属性将PersonID传递给sproc。完成sproc后,它会将PersonID返回给实际的主键属性。

我不喜欢必须添加一个列&完成这个的属性,但它与我对Craig的回答的评论一致:我希望能够插入具有新专业的Person,而无需每次都更改/重构Person实体。这样,至少我可以保留派生实体类型中的所有更改,即使这意味着这些更改涉及添加无意义的列,这些列在物理存储中始终为null。