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

Update of NULL owned entity throws - wants owned entity to be Added #17704

Closed
julielerman opened this issue Sep 8, 2019 · 2 comments
Closed

Comments

@julielerman
Copy link

I fear my github search skills are failing...this may already be known so sorry if its a dupe.

IN EF Core 3, we can now have NULL owned entity properties thanks to #9005. Yay thank you.

And it is possible (thanks to a change in an earlier version of EF Core) to replace an owned entity property. Yay thanks here also.

However if the owned entity property is NULL (thanks to the new capability) an attempt to update the property fails when calling SaveChanges. EF COre is looking for the owned type to be ADDED.

(I manually trimmed down unrelated code when pasting instead of creating a completely new project. Hope I didn't over-edit)

Exception has occurred: CLR/System.InvalidOperationException
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll: 'The entity of type 'PersonFullName' is sharing the table 'Employees' with entities of type 'Employee', but there is no entity of this type with the same key value that has been marked as 'Added'. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the key values.'
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.Validate(Dictionary`2 sharedTablesCommandsMap)
   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.Execute(DbContext _, ValueTuple`2 parameters)
   at Microsoft.EntityFrameworkCore.Storage.Internal.NoopExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
   at EFCore3DDD.Program.ReplaceEmployeeName() in d:\blahblah\Program.cs:line 34
   at EFCore3DDD.Program.Main(String[] args) in d:\blahblah\Program.cs:line 24

Steps to reproduce

Type to be owned:

public class PersonFullName {
  
  public static PersonFullName Create (string first, string last) {
    return new PersonFullName (first, last);
  }

  private PersonFullName (string first, string last) {
    First = first;
    Last = last;
  }
  public string First { get; private set; }
  public string Last { get; private set; }
}

Entity

using System;

namespace EFCore3DDD

{
  public class Employee
  { 
   public Employee(string first, string last, int companyId)
    {
      Name = PersonFullName.Create(first, last);
      CompanyId = companyId;
    }
     public Employee( int companyId)
    {
      
      CompanyId = companyId;

    }
    public int EmployeeId { get; set; }
    public PersonFullName Name { get; set; }
    public int CompanyId { get; set; }
  }
}

DbContext

using Microsoft.EntityFrameworkCore;
namespace EFCore3DDD
{
  public class CompanyContext : DbContext
  {
    public DbSet<Employee> Employees { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseSqlite("Data Source=DP_Companies.db");
      
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      modelBuilder.Entity<Employee>().OwnsOne(e=>e.Name);
    }
  }
}

Method that adds an employee without setting the Name owned entity, then retrieves employe ein a fresh context instance and attempts to set the name.

private static void AddNamelessEmployeeToCompanyThenUpdate()
    {
      using (var context = new CompanyContext())
      {
        context.Employees.Add(new Employee(2));
        context.SaveChanges();

      }
      using (var context = new CompanyContext())
      {
        var employee = context.Employees.FirstOrDefault(e => e.Name == null);
        employee.Name = PersonFullName.Create("Diego", "Vega");
        context.SaveChanges();
      }
    }


### Further technical details
EF Core version:  "3.0.0-preview9.19423.6"
Database Provider: (e.g. Microsoft.EntityFrameworkCore.Sqlite
Operating system: Windows 10
IDE: VS COde 1.38.0
@AndriySvyryd
Copy link
Member

Duplicate of #17422

@AndriySvyryd AndriySvyryd marked this as a duplicate of #17422 Sep 8, 2019
@julielerman
Copy link
Author

gosh I wish I'd found that! I'll toss the workaround into my article. Thanks Andriy!

@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

3 participants