entity-framework - 什么会导致 Entity Framework 在现有数据上保存已卸载(但可延迟加载)的引用?

标签 entity-framework lazy-loading

我在使用 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/

相关文章:

使用 FindByIdAsync 时取消 ASP.Net Core Identity

c# - 为什么脚手架没有按预期工作?

c# - 在存储库中使用 Include() 方法

Angular 6 使用两个共享模块 : Component is not a known element:

java - 我可以在运行时更改 create SQL Query 的 Lazy 属性值吗?

hibernate - 如何在使用 Hibernate 映射的类中实现 toString()?

entity-framework - Entity Framework 与NHibernate-性能

c# - 每个层次结构的表和复合主键

java - 是否可以在 Java 8 中执行一个惰性 groupby,返回一个流?

database - 在 ORM 中, "lazy loading"是否意味着您可能无法从较大的列中获得预期的结果?