Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EF core 3.1.1 - owned entity proxy problem #20213

Closed
PospisilBohumir opened this issue Mar 7, 2020 · 4 comments
Closed

EF core 3.1.1 - owned entity proxy problem #20213

PospisilBohumir opened this issue Mar 7, 2020 · 4 comments

Comments

@PospisilBohumir
Copy link

PospisilBohumir commented Mar 7, 2020

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

Further technical details

EF Core version: 3.1.2
Database provider: SqlServer
Target framework: core 3.1
Operating system: Windows

@AndriySvyryd
Copy link
Member

Instead of initializing it always set it in the getter if null.

Duplicate of #18007

@PospisilBohumir
Copy link
Author

it will not solve problem with proxy not capturing changes !

@AndriySvyryd
Copy link
Member

@PospisilBohumir The lazy loading proxies don't capture changes they just handle lazy loading. And if you make the change suggested above your test passes:

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; }
    }
}

@PospisilBohumir
Copy link
Author

it seems to be working ... thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants