无法确定类型之间关联的主体端 - Entity Framework 错误,class 之间的关系相同 class

Unable to determine the principal end of an association between the types - Entity Framework error, class relationship between the same class

出现下面的class,尝试在数据库中查找记录returns错误。 使用 C#、MVC 4、Entity Framework 4 和 SQL Server 2012 数据库。

错误

Unable to determine the principal end of an association between the types 'FlexApp.Models.Model.Usuario' and 'FlexApp.Models.Model.Usuario'. 

The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.

Class

public class Usuario
{
    [Key]
    public int UsuarioID { get; set; }
    public string Nome { get; set; }
    public int UsuCad { get; set; }        
    public int UsuAlt { get; set; }

    [ForeignKey("UsuCad")]
    public virtual Usuario UsuarioCad { get; set; }
    [ForeignKey("UsuAlt")]
    public virtual Usuario UsuarioAlt { get; set; }
}

数据库 FK

alter table USUARIO add constraint USUARIO_fk01 foreign KEY(UsuCad) REFERENCES USUARIO(UsuarioID);
alter table USUARIO add constraint USUARIO_fk02 foreign KEY(UsuAlt) REFERENCES USUARIO(UsuarioID);

错误是因为 class 名称 Usuario 与 属性 public virtual Usuario UsuarioAlt { get; set; }

中的名称完全相同

您不能使用与导航相同的类型 属性。这里的问题是导航属性是错误的。想想SQL数据库table,同一个唯一主键能不能在同一个table里做外键呢?您需要您的外键来自另一个 table.

导航属性 Entity Framework 用于确定您的 POCO 之间的实体关系模型,从而确定您的数据库 tables。

您想要一个不同的 class 关系。如果你有第二个 class 同名,使用命名空间别名,这样运行时可以知道两个 class 之间的区别,现在它就像一个 class 引用自己 .

你应该像下面这样写你的模型

public class Usuario
{
    [Key]
    public int UsuarioID { get; set; }
    public string Name { get; set; }
    public int UsuCad { get; set; }        
    public int UsuAlt { get; set; }
    public virtual SomeSecondClass SomeSecondClass { get; set; }
    public virtual SomeThirdClass SomeThirdClass { get; set; }
}

public class SomeSecondClass 
{
   public int SomeSecondClassID { get; set;}
   // More properties
}

public class SomeThirdClass 
{
   public int SomeThirdClassID { get; set;}
   // More properties
}

回复

经过大量搜索,我找到了一个使用技巧 InverseProperty 然后代码看起来像这样。这个 属性 ForeignKey 是挂载 link 和 属性 InverseProperty 来通知字段依赖这个其他阵营,反转外键。

感谢帮助

public class Usuario
{
    [Key]
    public int UsuarioID { get; set; }
    public string Nome { get; set; }
    public int UsuCad { get; set; }        
    public int UsuAlt { get; set; }

    [ForeignKey("UsuCad")]
    [InverseProperty("UsuarioID")]
    public virtual Usuario UsuarioCad { get; set; }
    [ForeignKey("UsuAlt")]
    [InverseProperty("UsuarioID")]
    public virtual Usuario UsuarioAlt { get; set; }
}

我不是 100% 确定您要在此处实现哪种数据库模式。我的假设是您希望拥有相同 table 的外键。在您的评论中,您说您想要一对一的关系。从技术上讲这是不可能的。因为当您尝试插入第一条记录时,您需要为您的外键列提供一个值。该值应该是现有记录的 Id。等等...我们还没有任何记录!

由于 1-1 在这里不太实用,您应该建立一对 zero/one 关系。这意味着您的外键列应该是 nullable,这样您就可以在外键列中插入第一个具有 NULL 值的记录。

我有点难以理解您的模型和 属性 名称。所以我将使用每个人都能理解的通用 class/table,但要符合您的特定要求(自引用外键)

我没有用数据标注,我用的是Fluent API

愚蠢的事情requirement/assumption是:

  • 一个可能有也可能没有Parent
  • 一个可能有也可能没有孩子

所以我们的实体 class 看起来像这样

public class Person
{
    public int Id { set; get; }
    public string Name { set; get; }
    public virtual Person Parent { set; get; }
    public virtual Person Kid { set; get; }    
}

现在要使用流畅的 API 来控制关系,我们需要转到我们的 DBContext class 并覆盖 OnModelCreating 方法

public class MyDbContext : DbContext
{
    public MyDbContext() : base("MyConnectionStringName") { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {    
        modelBuilder.Entity<Person>().HasKey(d => d.Id)
            .HasOptional(m => m.Kid).WithOptionalPrincipal(d=>d.Parent);

        base.OnModelCreating(modelBuilder);
    }   

    public DbSet<Person> Persons { set; get; }
}

这将创建包含 3 列的 table,IDNameParent_Id(NULLABLE,与 table 的 ID 的外键关系)

我可以这样插入数据

var db = new MyDbContext();

var myMom = new Person {Name = "Indira"};
var me = new Person {Name = "Shyju", Parent = myMom};
var myDaughter = new Person { Name = "Gauri", Parent = me};

db.Persons.Add(myMom);
db.Persons.Add(me);
db.Persons.Add(myDaughter);

db.SaveChanges();

并且您将拥有 ParentId 列的数据,该列具有指向相同 table.

的 ID 列的外键