Entity Framework Update DbContext - entity-framework

I have a Repository project like this.
https://github.com/tugberkugurlu/GenericRepository/tree/master/src
I have a method.
public void Edit(TEntity entity)
{
_dbContext.SetAsModified(entity);
}
public void SetAsAdded<TEntity>(TEntity entity) where TEntity : class
{
DbEntityEntry dbEntityEntry = GetDbEntityEntrySafely(entity);
dbEntityEntry.State = EntityState.Added;
}
But I am getting while update record. I am getting sometimes this error.
Attaching an entity of type 'TP.Model' failed because
another entity of the same type already has the same primary key
value. This can happen when using the 'Attach' method or setting the
state of an entity to 'Unchanged' or 'Modified' if any entities in the
graph have conflicting key values. This may be because some entities
are new and have not yet received database-generated key values. In
this case use the 'Add' method or the 'Added' entity state to track
the graph and then set the state of non-new entities to 'Unchanged' or
'Modified' as appropriate.

I solved the problem like this. I checked the first columns. Afterwards, I did something like this.
_dbSet = dbContext.DbSet<TEntity>();
The rest is cake.
_dbSet.Attach(entity);
DbEntityEntry entry = _dbContext.Entry(entity);
foreach (var proprty in entry.OriginalValues.PropertyNames)
{
var Current = entry.CurrentValues.GetValue<object>(proprty);
var New = entry.GetDatabaseValues().GetValue<object>(proprty);
if (Current != null)
{
if (!object.Equals(New, Current))
{
entry.Property(proprty).IsModified = true;
}
}
}

Related

Perform action when an entity is updated in Entity Framework

Is there a way to run code when an entity is updated? For example I have an entity that is updated many places in my code. I want to be able to update a DateTimeUpdated field any time that entity is updated without changing every function that updates that entity.
This is typically done by overriding the SaveChanges method in the DbContext.
Start by introducing a base class or a common interface for your editable entities that you want to track the DateTimeUpdated for:
public abstract class EditableEntityBase
{
public DateTime DateTimeUpdated { get; internal set; }
}
Your entities that you want to track this for should extend this class or implement a contract interface that will expose the property.
Then in your DbContext, override the SaveChanges method and insert:
var updatedEntities = ChangeTracker.Entries()
.Where(x => x.State == EntityState.Modified)
.Select(x => x.Entity)
.OfType<EditableEntityBase>();
foreach (var entity in updatedEntities)
{
entity.DateTimeUpdated = DateTime.Now; // or DateTime.UtcNow
}
return base.SaveChanges();
You can also include x.State == EntityState.Added for new records, though generally I'd rely on a Default at the DB to capture it on insert.

Entity Framework - Replace child collection using a detached model

