mVC3 EF4.1模型设计错误

时间:2012-01-03 16:49:14

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

使用代码优先模型

在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或数据注释显式配置此关联的主要结尾。

非常感谢你!

2 个答案:

答案 0 :(得分:1)

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

但是,看起来你在这里有一个1到0..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; }
}

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

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

答案 1 :(得分:0)

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