Entity Framework - 使用相同键的多个 1 到 0..1 关系
Entity Framework - Multiple 1 to 0..1 relationships using the same Key
我已经阅读了尽可能多的关于这个主题的帖子,但是 none 我尝试过的解决方案似乎有效。我有一个现有数据库并从现有数据库项目创建了一个新代码优先。
我有一个名为 Thing 的基地 table。每个对象在这个 table 中都有一条记录,使用 Id 作为唯一主键。每个其他对象都继承自此,但它们在子 table 中使用相同的 Id,而不在子 table 中使用新的标识列。有效地给每个 'Thing' 一个唯一的 ID:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Car
{
public int Id { get; set; }
//other properties
}
public class Person
{
public int Id { get; set; }
//other properties
}
public class Color
{
public int Id { get; set; }
//other properties
}
每个新记录首先在 'Thing' 中创建一个项目,然后使用该 Id 值在其各自的 table 中创建一个新记录,创建多个 1 到 0..1 关系,其中 Id 字段在派生的 tables 上也是事物的 FK。
事物 1 到 0..1 汽车
事物 1 到 0..1 人
事物 1 到 0..1 颜色
等等
我尝试了许多不同的 Data Annotation 和 Fluent API 组合,但它总是返回相同的错误:
'Unable to retrieve metadata for Model.Car'. Unable to determine the principal end of association between the types 'Model.Thing' and 'Model.Car'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
我确实设法通过使用带有反向注释的虚拟并将 Id 字段设置为 Key 和 ForeignKey 来克服此错误,但随后消息跳转到 Person。如果您随后将其设置为与 Car 相同,则消息将恢复为 Car。
看来我可以返回并为每个子项创建一个正常的外键 table,但这需要大量工作,我相信有可能以某种方式使它正常工作。最好使用流利的 API.
如果要使用Data Annotations,还需要将依赖实体的PK声明为FK:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car{get;set;}
}
public class Car
{
[Key,ForeignKey("Thing")]
public int ThingId { get; set; }
//other properties
public virtual Thing Thing{get;set;}
}
如果你打算使用 Fluent Api(从你的模型中删除属性),配置将是这样的:
modelBuilder.Entity<Car>().HasRequired(c=>c.Thing).WithOptional(t=>t.Thing);
根据指定的多重性,只有 Thing
是主体并且 Car
是从属才有意义,因为 Thing
可以在没有 Car
但是 Car
必须有一个 Thing
。
正如你所看到的,你不需要指定 ThingId
是这个 relationship.This 的外键是因为 Entity Framework 要求依赖的主键用作外键。由于别无选择,Code First 只会为您推断这一点。
更新
再次阅读您的问题,我认为您正在尝试创建层次结构。在这种情况下,您可以使用 Table per Type (TPT) 方法。
我已经阅读了尽可能多的关于这个主题的帖子,但是 none 我尝试过的解决方案似乎有效。我有一个现有数据库并从现有数据库项目创建了一个新代码优先。
我有一个名为 Thing 的基地 table。每个对象在这个 table 中都有一条记录,使用 Id 作为唯一主键。每个其他对象都继承自此,但它们在子 table 中使用相同的 Id,而不在子 table 中使用新的标识列。有效地给每个 'Thing' 一个唯一的 ID:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Car
{
public int Id { get; set; }
//other properties
}
public class Person
{
public int Id { get; set; }
//other properties
}
public class Color
{
public int Id { get; set; }
//other properties
}
每个新记录首先在 'Thing' 中创建一个项目,然后使用该 Id 值在其各自的 table 中创建一个新记录,创建多个 1 到 0..1 关系,其中 Id 字段在派生的 tables 上也是事物的 FK。
事物 1 到 0..1 汽车
事物 1 到 0..1 人
事物 1 到 0..1 颜色
等等
我尝试了许多不同的 Data Annotation 和 Fluent API 组合,但它总是返回相同的错误:
'Unable to retrieve metadata for Model.Car'. Unable to determine the principal end of association between the types 'Model.Thing' and 'Model.Car'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
我确实设法通过使用带有反向注释的虚拟并将 Id 字段设置为 Key 和 ForeignKey 来克服此错误,但随后消息跳转到 Person。如果您随后将其设置为与 Car 相同,则消息将恢复为 Car。
看来我可以返回并为每个子项创建一个正常的外键 table,但这需要大量工作,我相信有可能以某种方式使它正常工作。最好使用流利的 API.
如果要使用Data Annotations,还需要将依赖实体的PK声明为FK:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car{get;set;}
}
public class Car
{
[Key,ForeignKey("Thing")]
public int ThingId { get; set; }
//other properties
public virtual Thing Thing{get;set;}
}
如果你打算使用 Fluent Api(从你的模型中删除属性),配置将是这样的:
modelBuilder.Entity<Car>().HasRequired(c=>c.Thing).WithOptional(t=>t.Thing);
根据指定的多重性,只有 Thing
是主体并且 Car
是从属才有意义,因为 Thing
可以在没有 Car
但是 Car
必须有一个 Thing
。
正如你所看到的,你不需要指定 ThingId
是这个 relationship.This 的外键是因为 Entity Framework 要求依赖的主键用作外键。由于别无选择,Code First 只会为您推断这一点。
更新
再次阅读您的问题,我认为您正在尝试创建层次结构。在这种情况下,您可以使用 Table per Type (TPT) 方法。