I have a many-to-many relationship between Entity A and Entity B. Entity Framework has automatically created a junction table in SQL Server after running the migration. (I don't have this junction table defined anywhere in the code.) For example:
class EntityA
{
// ...
public ICollection<EntityB> Foo { get; set; }
}
class EntityB
{
// ...
public ICollection<EntityA> Bar { get; set; }
}
I need to replace the Foo collection on EntityA using a (detached) list coming in from a client application. I've spent the better part of a day trying to figure this out. Here is what I've tried:
[HttpPut]
public async Task<IActionResult> Update(EntityA someEntity)
{
var entry = context.EntityA.Attach(someEntity);
entry.State = EntityState.Modified;
var collection = entry.Collection(x => x.Foo);
collection.IsModified = true;
await context.SaveChangesAsync();
}
I've also tried changing the CurrentValue property of collection, and obviously I've also tried replacing Foo directly, but nothing seems to work -- the junction table remains empty. How can this child list be entirely replaced without having to Include() / load the entire list into memory for manual tracking / removal?
Ivan (in the comments above) is right. After some trial and error, I ended up writing an extension method that works for my case. Before I get to that, I want to credit this answer for pointing me in the right direction, which I ended up modifying to get it working with auto-generated EF junction tables. First, the extension method:
// assuming your models inherit from a base class or implement an interface
public interface IEntity
{
Guid Id { get; set; } // or int or whatever your ID field is
}
public static class DbExtensions
{
// Updates the many-to-many child collections of an entity (for an auto-generated EF junction table)
public static async Task UpdateJunctionTableAsync<T, Y>(this DbContext baseContext, T entity, Expression<Func<T, IEnumerable<Y>>> property)
where T : class, IEntity
where Y : class, IEntity
{
// scope these calls to a new context -- working off the base context
// tends to cause issues down the line with the change tracking
using var context = new DbContext();
// EF internally compares with DB entities, so we'll do the same
var dbEntity = await context.FindAsync<T>(entity.Id);
var dbEntry = context.Entry(dbEntity);
// access the collection entry that resulted in a junction table
var dbItemsEntry = dbEntry.Collection(property);
// get its associated CLR collection accessor
var accessor = dbItemsEntry.Metadata.GetCollectionAccessor();
// load the entry's items
await dbItemsEntry.LoadAsync();
// build a dictionary to track what needs to be added vs removed
var dbItemsMap = dbItemsEntry.CurrentValue.ToDictionary(e => e.Id);
// get the current items in the entity (not DB)
var items = (IEnumerable<Y>)accessor.GetOrCreate(entity, false);
// add them to the DB as needed
foreach (var item in items)
{
// if this already exists, no need to process it.
if (dbItemsMap.ContainsKey(item.Id))
dbItemsMap.Remove(item.Id);
else
{
// otherwise, add a tracked version of it.
context.Set<Y>().Attach(item);
accessor.Add(dbEntity, item, false);
}
}
// anything still left here has been deleted from the entity
foreach (var oldItem in dbItemsMap.Values)
accessor.Remove(dbEntity, oldItem);
// we have to clear the junction table from the incoming model's collection,
// otherwise EF will try to attach to it again, which will cause errors
// further down the line
var memberSelectorExpression = property.Body as MemberExpression;
if (memberSelectorExpression != null)
{
var propertyInfo = memberSelectorExpression.Member as PropertyInfo;
if (propertyInfo != null)
propertyInfo.SetValue(entity, null, null);
}
await context.SaveChangesAsync();
}
}
Using this is simple:
[HttpPut]
public async Task<IActionResult> UpdateFoo(EntityA model)
{
// update the junction table first
await context.UpdateJunctionTableAsync(model, x => x.Foo);
// then update whatever else you want
// e.g., if we were updating the whole row:
// context.EntityA.Attach(model).State = EntityState.Modified;
// save
await context.SaveChangesAsync();
return Ok();
}

Attaching Entity to context fails because it already exist

I use the Unity of Work and Generic Repository of CodeCamper.
to update an entity, the generic repo has:
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
the web api method:
public HttpResponseMessage Put(MyEditModel editModel)
{
var model = editModel.MapToMyEntity();
_myManager.Update(model);
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
The Update method:
public void Update(MyEntity model)
{
Uow.MyEntities.Update(model);
Uow.Commit();
}
In the Unityof Work:
IRepository<MyEntity> MyEntities { get; }
When updating an entity I get the following error:
Additional information: Attaching an entity of type 'X' failed because another entity of the same type already has the same primary key value.
This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values.
This may be because some entities are new and have not yet received database-generated key values.
In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
The update works fine, when it is the first method you call of the repository.
(I created an entity with an id already in the DB and called the Update.)
The update doesn't work when you do a get of the entity before you update it.
(For example, I get an entity X, convert it to a DTO, then change some values in the UI,
then call a web api that creates an entity X with the new values and
call the Update of the repository.)
Any ideas to avoid this?
When you have a CRUD app, you always call the get before the update.
I'm using my own attach method:
public void Attach<E>(ref E entity)
{
if (entity == null)
{
return;
}
try
{
ObjectStateEntry entry;
bool attach = false;
if (ObjectStateManager.TryGetObjectStateEntry(CreateEntityKey(entitySetName, entity), out entry))
{
attach = entry.State == EntityState.Detached;
E existingEntityInCache = (E)entry.Entity;
if (!existingEntityInCache.Equals(entity))
{
existingEntityInCache.SetAllPropertiesFromEntity(entity);
}
entity = existingEntityInCache;
}
else
{
attach = true;
}
if (attach)
objectContext.AttachTo(entitySetName, entity);
}
catch (Exception ex)
{
throw new Exception("...");
}
}
I had the same issue. The problem was in mixed contexts. When you read entity from DB in context1. Then if you can update this entity with contex2 (other instance of the same context with own entity cache). This may throw an exception.
Pls check for references too:
by context1:
read entity1 with referenced entity2 from DB
by context2:
read entity2 from DB. Then update entity1 (with referenced entity2 from context1).
When you try attach entity1 with referenced entity2 to context2, this throw exception because entity2 already exists in context2.
The solution is use only one context for this operation.

Generic repository to update an entire aggregate

I am using the repository pattern to provide access to and saving of my aggregates.
The problem is the updating of aggregates which consist of a relationship of entities.
For example, take the Order and OrderItem relationship. The aggregate root is Order which manages its own OrderItem collection. An OrderRepository would thus be responsible for updating the whole aggregate (there would be no OrderItemRepository).
Data persistence is handled using Entity Framework 6.
Update repository method (DbContext.SaveChanges() occurs elsewhere):
public void Update(TDataEntity item)
{
var entry = context.Entry<TDataEntity>(item);
if (entry.State == EntityState.Detached)
{
var set = context.Set<TDataEntity>();
TDataEntity attachedEntity = set.Local.SingleOrDefault(e => e.Id.Equals(item.Id));
if (attachedEntity != null)
{
// If the identity is already attached, rather set the state values
var attachedEntry = context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(item);
}
else
{
entry.State = EntityState.Modified;
}
}
}
In my above example, only the Order entity will be updated, not its associated OrderItem collection.
Would I have to attach all the OrderItem entities? How could I do this generically?
Julie Lerman gives a nice way to deal with how to update an entire aggregate in her book Programming Entity Framework: DbContext.
As she writes:
When a disconnected entity graph arrives on the server side, the
server will not know the state of the entities. You need to provide a
way for the state to be discovered so that the context can be made
aware of each entity’s state.
This technique is called painting the state.
There are mainly two ways to do that:
Iterate through the graph using your knowledge of the model and set the state for each entity
Build a generic approach to track state
The second option is really nice and consists in creating an interface that every entity in your model will implement. Julie uses an IObjectWithState interface that tells the current state of the entity:
public interface IObjectWithState
{
State State { get; set; }
}
public enum State
{
Added,
Unchanged,
Modified,
Deleted
}
First thing you have to do is to automatically set the state to Unchanged for every entity retrieved from the DB, by adding a constructor in your Context class that hooks up an event:
public YourContext()
{
((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
var entity = args.Entity as IObjectWithState;
if (entity != null)
{
entity.State = State.Unchanged;
}
};
}
Then change your Order and OrderItem classes to implement the IObjectWithState interface and call this ApplyChanges method accepting the root entity as parameter:
private static void ApplyChanges<TEntity>(TEntity root)
where TEntity : class, IObjectWithState
{
using (var context = new YourContext())
{
context.Set<TEntity>().Add(root);
CheckForEntitiesWithoutStateInterface(context);
foreach (var entry in context.ChangeTracker
.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.State);
}
context.SaveChanges();
}
}
private static void CheckForEntitiesWithoutStateInterface(YourContext context)
{
var entitiesWithoutState =
from e in context.ChangeTracker.Entries()
where !(e.Entity is IObjectWithState)
select e;
if (entitiesWithoutState.Any())
{
throw new NotSupportedException("All entities must implement IObjectWithState");
}
}
Last but not least, do not forget to set the right state of your graph entities before calling ApplyChanges ;-) (You could even mix Modified and Deleted states within the same graph.)
Julie proposes to go even further in her book:
you may find yourself wanting to be more granular with the way
modified properties are tracked. Rather than marking the entire entity
as modified, you might want only the properties that have actually
changed to be marked as modified.
In addition to marking an entity as modified, the client is also
responsible for recording which properties have been modified. One way
to do this would be to add a list of modified property names to the
state tracking interface.
But as my answer is already too long, go read her book if you want to know more ;-)
My opinionated (DDD specific) answer would be:
Cut off the EF entities at the data layer.
Ensure your data layer only returns domain entities (not EF entities).
Forget about the lazy-loading and IQueryable() goodness (read: nightmare) of EF.
Consider using a document database.
Don't use generic repositories.
The only way I've found to do what you ask in EF is to first delete or deactivate all order items in the database that are a child of the order, then add or reactivate all order items in the database that are now part of your newly updated order.
So you have done well on update method for your aggregate root, look at this domain model:
public class ProductCategory : EntityBase<Guid>
{
public virtual string Name { get; set; }
}
public class Product : EntityBase<Guid>, IAggregateRoot
{
private readonly IList<ProductCategory> _productCategories = new List<ProductCategory>();
public void AddProductCategory(ProductCategory productCategory)
{
_productCategories.Add(productCategory);
}
}
it was just a product which has a product category, I've just created the ProductRepository as my aggregateroot is product(not product category) but I want to add the product category when I create or update the product in service layer:
public CreateProductResponse CreateProduct(CreateProductRequest request)
{
var response = new CreateProductResponse();
try
{
var productModel = request.ProductViewModel.ConvertToProductModel();
Product product=new Product();
product.AddProductCategory(productModel.ProductCategory);
_productRepository.Add(productModel);
_unitOfWork.Commit();
}
catch (Exception exception)
{
response.Success = false;
}
return response;
}
I just wanted to show you how to create domain methods for entities in domain and use it in service or application layer. as you can see the code below adds the ProductCategory category via productRepository in database:
product.AddProductCategory(productModel.ProductCategory);
now for updating the same entity you can ask for ProductRepository and fetch the entity and make changes on it.
note that for retrieving entity and value object of and aggregate separately you can write query service or readOnlyRepository:
public class BlogTagReadOnlyRepository : ReadOnlyRepository<BlogTag, string>, IBlogTagReadOnlyRepository
{
public IEnumerable<BlogTag> GetAllBlogTagsQuery(string tagName)
{
throw new NotImplementedException();
}
}
hope it helps

