简体   繁体   中英

EF core 3.1.1 - owned entity proxy problem

I recently struggle with strange problem in EF core 3.1.1. We are trying to migrate from EF6 to EF core and Owned entities behave differently then Complex types in EF6. When owned entity is initialized by empty object then EF fails to detect change. This problem is occuring just against MSSQL, against "In memory" database everything seems to be fine.

public class OwnedEntityProxyTests
{
    [Fact]
    public void InMemoryTest()
    {
        Assert.True(Test(new DbContextOptionsBuilder<TestContext>()
            .UseInMemoryDatabase(databaseName: "Test")));
    }

    [Fact]
    public void MsSqlTest()
    {
        Assert.True(Test(new DbContextOptionsBuilder<TestContext>()
            .UseSqlServer("Data Source=.;MultipleActiveResultSets=True;Integrated Security=True")));
    }

    private static bool Test(DbContextOptionsBuilder<TestContext> builder)
    {
        var options = builder.UseLazyLoadingProxies().Options;
        using (var ctx = new TestContext(options))
        {
            ctx.TestEntities.RemoveRange(ctx.TestEntities.ToArray());
            ctx.TestEntities.Add(new TestEntity());
            ctx.SaveChanges();
        }

        using (var ctx = new TestContext(options))
        {
            var e = ctx.TestEntities.Single();
            e.TestOwnedEntity.Code = "test";
            return ctx.ChangeTracker.Entries().Any(o => o.State != EntityState.Unchanged);
        }
    }
}

public class TestContext : DbContext
{
    public TestContext() : base(new DbContextOptionsBuilder<TestContext>()
        .UseSqlServer("Data Source=.;MultipleActiveResultSets=True;Integrated Security=True")
        .UseLazyLoadingProxies()
        .Options)
    {
    }

    public TestContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<TestEntity> TestEntities { get; set; }
}

public class TestEntity
{
    public long Id { get; set; }
    public virtual TestOwnedEntity TestOwnedEntity { get; private set; } = new TestOwnedEntity();
}

[Owned]
public class TestOwnedEntity
{
    public string Code { get; set; }
}

In this example context should detect change. "In memory database" detects it correctly, but MS Sql provider fails to detect it. It is most likely caused by missing Proxy around owned type. Proxy is missing when all properties of owned type are default and without initialization the owned entity is null.

Could you please give me some tip how to setup it correctly? I don't want to remove default initialization of owned entities, it would force us to check everywhere if Owned entity is null or not.

whole project is on github

I believe you must implement method Equals for TestOwnedEntity , something like:

public override bool Equals(object obj)
{
    var other = obj as TestOwnedEntity;
    if (other == null) return false;
    if (object.ReferenceEquals(this, obj) return true;
    return other.Code == this.Code;
}

At the end they helped on EF core forum https://github.com/dotnet/efcore/issues/20213

public class TestEntity
{
    public long Id { get; set; }

    private TestOwnedEntity _testOwnedEntity;
    public virtual TestOwnedEntity TestOwnedEntity
    {
        get
        {
            if (_testOwnedEntity == null)
            {
                _testOwnedEntity = new TestOwnedEntity();
            }
            return _testOwnedEntity;
        }
        private set { _testOwnedEntity = value; }
    }
}

change in my test you can find here: https://github.com/PospisilBohumir/EfCoreTests/tree/FinalFix

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM