I am trying to do a simple update to the foreign key but the script never get sent over.
Here is the code I am using:
using (var db = new MyContext()
This is what I ended up building till the late hours of the morning, chould/should be refactored some more...
protected static async Task<int> SaveEntity<t>(t obj) where t : BaseModel
{
try
{
using (DatabaseContext db = GetDbContext())
{
//get the basemodel/fk reference properties
IEnumerable<PropertyInfo> props = obj.GetType().GetProperties().Where(p => p.PropertyType.BaseType == typeof(BaseModel));
if (obj.Id <= 0)
{//insert
db.Entry(obj).State = EntityState.Added;
//set fk reference props to unchanged state
foreach (PropertyInfo prop in props)
{
Object val = prop.GetValue(obj);
if (val != null)
{
db.Entry(val).State = EntityState.Unchanged;
}
}
//do insert
return await db.SaveChangesAsync();
}
else
{//update
//get the posted fk values, and set them to null on the obj (to avaid dbContext conflicts)
Dictionary<string, int?> updateFKValues = new Dictionary<string, int?>();
foreach (PropertyInfo prop in props)
{
BaseModel val = (BaseModel)prop.GetValue(obj);
if (val == null)
{
updateFKValues.Add(prop.Name, null);
}
else
{
updateFKValues.Add(prop.Name, val.Id);
}
prop.SetValue(obj, null);
}
//dbContext creation may need to move to here as per below working example
t dbObj = (t)db.Set(typeof(t)).Find(new object[] { obj.Id }); //this also differs from example
//update the simple values
db.Entry(dbObj).CurrentValues.SetValues(obj);
//update complex values
foreach (PropertyInfo prop in props)
{
Object propValue = null;
if (updateFKValues[prop.Name].HasValue)
{
propValue = (BaseModel)db.Set(prop.PropertyType).Find(new object[] { updateFKValues[prop.Name] });
}
prop.SetValue(dbObj, propValue);
if (propValue != null)
{
db.Entry(propValue).State = EntityState.Unchanged;
}
}
//do update
return await db.SaveChangesAsync();
}
}
}
catch (Exception ex)
{
ExceptionHelper.Log(ex);
throw;
}
}
This solution worked for me when needing to update the main object and it's foreign key objects attached.
public virtual async Task<Result<TEntity>> Update<TEntity>(TEntity entity) where TEntity : class
{
Result<TEntity> returnResult = new Result<TEntity>();
try
{
//get the fk reference properties
IEnumerable<PropertyInfo> props = entity.GetType().GetProperties().Where(p => p.PropertyType.BaseType == typeof(object));
//set fk reference props to modified state
foreach (PropertyInfo prop in props)
{
Object val = prop.GetValue(entity);
if (val != null)
{
dbContext.Entry(val).State = EntityState.Modified;
}
}
dbContext.Entry(entity).State = EntityState.Modified;
await dbContext.SaveChangesAsync();
returnResult.SetSuccess(result: entity);
}
catch (Exception e)
{
log.Error(e);
returnResult.SetError(exception: e);
}
return returnResult;
}
Since you are working with independent association. You can either
Adding and removing the relationship from ContactList
, but you need to retrieve from both Person
.
db.Entry(newContact).State = EntityState.Modified;
var p1 = db.Set<Person>().Include(p => p.ContactList)
.FirstOrDefault(p =>p.Id == 1);
p1.ContactList.Remove(newContact);
var p3 = db.Set<Person>().Include(p => p.ContactList)
.FirstOrDefault(p => p.Id == 3);
p3.ContactList.Add(newContact);
db.SaveChanges();
Or you can use disconnected object, but you need to manually manage the relationship.
db.Entry(newContact).State = EntityState.Modified;
var p1 = new Person { Id = 1 };
db.Entry(p1).State = EntityState.Unchanged;
var p3 = new Person { Id = 3 };
db.Entry(p3).State = EntityState.Unchanged;
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
manager.ChangeRelationshipState(newContact, p1, item => item.ContactOwner,
EntityState.Deleted);
manager.ChangeRelationshipState(newContact, p3, item => item.ContactOwner,
EntityState.Added);
db.SaveChanges();
PS
You might need to reconsider adding foreign key value, to make everything easier, updating foreign key just by mentioning the Id.
See this post for more information.
Basically this happens because EntryState.Modified
just looks for scalar properties (primitive types) and with independent association (your case) you don't have it.
There is a several ways to achieve this, @Yuliam has pointed some of them and here you can find more.