EF Entity not updated in database

This is the code that I have in my controller:
[HttpPost]
public ActionResult UpdateArticle(Article article)
{
if (ModelState.IsValid)
{
article.DateAdded =
this.articleRepository.GetSingle(article.ID).DateAdded;
article.DateModified = DateTime.Now;
this.articleRepository.Update(article);
return this.Redirect("~/Admin");
}
else
{
return this.Redirect("~/Admin/UnsuccessfulOperation");
}
}
From the view the data comes updated. I have a generic repository which handles the saving.
Update looks like this:
public virtual void Update(T entity)
{
//this.context.Entry(entity).State = EntityState.Modified;
this.context.SaveChanges();
}
If I uncomment the first line
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple
objects with the same key.
exception is thrown. When commented nothing is saved.
Any help is appreciated.
UPDATE
Ok the problem seems to be that the updated article is not "part" of the context so when I pass it to the update nothing happens. If I get the entity from the repository itself and pass the new values and after that pass this entity everything works as expected. This piece of code actually updates the date in the repository:
var art = this.articleRepository.GetSingle(article.ID);
art.Text = article.Text;
this.articleRepository.Update(art);
What I don't get is that this works too:
var art = this.articleRepository.GetSingle(article.ID);
art.Text = article.Text;
this.articleRepository.Update(article);
UPDATE 2
Thanks to Vitaliy I now know that attaching is the key, but when I try to attach the new entity I get the same ugly exception
An object with the same key already...
UPDATE 3
As I am not allowed to answer my own question in less than 8h I suppose I have to make another update.
Ok, so this is what I did in order to successfully detach the old and attach the new entity:
public virtual void Update(T entity, object id)
{
this.context.Entry(this.GetSingle(id)).State = EntityState.Detached;
this.context.Entry(entity).State = EntityState.Added;
this.context.Entry(entity).State = EntityState.Modified;
this.context.SaveChanges();
}
I will think of a better way to pass the ID, as it is already part of the entity, perhaps with and interface "myInterface" that has the ID property in it and T will be of type "myInterface".
Thanks a lot to Vitaliy.
You are updating article, which is not attached to Context, thus nothing will be saved.
Probably you intention was to change DateModified then you should do it like this:
public ActionResult UpdateArticle(Guid articleID)
{
if (ModelState.IsValid)
{
var article =
this.articleRepository.GetSingle(articleID);
article.DateModified = DateTime.Now;
this.articleRepository.Update(article);
return this.Redirect("~/Admin");
}
else
{
return this.Redirect("~/Admin/UnsuccessfulOperation");
}
}