我在使用Entity Framework 4.1 Code First的ASP.NET MVC 3应用程序中遇到了一个有趣的错误。我有三个按顺序连接的类/表。有一个Invitation
引用Project
,然后引用Company
。
当我加载公司并保存时,一切都很好。项目也一样。但是,当编辑和保存邀请时,它会清除公司中的字段。他们都只是空白!
当我编辑项目时,我需要显示公司的一些信息,所以我明确加载.Include(x => x.Company)
。当我编辑邀请时,我不需要公司,所以我没有打扰包括它。
我认为如果对象没有被加载,那么EF应该没有任何理由将其标记为已编辑,对吧?
更新:通过注释掉代码行进行了大量调试后,我将其缩小了一些。
要清除的实际对象是公司引用的Contact
对象。它并没有真正被清除,因为在构造函数中创建了一个新的联系人(因此新公司不会为空。)
所以我想这改变了我的问题:有没有办法让引用属性设置为默认值而不破坏EF?
public class InvitationController
{
[HttpPost]
public RedirectToRouteResult AcceptInvitation(int id, int companyId, int projectId, Invitation invitation)
{
// This line triggered the problem by loading a company, without
// eagerly loading the contacts.
CheckAuthorizationEdit(companyId, CommunicationService.GetById(id));
var dbResponse = InvitationService.GetPreviousResponse(companyId, projectId);
dbResponse.WillBid = invitation.WillBid;
InvitationService.Save(dbResponse);
return RedirectToAction("Response", new { id, companyId } );
}
private void CheckAuthorizationEdit(int companyId, Communication communication)
{
var companyIds = communication.DistributionList.Companies.Select(c => c.Id).ToList();
//CheckAuthorization(companyIds);
}
}
public class InvitationService
{
public Invitation GetPreviousResponse(int companyId, int projectId)
{
return (from invitation in _db.Invitations
where invitation.ProjectId == projectId && invitation.SenderCompanyId == companyId
select invitation).SingleOrDefault();
}
public void Save(Invitation invitation)
{
_db.SaveChanges();
}
}
public class Invitation
{
public int Id { get; set; }
public int ProjectId { get; set; }
[ForeignKey("ProjectId")]
public virtual Project Project { get; set; }
// ...
}
public class Project
{
public int Id { get; set; }
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
public virtual Company Company { get; set; }
// ...
}
public class Company
{
public Company()
{
MainContact = new Contact();
}
public int Id { get; set; }
public virtual Contact MainContact { get; set; }
// ...
}
public class Contact
{
public int Id { get; set; }
public string AddressLine1 { get; set; }
// ...
}
答案 0 :(得分:4)
如果我理解正确,你会有这样的事情:
public class Company
{
public Company()
{
MainContact = new Contact();
}
public int Id { get; set; }
public virtual Contact MainContact { get; set; }
}
像这样的简单代码......
var company = context.Companies.Find(1);
context.SaveChanges();
...确实会在数据库中创建一个新的空联系人。
我要得出的主要结论是:不要在构造函数中实例化引用导航属性!(实际上,只要你将内容留空,实例化导航集合就可以了。也可以实例化构造函数中复杂类型的属性很好,因为它们不是其他实体。)
如果您想确保与新公司建立新联系人,可能Company
类中的静态工厂方法是更好的选择:
public static Company CreateNewCompany()
{
return new Company { MainContact = new Contact() };
}
这也有效:
var company = context.Companies.Find(1);
context.Entry(company.MainContact).State = EntityState.Detached;
context.SaveChanges();
但是这样的程序看起来很荒谬。
编辑:
通过自动更改检测的方式导致行为。这段代码......
context.Configuration.AutoDetectChangesEnabled = false;
var company = context.Companies.Find(1);
context.SaveChanges();
...不会创建新联系人。这是在 SaveChanges
Find
内部进行的更改检测,它认为将MainContact
中的company
标识为新实体并将其放入{{} 1}}陈述进入上下文。