Alex Kibler
Alex Kibler

Reputation: 4954

EFCore Update isn't updating rows in the database

I've got a payload coming up from my client that is an object whose properties are primitives and other objects whose properties are also primitives.

For example:

public class MainObj
{
   [Key]
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public int id {get;set;}
   public string foo {get;set;}
   public OtherObj bar {get;set;}
}

public class OtherObj
{
   [Key]
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public int id {get;set;}      
   public int test {get;set;}
}

Once I validate the request, I get the object the user is trying to update:

var obj = _context.MainObjs.Select(x => new MainObj
{
     id = x.id,
     foo = x.foo,
     bar = x.bar
}).SingleOrDefaultAsync(x => x.id == request.id);

I'm doing the .Select because if I don't, then bar never gets populated.

Then I update the properties with what comes up from the client:

obj.foo = request.foo;
obj.bar = request.bar;

Then I try to save changes:

_context.SaveChangesAsync();

However, nothing is persisting in the database when I do this. What am I doing wrong? I've only worked with EF6 in the past, so I dunno if EFCore has something weird about updating objects with foreign key relationships. And to be clear, my actual objects have many more properties than this, but I don't believe that's the problem.

EDIT: I tried using .Include syntax instead of .Select , which looks like this:

var obj = _context.MainObjs.Include(x =>x.bar).SingleOrDefaultAsync(x => x.id == request.id);

but then I get an error that The instance of entity type cannot be tracked because another instance of this type with the same key is already being tracked.

EDIT2: Replacing all of that other code with simply _context.Update(request) is working. I'm curious why the other way isn't though.

Upvotes: 0

Views: 155

Answers (1)

Gert Arnold
Gert Arnold

Reputation: 109109

The statement ...

_context.MainObjs.Select(x => new MainObj { ... })

... doesn't attach anything to the context. It just creates a new MainObj object, but EF doesn't know about it. In EF6 it wasn't allowed to create entity types in EF LINQ queries, exactly to prevent this confusion.

So by using _context.MainObjs.Include(x =>x.bar), you do attach a MainObj and its bar to the context and its changes will be tracked.

Finally, the statement _context.Update(request) attaches request to the context and marks it as Modified, including its foreign key to its bar property.

Upvotes: 2

Related Questions