【问题标题】:Unable to determine the principal end of an association between the types - Entity Framework error, class relationship between the same class无法确定类型之间关联的主体结束 - 实体框架错误,同一类之间的类关系
【发布时间】:2016-02-28 21:17:04
【问题描述】:

拥有下面的类,并尝试在数据库中查找记录返回错误。 使用 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.

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; }
}

数据库外键

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);

【问题讨论】:

  • 外键是否存在?
  • 数据库@Papa中存在外键
  • 有代码优先和数据库优先的方法。不过我最喜欢的是Read first 方法
  • @marlon.tiedt 您期望的数据库结构是什么?你能说一下你想要达到什么类型的关系吗?一对多/一/零?
  • 关系是一对一的@Shyju

标签: c# .net entity-framework asp.net-mvc-4


【解决方案1】:

回复

找了好久,找到了一个使用InverseProperty的tips 然后代码看起来像这样。这个属性ForeignKey是挂载链接和属性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; }
}

【讨论】:

    【解决方案2】:

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

    由于 1-1 在这里并不实用,因此您应该建立一对零/一的关系。这意味着您的外键列应该是可为空的,这样您就可以在外键列中插入具有NULL 值的第一条记录。

    我很难理解您的模型和属性名称。所以我将使用每个人都可以理解的通用类/表,但要满足您的特定要求(自引用外键)

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

    愚蠢的业务要求/假设是:

    • 一个可能有也可能没有父母
    • 一个可能有也可能没有孩子

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

    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; }    
    }
    

    现在要使用 fluent API 来控制关系,我们需要转到我们的 DBContext 类并覆盖 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 列、IDNameParent_Id 的表(NULLABLE,与 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 列,该列具有同一个表的 ID 列的外键。

    【讨论】:

      【解决方案3】:

      错误是因为类名Usuario与属性public virtual Usuario UsuarioAlt { get; set; }中的完全相同

      您不能使用与导航属性相同的类型。这里的问题是导航属性是错误的。想想SQL数据库表,同一个唯一的主键可以是同一张表的外键吗?你需要你的外键来自另一个表。

      导航属性是 Entity Framework 用来确定您的 POCO 之间的实体关系模型以及您的数据库表之间的实体关系模型。

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

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

      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
      }
      

      【讨论】:

      • 但是这样我就得复制课程了?
      • 不,您不会有重复的课程。我已经扩展了代码希望它有所帮助。这是代码优先方法。使用 EF 约定。阅读此博客。 tinyurl.com/p24ytvt
      • 如果你有一个自引用表,你可以拥有一个与 EF 中的类同名的属性。
      • @SWeko 您不能使用与导航属性相同的类型。这里的问题是导航属性是错误的。想想SQL数据库表,同一个唯一的主键可以是同一张表的外键吗?你需要你的外键来自另一个表。
      • @SWeko 导航属性是 Entity Framework 用来确定 POCO 和数据库表之间的实体关系模型
      猜你喜欢
      • 2014-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-26
      相关资源
      最近更新 更多