使用代码优先模型
在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或数据注释显式配置此关联的主要结尾。
非常感谢你!
答案 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)
Contact
和Company
之间存在一对一的关系,例外情况说EF不能决定什么是主体(拥有主键)和什么是依赖(拥有外键) )因为导航属性是“对称的”。 EF要求您明确指定。
有关一对一关系的一些注意事项(使用EF比使用一对多甚至多对多关系更难掌握):
EF仅支持Shared Primary Key Associations来定义一对一的关系。这意味着关联实体的主键值必须相同。对于其中一个实体,主键同时是外键。您不能使用独立的外键。
这一点的后果是您可以删除Contact.CompanyID
和Company.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);