Reputation: 8305
I came across this question, and liked how the generic update for one-to-many is implemented.
I tried to mimic it to implement a one-to-one version for myself but could not be totally successful. Following is the result of my struggle -
public async Task<int> UpdateAsync<T>(T entity, params Expression<Func<T, object>>[] navigations) where T : EntityBase
{
var dbEntity = await _DbCtx.FindAsync<T>(entity.Id);
var entry = _DbCtx.Entry(dbEntity);
entry.CurrentValues.SetValues(entry);
foreach (var nav in navigations)
{
string propertyName = nav.GetPropertyAccess().Name;
// Problem #01 //
// if possible, I'd like to avoid this reflection in favor of EF Core MetaData?
var child = (EntityBase)entity.GetType().GetProperty(propertyName).GetValue(entity);
if (child == null)
continue; // if the client-sent model doesn't have child, skip
var referenceEntry = entry.Reference(propertyName);
await referenceEntry.LoadAsync();
var dbChild = (EntityBase)referenceEntry.CurrentValue;
if (dbChild == null)
{
// Problem #02 //
// if the existing entity doesn't have child, the client-sent child will be assigned.
// but I could not figure out how to do this
}
else
{
_DbCtx.Entry(dbChild).CurrentValues.SetValues(child);
}
}
return await _DbCtx.SaveChangesAsync();
}
I have marked the problems as Problem #01
and Problem #02
in code comments above. Any suggestion, solution will be appreciated. Thanks.
Edit :
Alternatively, if you think there is a better, more efficient way of doing the same thing I'm trying to do above, please share your knowledge.
Upvotes: 0
Views: 345
Reputation: 118
For Problem #01, I couldn't find equivalent of GetCollectionAccessor
, but one way I can think of solving it using EF Core
metadata, would be calling the Entry
method in the disconnected Entity:
var entry = _context.Entry(dbEntity);
entry.CurrentValues.SetValues(entry);
var disconnectedEntry = _context.Entry(entity); // new
foreach (var nav in navigations)
{
string propertyName = nav.GetPropertyAccess().Name;
var navigationChild = disconnectedEntry.Navigation(propertyName).CurrentValue; // new
}
Bear in mind that in every Entry
method call EF Core
will try to DetectChanges. As this verification is not necessary for this Entity, you can save some performance disabling the AutoDetectChanges
as suggested in this issue.
Now for Problem #02, you can just assign the child Entity
to the ReferenceEntry
CurrentValue, like this:
var referenceEntry = entry.Reference(propertyName);
await referenceEntry.LoadAsync();
var dbChild = (EntityBase)referenceEntry.CurrentValue;
if (dbChild == null)
{
referenceEntry.CurrentValue = navigationChild; // new
}
else
{
_DbCtx.Entry(dbChild).CurrentValues.SetValues(child);
}
I hope it can help you. If some problem arises let me know!
I also suggest you to read about the TrackGraph method. Depending on how your entities work, maybe everything could be done with this method with a couple of lines.
Upvotes: 1