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

Issue with updating Owned Entities following DDD approach (EF Core 2.0) #10148

Closed
alktly opened this issue Oct 24, 2017 · 2 comments
Closed

Issue with updating Owned Entities following DDD approach (EF Core 2.0) #10148

alktly opened this issue Oct 24, 2017 · 2 comments

Comments

@alktly
Copy link

alktly commented Oct 24, 2017

Hello,

As title describes, we are experiencing issues with updating Owned Entities following DDD in EF Core 2.0.
According to DDD approach, Value Objects have to be immutable and due to this, we are getting following exception when we try to change/update our Value Objects with a new one.

Exception:

"The instance of entity type 'License.DeviceCapacity#LicenseDeviceCapacity' cannot be tracked because another instance with the same key value for {'LicenseKey'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values."

Steps to reproduce

In order to better explain the issue, I am sharing the code sample with you.

License Entity Object:

public class License
{
    public static License Create(string licenseKey, int period, int capacity, int? capacityToShow = null)
    {
        return new License
        {
            DeviceCapacity = LicenseDeviceCapacity.Create(capacity, capacityToShow),
            LicenseKey = licenseKey,
            Period = period
        };
    }

    protected License()
    {
    }

    public string LicenseKey { get; private set; }
    public int Period { get; private set; }
    public LicenseDeviceCapacity DeviceCapacity { get; private set; }

    public License ChangePeriod(int period)
    {
        Period = period;

        return this;
    }

    public License ChangeCapacity(int capacity)
    {
        DeviceCapacity = LicenseDeviceCapacity.Create(capacity, DeviceCapacity.CapacityToShow);

        return this;
    }
}

LicenseDeviceCapacity Value Object (owned entity - by license):

public class LicenseDeviceCapacity : ValueObject
{
    public static LicenseDeviceCapacity Create(int capacity, int? capacityToShow)
    {
        return new LicenseDeviceCapacity { Capacity = capacity, CapacityToShow = capacityToShow };
    }

    protected LicenseDeviceCapacity()
    { }

    public int Capacity { get; private set; }
    public int? CapacityToShow { get; private set; }
}

LicenseDBContext Object:

public class LicenseDBContext : DbContext
{
    public DbSet<License> Licenses { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<License>()
           .HasKey(l => l.LicenseKey);

        modelBuilder.Entity<License>()
        .OwnsOne(l => l.DeviceCapacity,
           capacity =>
           {
               capacity.Property(c => c.Capacity)
           .IsRequired();
           });

        base.OnModelCreating(modelBuilder);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Data Source=.;Initial Catalog=LicenseDBTest;Integrated Security=True");
    }
}

LicenseRepository which we use for CRUD operations on License Entity Object:

public class LicenseRepository
{
    private readonly LicenseDBContext context;

    public LicenseRepository(LicenseDBContext context)
    {
        this.context = context;
    }

    public async Task CreateAsync(License license)
    {
        await context.Licenses.AddAsync(license);
        await context.SaveChangesAsync();
    }

    public async Task UpdateAsync(License license)
    {
        await context.SaveChangesAsync();
    }

    public async Task<License> FindByIdAsync(string licenseKey)
    {
        return await context.Licenses.FindAsync(licenseKey);
    }
}

Lastly as an example, when we try to update License's DeviceCapacity with following code, we are getting aforementioned exception.

var license = licenseRepository.FindByIdAsync("A").Result;
license.ChangeCapacity(100);
licenseRepository.UpdateAsync(license).Wait();

P.S. In whole project, we are using LicenseDBContext Object as Singleton.

When we try to reproduce exact same scenario with EntityFramework 6.1.3, we do not encounter any problem and everything goes smoothly. The problem is only with Entity Framework Core 2.0.

As far as we are concerned, this is a bug on EF Core, and until this is fixed, could you be kind enough to help us by share with us any possible temporary workarounds?

Further technical details

EF Core version: Microsoft.EntityFrameworkCore 2.0.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer 2.0.0
Operating system: Microsoft Windows 7 or higher
IDE: Visual Studio 2017

@smitpatel
Copy link
Contributor

Duplicate of #7340
Also see #9803

@equanimity
Copy link

The same issue is in EF Core 2.1 with InMemory xunit test

You can not get from the context (in memory database) an entity, change something and update with the same context. You get the famous error: The instance of entity type '....' cannot be tracked because another instance with the same key value for...

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
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

4 participants