question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Update owned types in SQLite throws an exception

See original GitHub issue

I’m updating two owned types using In-Memory Database Provider and works fine. When I use SQLite Database Provider I’m getting an exception.

Steps to reproduce

Add-Migration Initial -Verbose Update-Database -Verbose Set Copy to output Directory to Copy if newer for newly created MyDatabase.db Run the program

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace OwnedTypesTest
{
    public class Material
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<MaterialLocation> MaterialLocations { get; set; }

        private Material() { }
        public Material(string name, IEnumerable<MaterialLocationDto> materialLocationDtos)
        {
            Name = name;
            MaterialLocations = materialLocationDtos?.Select(ml => new MaterialLocation(this, ml.Location, ml.RewardAmount, ml.TransportationRewardAmount, ml.TransportationRewardMinimumAmount)).ToList();
        }

        public void Update(string name, IEnumerable<MaterialLocationDto> materialLocationDtos)
        {
            Name = name;
            MaterialLocations = materialLocationDtos?.Select(ml => new MaterialLocation(this, ml.Location, ml.RewardAmount, ml.TransportationRewardAmount, ml.TransportationRewardMinimumAmount)).ToList();
        }
    }

    public class MaterialLocationDto
    {
        public Location Location { get; set; }
        public decimal? RewardAmount { get; set; }
        public decimal? TransportationRewardAmount { get; set; }
        public int? TransportationRewardMinimumAmount { get; set; }
    }

    public class Location
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<MaterialLocation> MaterialLocations { get; set; }

        private Location() { }
        public Location(string name, IEnumerable<LocationMaterialDto> locationMaterialDtos)
        {
            Name = name;
            MaterialLocations = locationMaterialDtos?.Select(lm => new MaterialLocation(lm.Material, this, lm.RewardAmount, lm.TransportationRewardAmount, lm.TransportationRewardMinimumAmount)).ToList();
        }

        public void Update(string name, IEnumerable<LocationMaterialDto> locationMaterialDtos)
        {
            Name = name;
            MaterialLocations = locationMaterialDtos?.Select(lm => new MaterialLocation(lm.Material, this, lm.RewardAmount, lm.TransportationRewardAmount, lm.TransportationRewardMinimumAmount)).ToList();
        }
    }

    public class LocationMaterialDto
    {
        public Material Material { get; set; }
        public decimal? RewardAmount { get; set; }
        public decimal? TransportationRewardAmount { get; set; }
        public int? TransportationRewardMinimumAmount { get; set; }
    }

    public class MaterialLocation
    {
        public int MaterialId { get; protected set; }
        public Material Material { get; protected set; }
        public int LocationId { get; protected set; }
        public Location Location { get; protected set; }
        public Reward Reward { get; protected set; }
        public TransportationReward TransportationReward { get; protected set; }

        private MaterialLocation() { }
        public MaterialLocation(Material material, Location location, decimal? rewardAmount, decimal? transportationRewardAmount, int? transportationRewardMinmumAmount)
        {
            Material = material;
            Location = location;
            Reward = new Reward(rewardAmount);
            TransportationReward = new TransportationReward(transportationRewardAmount, transportationRewardMinmumAmount);
        }
    }

    // Owned type
    public class Reward
    {
        public decimal? Amount { get; protected set; }

        private Reward() { }
        public Reward(decimal? amount)
        {
            Amount = amount;
        }
    }

    // Owned type
    public class TransportationReward
    {
        public decimal? Amount { get; protected set; }
        public int? MinimumAmount { get; protected set; }

        private TransportationReward() { }
        public TransportationReward(decimal? amount, int? minimumAmount)
        {
            Amount = amount;
            MinimumAmount = minimumAmount;
        }
    }

    public class MyDbContext : DbContext
    {
        public DbSet<Material> Materials { get; set; }
        public DbSet<Location> Locations { get; set; }
        public DbSet<MaterialLocation> MaterialLocations { get; set; }

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

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MaterialLocation>().HasKey(ml => new { ml.MaterialId, ml.LocationId });
            modelBuilder.Entity<MaterialLocation>().OwnsOne(ml => ml.Reward);
            modelBuilder.Entity<MaterialLocation>().OwnsOne(ml => ml.TransportationReward);
        }
    }

    // Used for SQLite initial migration 
    public class MyDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
    {
        public MyDbContext CreateDbContext(string[] args)
        {
            DbContextOptionsBuilder<MyDbContext> optionBuilder = new DbContextOptionsBuilder<MyDbContext>();

            optionBuilder.EnableSensitiveDataLogging();
            optionBuilder.UseSqlite("Data Source=MyDatabase.db");

            return new MyDbContext(optionBuilder.Options);
        }
    }

    class Program
    {
        static async Task Main()
        {
            ServiceCollection services = new ServiceCollection();

            //services.AddDbContext<MyDbContext>(options => options.UseInMemoryDatabase("MyDatabase"));
            services.AddDbContext<MyDbContext>(options => options.UseSqlite("Data Source=MyDatabase.db").EnableSensitiveDataLogging());

            using (ServiceProvider serviceProvider = services.BuildServiceProvider())
            {
                int locationId, material1Id, material2Id;

                using (IServiceScope scope = serviceProvider.CreateScope())
                {
                    MyDbContext context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
                    Material material1 = new Material("Material1", null);
                    Material material2 = new Material("Material2", null);
                    Location location = new Location("Location1",
                        new LocationMaterialDto[]
                        {
                            new LocationMaterialDto { Material = material1, RewardAmount = 10.23M, TransportationRewardAmount = 5.25M, TransportationRewardMinimumAmount = 10 },
                            new LocationMaterialDto { Material = material2 },
                        });

                    context.Materials.Add(material1);
                    context.Materials.Add(material2);
                    context.Locations.Add(location);

                    context.SaveChanges();

                    locationId = location.Id;
                    material1Id = material1.Id;
                    material2Id = material2.Id;
                }

                using (IServiceScope scope = serviceProvider.CreateScope())
                {
                    MyDbContext context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
                    Location location = await context.Locations.Include(m => m.MaterialLocations).ThenInclude(ml => ml.Material).FirstOrDefaultAsync(l => l.Id == locationId);
                    Material material1 = await context.FindAsync<Material>(material1Id);
                    Material material2 = await context.FindAsync<Material>(material2Id);

                    foreach (MaterialLocation materialLocation in location.MaterialLocations)
                    {
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Reward amount before changing: {materialLocation.Reward?.Amount}");
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Transportation reward amount before changing: {materialLocation.TransportationReward?.Amount}");
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Transportation reward minimum amount before changing: {materialLocation.TransportationReward?.MinimumAmount}");
                    }

                    Console.WriteLine();

                    LocationMaterialDto[] locationMaterialDtos = new LocationMaterialDto[]
                    {
                        new LocationMaterialDto
                        {
                            Material = material1,
                            RewardAmount = location.MaterialLocations[0].Reward?.Amount,
                            TransportationRewardAmount = location.MaterialLocations[0].TransportationReward?.Amount,
                            TransportationRewardMinimumAmount = location.MaterialLocations[0].TransportationReward?.MinimumAmount
                        },
                        new LocationMaterialDto
                        {
                            Material = material2,
                            RewardAmount = 7.88M,
                            TransportationRewardAmount = 1.22M,
                            TransportationRewardMinimumAmount = 2
                        }
                    };

                    location.Update(location.Name, locationMaterialDtos);

                    foreach (MaterialLocation materialLocation in location.MaterialLocations)
                    {
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Reward amount before saving changes: {materialLocation.Reward.Amount}");
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Transportation reward amount before saving changes: {materialLocation.TransportationReward.Amount}");
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Transportation reward minimum amount before saving changes: {materialLocation.TransportationReward.MinimumAmount}");
                    }

                    Console.WriteLine();

                    await context.SaveChangesAsync();

                    foreach (MaterialLocation materialLocation in location.MaterialLocations)
                    {
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Reward amount after saving changes: {materialLocation.Reward.Amount}");
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Transportation reward amount after saving changes: {materialLocation.TransportationReward.Amount}");
                        Console.WriteLine($"({materialLocation.Location.Name} {materialLocation.Material.Name}) - Transportation reward minimum amount after saving changes: {materialLocation.TransportationReward.MinimumAmount}");
                    }

                    Console.WriteLine();
                    Console.ReadLine();
                }
            }
        }
    }
}

