Reputation: 4954
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
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