【问题标题】:mVC3 EF4.1 Model design errormVC3 EF4.1 模型设计错误
【发布时间】:2012-01-03 16:49:14
【问题描述】:

MVC3 中的 EF 4.1 和延迟加载,使用代码优先模型

难以设计正确的模型。请看一下,让我知道我做错了什么。我该如何解决它。

我正在使用 Membership API 创建帐户。成功创建帐户后。我重定向以自动创建联系人记录。 contactId(自动生成数据库),userid(存储会员api生成的用户id)

模型是:

public class Contact
{
   public int ContactID { set; get; }
   public string UserId { set; get; }
   public string LastName { set; get; }
   public int? CompanyID { set; get; } // not sure if I need this as it will be NULL
   public virtual Company CompanyInfo { set; get; }
}

接下来,用户可以单击创建公司链接或稍后注销并登录以创建公司记录。

public class Company
{    
  public int CompanyID { set; get; }
  public int ContactID { set; get; }
  public string CompanyName { set; get; }
  public virtual Contact Contacts { set; get; }
}

当用户决定创建公司记录时,我正在检查公司是否已经存在,如果存在我只是显示联系信息和公司信息,或者如果没有找到我重定向到创建公司。

public ActionResult chckifCompanyFound()
    {
        int contactId = 1; //Assuming I retrieved the value 
        //I think I should get the data from Company table, if company data found then contact data could be retrieved using lazy loading? 
       Company c= db.Company.Include(c => c.Contacts).Where(x => x.ContactID == contactId).FirstOrDefault();
      if(c == null)
          //redirect to create company
      else
         // view data from c company object
    }

目前,在会员 API 创建帐户后尝试创建联系人记录时,它会显示异常。我这样创建记录:

Contact contact = new Contact();
contact.UserId = userId;
contact.LastName = lastName;
db.Contacts.Add(contact);
db.SaveChanges();

例外:
无法确定类型“D.Models.Contact”和“D.Models.Company”之间关联的主体端。此关联的主体端必须使用关系流式 API 或数据注释显式配置。

非常感谢!

【问题讨论】:

    标签: asp.net-mvc-3 entity-framework-4 entity-framework-4.1


    【解决方案1】:

    答案在您的例外中:必须使用关系流式 API 或数据注释显式配置此关联的主体端。

    但是,您在这里似乎有 1 对 0..1 的关系:每个公司必须有 1 个联系人,但每个联系人可以属于零个或 1 个公司。这真的是你的意图吗?

    在我看来,您真正追求的是 1 对 * 关系,其中每个公司可以有许多联系人,每个联系人属于零个或 1 个公司。

    数据注释

    public class Company // the principal
    {    
      public int CompanyID { set; get; }
      //public int ContactID { set; get; } Company is the principal
      public string CompanyName { set; get; }
      public virtual ICollection<Contact> Contacts { set; get; } // has many contacts
    }
    public class Contact
    {
       public int ContactID { set; get; }
       public string UserId { set; get; }
       public string LastName { set; get; }
    
       // you do need CompanyId, but it will be nullable in the db
       public int? CompanyID { set; get; } 
       [ForeignKey("CompanyID")] 
       public virtual Company CompanyInfo { set; get; }
    }
    

    流畅的 API 注意:使用 fluent API,您的实体不需要 [ForeignKey] 属性

    modelBuilder.Entity<Company>()
        .HasMany(principal => principal.Contacts)
        .WithOptional(dependent => dependent.CompanyInfo)
        .HasForeignKey(dependent => dependent.CompanyID);
    

    【讨论】:

      【解决方案2】:

      ContactCompany 之间存在一对一的关系,并且异常说 EF 无法决定什么是主体(具有主键)和什么是依赖项(具有外键),因为导航属性是“对称的”。 EF 要求您明确指定。

      关于一对一关系需要注意的几点(用 EF 比一对多甚至多对多关系更难掌握):

      • EF 仅支持Shared Primary Key Associations 定义一对一关系。这意味着关联实体的主键值必须相同。对于其中一个实体,主键同时是外键。您不能使用独立的外键。

      • 这一点的结果是您可以删除Contact.CompanyIDCompany.ContactID。 EF 不会将这些属性视为外键属性。

      • 第二个后果是,其中一个主键不能是数据库中自动生成的身份,因为它必须始终与另一个(主体)实体具有相同的值。

      • 您必须决定哪个实体是主体,哪个是从属实体。根据您的描述,我猜Contact 是委托人(因为您允许在没有公司的情况下进行联系),而Company 是受抚养人(因为没有没有联系的公司是可能的)。

      • 然后您可以在派生上下文中使用 Fluent API 定义映射:

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // ...
        
            modelBuilder.Entity<Contact>()
                .HasOptional(ct => ct.CompanyInfo)
                .WithRequired(cm => cm.Contacts);
        
            // ...
        }
        

      我相信此映射将自动确保(如果您使用 EF 创建数据库)Contact 的主键是数据库中的标识,但 Company 的主键不是。如果没有,您可以明确关闭它:

      modelBuilder.Entity<Company>()
          .Property(c => c.CompanyID)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
      

      【讨论】:

      • 重新阅读问题后,看起来这就是问题所要求的。我对 Company 实体上的 public virtual Contact Contacts { set; get; } 属性的命名感到困惑。
      • @olivehour:我也对复数感到困惑。只有在阅读异常后,我才注意到没有ICollection。谁知道呢,也许他确实想要一对多的关系,至少公司有很多个联系人感觉更自然。
      • 非常感谢你们俩的帮助。我将尝试更新我的代码,看看它是否有效。我很抱歉混淆了实体的命名。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-06
      • 1970-01-01
      相关资源
      最近更新 更多