Expected

In-Memory Database Provider

(Location1 Material1) - Reward amount before changing: 10.23
(Location1 Material1) - Transportation reward amount before changing: 5.25
(Location1 Material1) - Transportation reward minimum amount before changing: 10
(Location1 Material2) - Reward amount before changing:
(Location1 Material2) - Transportation reward amount before changing:
(Location1 Material2) - Transportation reward minimum amount before changing:

(Location1 Material1) - Reward amount before saving changes: 10.23
(Location1 Material1) - Transportation reward amount before saving changes: 5.25
(Location1 Material1) - Transportation reward minimum amount before saving changes: 10
(Location1 Material2) - Reward amount before saving changes: 7.88
(Location1 Material2) - Transportation reward amount before saving changes: 1.22
(Location1 Material2) - Transportation reward minimum amount before saving changes: 2

(Location1 Material1) - Reward amount after saving changes: 10.23
(Location1 Material1) - Transportation reward amount after saving changes: 5.25
(Location1 Material1) - Transportation reward minimum amount after saving changes: 10
(Location1 Material2) - Reward amount after saving changes: 7.88
(Location1 Material2) - Transportation reward amount after saving changes: 1.22
(Location1 Material2) - Transportation reward minimum amount after saving changes: 2

SQLite Database Provider

