Reputation: 15032
I've recorded a screencast with my problem, you can find it here, please look if you have time.
I have the following code that should perform AddOrUpdate functionality, but instead all existing records are recreated, so as a result I have several New Yorks, several USA's. I'm transferring EntityState from client, so that if data changed on client, the client updates EntityState property accordingly and sends it to server.
[HttpPost, HttpGet, HttpPut]
public HttpResponseMessage SaveRecord(RecordViewModel record)
{
var model = Mapper.Map<Record>(record);
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
db.Attach(model);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
return Request.CreateResponse(HttpStatusCode.OK, Mapper.Map<RecordViewModel>(model));
}
I'm attaching entities with the following function
public void AttachAndMarkAs<T>(T entity, EntityState state, Func<T, object> id) where T : class
{
var entry = Entry(entity);
if (entry.State == EntityState.Detached)
{
var set = Set<T>();
T attachedEntity = set.Find(id(entity));
if (attachedEntity != null)
{
var attachedEntry = Entry(attachedEntity);
if (state != EntityState.Unchanged)
{
attachedEntry.CurrentValues.SetValues(entity);
attachedEntry.State = state;
}
}
else
{
entry.State = state;
}
}
}
Which is relayed via the following ones:
public void Attach(City entity)
{
if (entity != null)
{
Attach(entity.Country);
AttachAndMarkAs(entity, entity.EntityState ?? EntityState.Added, instance => instance.Id);
}
}
public void Attach(Country entity)
{
if (entity != null)
{
AttachAndMarkAs(entity, entity.EntityState ?? EntityState.Added, instance => instance.Id);
}
}
I don't understand which part of the code handles Adding entities instead of Updating them, because the EntityState values are correct...
Upvotes: 2
Views: 9166
Reputation: 4738
You need to pay attention to how Entity Framework deal with child/referenced entities when you're changing the state of the parent entity programmatically.
Have a look at this article that summarizes how things are working in many different cases.
In the example below, you might think that all the referenced entities will automatically be set as Modifiied
:
using (var context = new BloggingContext())
{
context.Entry(existingBlog).State = EntityState.Modified;
// Do some more work...
context.SaveChanges();
}
But actually, they won't. As written on the article:
If you have multiple entities that need to be marked Modified you should set the state for each of these entities individually.
Finally, here is the way you need to handle the AddOrUpdate
specific case when your PrimaryKey
is an Int
:
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added : EntityState.Modified;
context.SaveChanges();
}
Upvotes: 3
Reputation: 2825
If you are using a simple int
for your id, you can use the following method.
public abstract class BaseEntity
{
public int Id { get; set; }
}
public void AddOrUpdate<T> (T entity) where T : BaseEntity
{
if(entity.Id > 0){
Entry(entity).State = EntityState.Modified;
}
else
{
Set<T>().Add(entity);
}
}
//
var model = Mapper.Map<Record>(record);
db.AddOrUpdate(model);
db.SaveChanges();
Upvotes: 3