In my MVC application, I have been using Repository pattern for DAL.
Now, when I do select one entity record and and update the entity field value and do Update operation then getting below error.
Attaching an entity of type 'DAL.User' 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."} System.Exception
Below is repository stuff:
public void Update(TEntity entity)
{
if (_context.Entry(entity).State != EntityState.Modified)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
Calling as follow:
In Bussines layer library:
Manager class :
private readonly IUnitOfWork _unitOfWork;
private IRepository <User , int> UserRepository
{
get
{
return _unitOfWork.GetRepository<AccountUser, int>();
}
}
public void UpdateUserEntity(UserDTO u)
{
try
{
User model = new User ();
UserRepository.Update(Mapper.Map(u, model));
_unitOfWork.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
Please guide me how I could resolve above error.
The exception says that there is another entity with the same key that has been attached, but different reference.
The exception could be caused by previous attached entity.
db.Set<Entity>().Attach(new Entity { Id = 123 });
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
Or could be also caused by tracked entity that automatically attached.
db.Set<Entity>().FirstOrDefault(e => e.Id == 123); // automatically attached
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
The second cause can be solved by mentioning AsNoTracking when retrieving item.
db.Set<Entity>().AsNoTracking().FirstOrDefault(e => e.Id == 123);
Or to be safe you can use this extension to always detach any attached entity.
public static class DbSetExtension
{
public static void SafeAttach<T>(
this DbContext context,
T entity,
Func<T, object> keyFn) where T : class
{
var existing = context.Set<T>().Local
.FirstOrDefault(x => Equals(keyFn(x), keyFn(entity)));
if (existing != null)
context.Entry(existing).State = EntityState.Detached;
context.Set<T>().Attach(entity);
}
}
Usage.
db.SafeAttach(entity, e => e.Id);
It's because of the reason,
"TEntity entity as a new object instead of the one which already exists".
Means,Entity framework treats each new object as new entry.(eventhough with same existing old data,PK & all).
Solution is,
First retrieve the object from database
Do/assign the changes to the same object (preferably without changing Primary key)
Then do state as Modified ,Update,SaveChange()
Related
I want to update an existing entity object from another model. But each time I got a new object (Having the mapped properties and default values for other properties.) Instead, I want a partially updated destination object.
AutoMapper.Mapper.Initialize(cfg => cfg.CreateMap<Customer, MYPOCO>().ReverseMap());
public void UpdateEntity(Customer customerSrc)
{
MYPOCO pocoDesc= dbContext.DD_POCO.SingleOrDefault(m => m.Id == 123);
pocoDesc = AutoMapper.Mapper.Map<Customer, MYPOCO>(customerSrc, pocoDesc);
// Here "pocoDesc" is a new object, I got only "customerSrc" data and lost all other existing properties values.
}
Automapper: 6.2.2(version)
Tried Automapper: Update property values without creating a new object
Any Idea?
if it still issue, try the following :
public void UpdateEntity(Customer customerSrc)
{
MYPOCO pocoDesc= dbContext.DD_POCO.SingleOrDefault(m => m.Id == 123);
AutoMapper.Mapper.Map<Customer, MYPOCO>(customerSrc, pocoDesc);
dbContext.DD_POCO.Update(pocoDesc);
dbContext.Save();
}
This blow code for my post edit action :
public ActionResult EditProduct(EditProductModel viewModel,HttpPostedFileBase file)
{
if (Session["AdminId"] != null && Session["AdminName"] != null)
{
repository = new Repository();
var pro = repository.FindProductById(viewModel.Id);
// Automappper configoration.
var config = new MapperConfiguration(cgf => cgf.CreateMap<EditProductModel, Product>());
var mapper = config.CreateMapper();
pro = mapper.Map<Product>(viewModel);
repository.UpdateProduct(pro);// Error
repository.SaveChanges();
return RedirectToAction("ShowProduct",new{id = AdminId});
}
return RedirectToAction("AdminLogin");
}
The repository.UpdateProduct() Containing this code:
public void UpdateProduct(Product obj)
{
entities.Entry(obj).State = EntityState.Modified;
}
But this top code throws this error :
Attaching an entity of type 'MobileShop.Models.Product' 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.
How I can solve it?
Your pro object is already loaded from context, and it is tracked by EF. Changing it's state to modified in Update method is redundant, and hence giving this error.
So you can either skip the code in your Update method that changes the state,
OR comment out the following lines, because you are anyway building your entity from viewModel
repository = new Repository();
var pro = repository.FindProductById(viewModel.Id);
As a rule; if an object has been loaded from context, dont attach it
And if it has been created outside the context, set its state to modified.
A good read - https://msdn.microsoft.com/en-us/data/jj592676.aspx
Add this lines for updating and saving:
using (var Context = new MobileDatabase_1_Entities())
{
Context.Entry(pro).State = EntityState.Modified;
Context.SaveChanges();
}
I created my own ContextProvider, sub classed from EFContextProvider. In BeforeSaveEntity I am running some business logic to validate the transaction. I need the updates to be "all or nothing", so if the 3rd entity in the collection fails the validation, the entire batch should be discarded, even though Ive already returned "true" for the first 2 entities.
I have a class level property thats getting set when any entity fails. In the final check in BeforeSaveEntities I can get the value of the flag.
I think this is where I can abort the update, but not sure how. Do I clear the map? Or throw an error?
Also, I will need to re-query the DB for my validation routines. I've read some posts that talk about creating a 2nd instance of the context to do the querying for the current values. Is there some docs on doing this, or gotchas I need to be aware of?
thanks
In your BeforeSaveEntities call you can throw an EntityErrorsException: Here is an example where we throw an exception if there is attempt to save any "Order" objects within a save bundle:
[HttpPost]
public SaveResult SaveWithEntityErrorsException(JObject saveBundle) {
ContextProvider.BeforeSaveEntitiesDelegate = ThrowEntityErrorsException;
return ContextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> ThrowEntityErrorsException(Dictionary<Type, List<EntityInfo>> saveMap) {
List<EntityInfo> orderInfos;
if (saveMap.TryGetValue(typeof(Order), out orderInfos)) {
var errors = orderInfos.Select(oi => {
return new EntityError() {
EntityTypeName = typeof(Order).FullName,
ErrorMessage = "Cannot save orders with this save method",
ErrorName = "WrongMethod",
KeyValues = new object[] { ((Order) oi.Entity).OrderID },
PropertyName = "OrderID"
};
return new EFEntityError(oi, "WrongMethod", "Cannot save orders with this save method", "OrderID");
});
var ex = new EntityErrorsException("test of custom exception message", errors);
// if you want to see a different error status code use this.
// ex.StatusCode = HttpStatusCode.Conflict; // Conflict = 409 ; default is Forbidden (403).
throw ex;
}
return saveMap;
}
And you should use BeforeSaveEntities exclusively instead of BeforeSaveEntity as your save logic becomes more complicated.
I had a requirement to perform server side calculations on entities that had been changed on the client - without saving - and get the results back to the client. The solution based on Breeze named saves that I came up with could be useful in this situation too.
I added the following method to the base class for my Breeze controllers.
protected SaveResult OverrideSaveChanges(JObject saveBundle, Action<List<object>> action, bool shouldSave = false)
{
var saveChangesDelegate = new SaveChangesOverride(action, shouldSave);
return saveChangesDelegate.Execute(saveBundle, ContextProvider);
This allows concrete controllers to implement named saves very simply. The saveBundle plus an Action<List<object>> are passed into the OverrideSaveChanges method. The action can make whatever modifications to the entities that are required and those changes will be propagated back to the client. The objects in the list are the entities that the client recognized as having changes and sent down to the server for the named save. Optionally, you could pass a shouldSave argument with a value of true to have the entities saved - the default is false.
OverrideChanges delegates to SaveChangesOverride for most of the heavy lifting.
public class SaveChangesOverride
{
public SaveChangesOverride(Action<List<object>> action, bool shouldSave = false)
{
Action = action;
ShouldSave = shouldSave;
}
private readonly Action<List<object>> Action;
private readonly bool ShouldSave;
public List<object> Entities;
public SaveResult Execute(JObject saveBundle, ContextProvider contextProvider)
{
contextProvider.BeforeSaveEntitiesDelegate = OnBeforeSaveEntities;
contextProvider.SaveChanges(saveBundle);
return new SaveResult
{
Entities = Entities,
KeyMappings = new List<KeyMapping>()
};
}
private Dictionary<Type, List<EntityInfo>> OnBeforeSaveEntities(Dictionary<Type, List<EntityInfo>> arg)
{
Entities = arg.SelectMany(x => x.Value).Select(x => x.Entity).ToList();
Action(Entities);
if (!ShouldSave)
{
return new Dictionary<Type, List<EntityInfo>>();
}
return arg;
}
}
Although we have access to all of the changed entities in the saveBundle actually performing the modifications in OnBeforeSaveChanges allows us to work with entities rather than a JObject.
Also, contextProvider.SaveChanges must be called regardless of whether we wish to have the entities saved. This is what triggers OnBeforeSaveEntities to be called. To ensure that the entities are not saved despite calling SaveChanges (if that is what is desired), rather than returning arg from OnBeforeSaveEntities, an empty dictionary is returned.
To ensure that the changes make it back to the client, a reference to the entities is saved in OnBeforeSaveEntities. This is used in Execute to prepare a SaveResult that is populated with the modified entities.
I have two sub-types of a super-type "Entity", namely "Household" and "Involved Body".
I've modeled them as shown below in my database and they were auto-generated to the EF Model (again shown below).
database
edmx model
Using the default scaffolding for MVC I am able to add a new Household without any problems. However, when I try to add a new Involved Body I hit an error when it tries to add the Entity Type.
There only relevant (as far as I can tell) difference between the two sub-types is that the EntityType for a Household is hard-coded as "Household" whereas the EntityType for an Involved Body can be any EntityType except "Household" - this is selected from a list by the user.
The Create Action on the HTTP POST for the Involved Body throws an error relating to the foreign key between tEntity and tEntityType with the tEntityType being null. Code as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude = "entityID")]tEntity tentity
, tInvolvedBody tinvolvedbody
, tAddress taddress
, tAddressEntity taddressentity
//, tEntityType tentitytype
, int entityTypeID
)
{
#region entity type
//find entity type from id
var tentitytype = db.tEntityTypes.Find(entityTypeID);
#endregion
#region address
//assume start date of involved body not needed for reporting
taddressentity.startDate = DateTime.Now.Date;
#endregion
if (ModelState.IsValid)
{
db.tEntities.Add(tentity);
db.tInvolvedBodies.Add(tinvolvedbody);
db.tAddresses.Add(taddress);
db.tAddressEntities.Add(taddressentity);
db.SaveChanges();
return RedirectToAction("Index");
}
//recreate viewbag for entityType dropdown
var q = (
from e in db.tEntityTypes
where e.entityType != "Household"
select e
);
ViewBag.entityTypeID = new SelectList(q, "entityTypeID", "entityType");
return View(tinvolvedbody);
}
I've tried adding the tEntityType to the parameters list for the create but this results in the ModelState.IsValid returning false due to the entityType being null on all the objects.
I've also tried actively linking the entity type to each of the other objects using:
tentity.tEntityType = tentitytype;
tinvolvedbody.tEntity.tEntityType = tentitytype;
taddressentity.tEntity.tEntityType = tentitytype;
The above ends up working but it creates a new Entity for each of the other objects i.e. I get three new rows in my tEntity table, one is the Entity, one links to tInvolvedBody and one links to tAddressEntities. This makes no sense...
How can I insert a new InvolvedBody that creates an Entity, picks up the Entity Type and then links to the AddressEntity junction table?
Finally worked through this. Not sure if the answer is 'perfect' from a developer perspective but it works.
After intense debugging I realised that the navigation properties for the involved body and address entity were both looking for an entitytypeID which I had assumed would be provided by the entity object.
If I passed these in directly with the code shown:
tinvolvedbody.tEntity.tEntityType = tentitytype;
taddressentity.tEntity.tEntityType = tentitytype;
...I ended up with three new entitites and no relational data existing between all of {entity, involved body, address}
The code that works removes the explicit addition of a new entity and relies on EF to create an entity from the Involved Body. I then used the newly created entityID to map the address via addressentity as follows:
[HttpPost]
public ActionResult Create([Bind(Exclude="entityID")]tEntity tentity
,tInvolvedBody tinvolvedbody
,tAddress taddress
,tAddressEntity taddressentity
,int entityTypeID
)
{
#region entity type
var t =
(
from e in db.tEntityTypes
where (e.entityTypeID == entityTypeID)
select e
);
tinvolvedbody.tEntity.tEntityType = t.First();
#endregion
#region address
//assume start date of involved body not needed for reporting
taddressentity.startDate = DateTime.Now.Date;
#endregion
if (ModelState.IsValid)
{
db.tInvolvedBodies.Add(tinvolvedbody);
db.tAddresses.Add(taddress);
taddressentity.tEntity = db.tEntities.Find(tinvolvedbody.bodyID);
db.tAddressEntities.Add(taddressentity);
db.SaveChanges();
return RedirectToAction("Index");
}
Have you tried setting the typeID specifically? Also, from what I gathered from your model, the taddress is a child of taddressentity? As such, should it not be inserted first in order for the foreign key?
if (ModelState.IsValid)
{
tentity.entityTypeID = entityTypeID;
db.tEntities.Add(tentity);
tinvolvebody.bodyID= tentity.entityID
db.tInvolvedBodies.Add(tinvolvedbody);
taddressentity.entityID = tentity.entityID;
db.tAddressEntities.Add(taddressentity);
taddress.UPRN = taddressentity.UPRN;
db.tAddresses.Add(taddress);
db.SaveChanges();
return RedirectToAction("Index");
}
Is it possible to get the EntityKey and type of an entity's parent entity without knowing the type? I've tried doing the following
public partial class TestEntities
{
partial void OnContextCreated()
{
this.SavingChanges += new EventHandler(logChanges);
}
void logChanges(object sender, EventArgs e)
{
IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(
EntityState.Added |
EntityState.Deleted |
EntityState.Modified);
TestEntities context = sender as TestEntities;
foreach (ObjectStateEntry stateEntryEntity in changes)
{
if (!stateEntryEntity.IsRelationship && stateEntryEntity.Entity != null)
{
Audit audit = new Audit
{
AuditID = Guid.NewGuid()
};
foreach (var relationship in stateEntryEntity.RelationshipManager.GetAllRelatedEnds())
{
var parent = stateEntryEntity.RelationshipManager.GetRelatedCollection<EntityObject>(relationship.RelationshipName, relationship.TargetRoleName);
audit.Decription =
string.Format("{0} changed on {1} with id of {2}",stateEntryEntity.Entity, parent.GetType().Name);
}
context.AddToAudits(audit);
}
}
}
}
But I get the following.
An EntityCollection of EntityObject objects could not be returned for role name
'Canine' in relationship 'TestModel.FK_CANINE'. Make sure that the
EdmRelationshipAttribute that defines this relationship has the correct
RelationshipMultiplicity for this role name. For more information, see the
Entity Framework documentation.
I'm wondering if maybe I'm approaching this the worng way.
After search it isn't feasible to do what I wanted with EF. I wanted to crawl the inheritance tree for Auditing purposes. I ended up creating an Auditor interface that logs an audit message. I created Auditor implementations for the each EF entity type I wanted to audit. When the calling code asked for a class that implemented the Auditor interface I used named mappings from Microsoft Unity to inject the concrete class or avoid auditing if no mapping was found.