System.InvalidOperationException
  HResult=0x80131509
  Message=The instance of entity type 'Reward' with the key value '{MaterialLocationMaterialId: 1, MaterialLocationLocationId: 1}' is marked as 'Added', but the instance of entity type 'MaterialLocation' with the key value '{MaterialId: 1, LocationId: 1}' is marked as 'Modified' and both are mapped to the same row.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.AddEntry(IUpdateEntry entry)
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.CreateModificationCommands(IList`1 entries, IUpdateAdapter updateAdapter, Func`1 generateParameterName)
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.<BatchCommands>d__11.MoveNext()
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.<ExecuteAsync>d__10.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.<ExecuteAsync>d__10.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<SaveChangesAsync>d__95.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<SaveChangesAsync>d__93.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__54.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at OwnedTypesTest.Program.<Main>d__0.MoveNext() in C:\Users\stefan.simion\source\repos\OwnedTypesTest\OwnedTypesTest\Program.cs:line 224

Further technical details

EF Core version: 3.0.0-rc1.19456.14 Database provider: Microsoft.EntityFrameworkCore.InMemory and Microsoft.EntityFrameworkCore.Sqlite Target framework: .NET Core 3.0.100-rc1-014190 Operating system: Windows 10 IDE: Visual Studio 2019 16.2.5

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:5

github_iconTop GitHub Comments

5reactions
Neme12commented, Nov 1, 2019

I don’t get it. Nullable owned entity types are a new feature but when I simply assign to it to make it non-null, I get an exception…? This is the most basic usage I could think of. If this doesn’t work… then what does? Owned entity types are now completely unusable 😞 And it’s not like I have a choice in making them nullable - it didn’t used to be null, and now it is, and I can’t make it not null?

Is there really going to be no fix for this for 3.0? @AndriySvyryd

1reaction
aliensqueegeecommented, Oct 16, 2019

I just installed .NET Core 3.1.100-preview1-014459 and updated all EF Core packages to 3.1.0-preview1.19506.2. Run the program and everything is working fine now. Thanks @AndriySvyryd

Read more comments on GitHub >

github_iconTop Results From Across the Web

update method in sqlite didn't throw Exception?
I figure I'll try to update it and if it throws Exception then it means the rows don't exist and it will create...
Read more >
Breaking changes in EF Core 6.0
Changing the owner of an owned entity now throws an exception, Medium ; Azure Cosmos DB: Related entity types are discovered as owned,...
Read more >
Breaking changes included in EF Core 3.x
ToTable on a derived type throws an exception, Low. EF Core no longer sends pragma for SQLite FK enforcement, Low. Microsoft.
Read more >
Result and Error Codes
The SQLITE_READONLY result code is returned when an attempt is made to alter some data for which the current database connection does not...
Read more >
sqlite3 — DB-API 2.0 interface for SQLite databases
SQLite is a C library that provides a lightweight disk-based database that doesn't require a separate ... How to adapt custom Python types...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found