我在使用 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; }
// ...
}
最佳答案
如果我理解正确的话,你会得到这样的东西:
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
内部工作的变更检测,它会识别 company
中的 MainContact
code> 作为一个新实体,并将其置于上下文中的 Added
状态。
关于entity-framework - 什么会导致 Entity Framework 在现有数据上保存已卸载(但可延迟加载)的引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6779226/