I had a question more detailed earlier which I had no answer, I will have the same question with a simpler way:
I have an EF database with foreign key to another table.
I would like to UPDATE an ENTITY. But I need to this like this and I'll write the codes below:
Go to database and retrieve the Member by id, return EF Member object
Do some changes on the object OUTSIDE the EF Context
Send the MODIFED EF Member into a Save method
In BL layer save method uses the context and save changes.
1)
MemberManager currentMemberManager = new MemberManager();
Member NewMember = currentMemberManager.GetById(2);
2)
NewMember.FirstName = "NewFirstName";
NewMember.LanguageId = 1;
3)
currentMemberManager.Save(NewMember);
4)
public void Save2(Member newMember)
{
using (var Context = new NoxonEntities())
{
Member existingMember = Context.Member.First(c => c.Id == newMember.Id);
existingMember.FirstName = newMember.FirstName;
existingMember.Language = Context.Language.First(c => c.Id == newMember.LanguageId);
Context.SaveChanges();//In here I get the error below
}
}
The changes to the database were committed successfully, but an error
occurred while updating the object context. The ObjectContext might be
in an inconsistent state. Inner exception message: A referential
integrity constraint violation occurred: The property values that
define the referential constraints are not consistent between
principal and dependent objects in the relationship.
Note: You may suggest to SEND a different class (Ex: public class
MyMember) that has all the necessary properties and totally separated
from EF. But this requires much work to get all EF object converting
into my separate classes. Am I right?
I am hoping there is a way to Detach the entity just long enough for me to modify it and save the values into database. (Also, I tried the Detach method which updates no rows at all)
I've been trying to solve this for hours now.
Please, help me to understand it better, I really need a solution. Thank you so much to anyone how has some ideas.
Could you do something simple like detaching the entity, then attaching it to the context when you're ready to save?
MemberManager currentMemberManager = new MemberManager();
Member NewMember = currentMemberManager.GetById(2);
The get:
public Member GetById(int id)
{
var member = YourContext.Members.FirstOrDefault(m => m.id == id);
YourContext.Detach(member);
return member;
}
The save:
public void Save2(Member newMember)
{
using (var Context = new NoxonEntities())
{
Context.Attach(newMember);
Context.ObjectStateManager.ChangeObjectState(newMember, EntityState.Modified);
Context.SaveChanges();
}
}
Related
I'd like to understand why "The ObjectContext might be in an inconsistent state." exception occurs.
I know that there are multiple threads related to similar issue but none seems to answer my question. (Closest to my problem is: The changes to the database were committed successfully...The ObjectContext might be in an inconsistent state )
I've got Database First model containing tables:
Training (PK Id)
User (PK Id)
UserTraining(PK (UserId, TrainingId), FK(UserId), FK(TrainingId))
Documentation(PK Id, FK TrainingId)
UserDocumentation (PK (UserId, TrainingId, DocumentationId),
FK(UserId, TrainingId), FK(DocumentationId))
All above tables are properly mapped in code(all keys are present etc.).
What I'm doing is - Create Training with Documentation, then Create UserTraining with UserDocumentation, like below:
var type = this.trainingRepository.GetTrainingType(command.Training.TypeId);
var training = new Training(
type,
command.Training.Name,
command.Training.Documentation,
...);
if (type.Equals(TrainingType.Mandatory))
{
var users = this.userRepository.GetAllUsers();
foreach(var user in users)
{
user.AssignTraining(training);//under the hood is created new instance of UserTraining with relation to training and UserDocumentation related to UserTraining and Documentation from training
}
}
trainingRepository.Add(training);
trainingRepository.SaveChanges();
There are 2 repositories but each uses the same instance of DBContext(registered as PerRequest).
Now if I move adding and saving changes above if (type.Equals(TrainingType.Mandatory)) and add one move SaveChanges at the end of method everything passes without any issues.
Unfortunately in case above it throws exception mentioned in title.
Now the question - Why does adding in the middle and then updating work and my solution doesn't? Can it be related to poor database schema or rather EF limitations?
AssignTraining method:
public void AssignTraining(Training training)
{
this.State.UserTrainings.Add(new UserTraining(training));
}
UserTraining constructor:
public UserTraining(Training training) : base(new
DataAccess.Database.UserTraining())
{
this.State.Training = training;
if(training.Documentation != null)
{
this.State.UserDocumentations.Add(new UserDocumentation(training.Documentation));
}
}
Update: Ahh! Dumb error: I had two instances of a Repository available, and, our implemntation requires that we provide a connection string for each Repo, so the two Repos were pointing to different databases, and I was adding an entity from one DB to another, and its id probably could not be found in the updated DB.
I'm running .Net MVC 4.0, with EF 5. We implement a repository pattern. In the following Delete method of a WebApi controller I have the following code:
int userID = UserHelper.GetCurrentUserID();
DateTime now = DateTime.UtcNow;
ExhibitLinkRepository el = new ExhibitLinkRepository();
el.setCase = caseID;
ExhibitLink link = el.All.SingleOrDefault(l => l.id == id);
//Mark Link Deleted
link.usermodified_id = userID;
link.datetimemodified = now;
link.deleted_flag = true;
exhibitLinkRepository.InsertOrUpdate(link);
exhibitLinkRepository.SaveChanges();
Where exhibitLinkRepository.InsertOrUpdate:
public void InsertOrUpdate(ExhibitLink exhibitLink)
{
if (exhibitLink.id == default(int))
{
// New entity
context.ExhibitLinks.Add(exhibitLink);
}
else
{
// Existing entity
context.Entry(exhibitLink).State = EntityState.Modified;
}
}
When I invoke context.SaveChanges() I get the dreaded:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
Now, the ExhibitLink table has foreign key constraints as follows:
There happen to be triggers on the underlying DB for some of these related tables, but disabling them did not change the outcome.
I just don't get it. Any ideas?
I'm using Entity Framework 6, Code First approach. I'll try to present my problem with a simple piece of code:
public void ViewEntity(MyEntity Entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
Db.MyEntities.Attach(Entity); // Exception
}
}
The exception message is: Attaching an entity of type 'MyProgram.MyEntity' failed because another entity of the same type already has the same primary key value.
From what I've read on MSDN it's an expected behaviour. But what I want on that last line is to first check if there is an entity with the same key already attached to a context; if it is, use it instead, and only otherwise attach my entity to context.
But I've failed to find a way to do so. There are many utility methods on ObjectContext instance (for example GetObjectByKey). I can't test them all 'cause they all ultimately need a qualifiedEntitySetName, and I don't have any in my real imlpementation, because this method should be on an abstract class and it should work for all entity types. Calling Db.Entity(this) is no use, there is no EntityKey which would have EntitySetName.
So all of this became complex really fast. And in my terms I just want to check if the object is already in "cache" (context), use it, otherwise use my object and attach it to this context.
To be clear, I have a detached object from a TreeNode.Tag in the first place, and I just want to use it again, or if it's impossible; if there already is one in the context), use that one instead. Maybe I'm missing some crucial concepts of EF6, I'm just starting out with EF.
I've found a solution for me. As I guessed correctly ObjectContext.GetObjectByKey method does what I need, but first I needed to construct qualifiedEntitySetName, and I found a way to do so. A tad bit cumbersome (using reflection, iterating properties of MyDbContext), but does not compare to a headache of a problem I made out of all this. Just in case, here's the patch of code that is a solution for me:
public SdsAbstractObject GetAttachedToContext()
{
var ObjContext = (SdsDbContext.Current as IObjectContextAdapter).ObjectContext;
var ExistingItem = ObjContext.GetObjectByKey(GetEntityKey()) as SdsAbstractObject;
if (ExistingItem != null)
return ExistingItem;
else
{
DbSet.Attach(this);
return this;
}
}
public EntityKey GetEntityKey()
{
string DbSetName = "";
foreach (var Prop in typeof(SdsDbContext).GetProperties())
{
if (Prop.PropertyType.IsGenericType
&& Prop.PropertyType.GenericTypeArguments[0] == ObjectContext.GetObjectType(GetType()))
DbSetName = Prop.Name;
}
if (String.IsNullOrWhiteSpace(DbSetName))
return null;
else
return new EntityKey("SdsDbContext." + DbSetName, "Id", Id);
}
An Entity can be in one of five stages : Added, Unchanged, Modified, Deleted, Detached.
public void ViewEntity(MyEntity entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
// Set the Modified state of entity or you can write defensive code
// to check it before set the state.
if (Db.Entry(entity).State == EntityState.Modified) {
Db.Entry(entity).State = EntityState.Modified
}
// Attached it
Db.MyEntities.Attach(Entity);
Db.SaveChanges();
}
}
Since EF doesn't know which properties are different from those in the database, it will update them all.
I am trying to write a GenericEFRepository which will be used by other Repositories. I have a Save method as below.
public virtual void Save(T entity) // where T : class, IEntity, new() And IEntity enforces long Id { get; set; }
{
var entry = _dbContext.Entry(entity);
if (entry.State != EntityState.Detached)
return; // context already knows about entity, don't do anything
if (entity.Id < 1)
{
_dbSet.Add(entity);
return;
}
var attachedEntity = _dbSet.Local.SingleOrDefault(e => e.Id == entity.Id);
if (attachedEntity != null)
_dbContext.Entry(attachedEntity).State = EntityState.Detached;
entry.State = EntityState.Modified;
}
You can find the problem in comments of below code
using (var uow = ObjectFactory.GetInstance<IUnitOfWork>()) // uow is implemented like EFUnitOfWork which gives the DbContext instance to repositories in GetRepository
{
var userRepo = uow.GetRepository<IUserRepository>();
var user = userRepo.Get(1);
user.Name += " Updated";
userRepo.Save(user);
uow.Save(); // OK only the Name of User is Updated
}
using (var uow = ObjectFactory.GetInstance<IUnitOfWork>())
{
var userRepo = uow.GetRepository<IUserRepository>();
var user = new User
{
Id = 1,
Name = "Brand New Name"
};
userRepo.Save(user);
uow.Save();
// NOT OK
// All fields (Name, Surname, BirthDate etc.) in User are updated
// which causes unassigned fields to be cleared on db
}
The only solution I can think of is creating Entities via repository like userRepo.CreateEntity(id: 1) and repository will return an Entity which is attached to DbContext. But this seems error prone, still any developer may create an entity using new keyword.
What are your solution suggestions about this particular problem?
Note: I already know about cons and pros of using a GenericRepository and an IEntity interface. So, "Don't use a GenericRepository, don't use an IEntity, don't put a long Id in every Entity, don't do what you are trying to do" comments will not help.
Yes it is error prone but simply that is the problem with EF and repositories. You must either create entity and attach it before you set any data you want to update (Name in your case) or you must set modified state for each property you want to persist instead of whole entity (as you can imagine again developer can forget to do that).
The first solution leads to special method on your repository doing just this:
public T Create(long id) {
T entity = _dbContext.Set<T>().Create();
entity.Id = id;
_dbContext.Set<T>().Attach(entity);
return entity;
}
The second solution needs something like
public void Save(T entity, params Expression<Func<T, TProperty>>[] properties) {
...
_dbContext.Set<T>().Attach(entity);
if (properties.Length > 0) {
foreach (var propertyAccessor in properties) {
_dbContext.Entry(entity).Property(propertyAccessor).IsModified = true;
}
} else {
_dbContext.Entry(entity).State = EntityState.Modified;
}
}
and you will call it like:
userRepository(user, u => u.Name);
This is kind of a fundamental problem of this approach because you expect the repository to magically know which fields you changed and which ones you didn't. Using null as a signal for "unchanged" does not work in case null is a valid value.
You'd need to tell the repository which fields you want to have written, for example sending a string[] with the field names. Or one bool for each field. I do not think this is a good solution.
Maybe you can invert the control flow like this:
var entity = repo.Get(1);
entity.Name += "x";
repo.SaveChanges();
That would allow change tracking to work. It is closer to how EF wants to be used.
Alternative:
var entity = repo.Get(1);
entity.Name += "x";
repo.Save(entity);
While the other two answers provide good insight into how perhaps you can avoid this issue I think its worth pointing out a couple of things.
What you are trying to do (ie a proxy entity update) is extremely EF-centeric and IMO actually doesn't make sense outside of the EF context and hence it doesnt make sense that a generic repository would be expected to behave in this way.
You actually haven't even gotten the flow quite right for EF, if you attach an object with a few fields already set EF will conciser what you told it to be the current DB state unless you modify a value or set a modified flag. To do what you are attempting without a select you would normally attach an object without the name and then set the name after attaching the ID object
Your approach is normally used for performance reasons, I would suggest that by abstracting over the top of an existing framework you are almost always going to suffer some logical performance degradation. If this is a big deal maybe you shouldn't be using a repository? The more you add to your repository to cater to performance concerns the more complex and restrictive it becomes and the harder it gets to provide more than one implementation.
All that being said I do think you can handle this particular case in a generic situation.
This is one possible way you could do it
public void UpdateProperty(Expression<Func<T,bool>> selector, FunctionToSetAProperty setter/*not quite sure of the correct syntax off the top of my head*/)
{
// look in local graph for T and see if you have an already attached version
// if not attach it with your selector value set
// set the property of the setter
}
Hope this makes some sense, I'm not by my dev box atm so I cant really do a working sample.
I think this is a better approach for a generic repository as it allows you to implement this same behavior in multiple different ways, the abovc may work for EF but there will be different methods if you have an in memory repository (for example). This approach allows you to implement different implementations that fulfill the intent rather than restrict your repository to only act like EF.
I am trying to not have my Business Logic know the inner workings of my Data Layer and vica versa.
But Entity Framework is making that hard. I can insert into a collection (in my Business Layer) without a reference to the ObjectContext:
order.Containers.Add(new Container { ContainerId = containerId, Order = order });
And that saves fine when it comes time to do a SaveChanges() in the Data Layer.
But to delete an item from a collection I need a reference to the ObjectContext. (I am case #1 in this guide to deleting EF Entities.) If I just do this:
delContainers.ForEach(container => order.Containers.Remove(container));
Then when I call SaveChanges() I get an exception telling me that I need to delete the object as well as the reference.
So, my options as I see it are:
To pass a delegate to my Business Logic that will call the Entity Framework ObjectContext Delete method.
Or (I am hoping) find a way to get all entities that have had their reference deleted and actually delete them. (Right before calling SaveChanges() in my data layer.)
Does anyone know a way to do that?
UPDATE:
I tried this:
// Add an event when Save Changes is called
this.ObjectContext.SavingChanges += OnSavingChanges;
...
void OnSavingChanges(object sender, EventArgs e)
{
var objectStateEntries = ObjectContext.ObjectStateManager
.GetObjectStateEntries(EntityState.Deleted);
foreach (var objectStateEntry in objectStateEntries)
{
if (objectStateEntry.IsRelationship)
{
// Find some way to delete the related entity
}
}
}
But none even though I deleted a relationship, the set of deleted items is empty.
(I tried viewing all the items too and my relationship is not in there. Clearly there is something fundamental that I don't get about ObjectStateManager.)
The correct solution for EF is point 3. from the linked article. It means propagating FK to principal entity into PK for dependent entity. This will form something called identifying relation which automatically deletes dependent entity when it is removed from the parent entity.
If you don't want to change your model and still want to achieve that in persistence ignorant way you probably can but it will work only for independent associations. Some initial implementation which works at least for my simple tested solution:
public partial class YourObjectContext
{
public override int SaveChanges(SaveOptions options)
{
foreach (ObjectStateEntry relationEntry in ObjectStateManager
.GetObjectStateEntries(EntityState.Deleted)
.Where(e => e.IsRelationship))
{
var entry = GetEntityEntryFromRelation(relationEntry, 0);
// Find representation of the relation
IRelatedEnd relatedEnd = entry.RelationshipManager
.GetAllRelatedEnds()
.First(r => r.RelationshipSet == relationEntry.EntitySet);
RelationshipType relationshipType = relatedEnd.RelationshipSet.ElementType;
if (!SkipDeletion(relationshipType))
{
// Now we know that model is inconsistent and entity on many side must be deleted
if (!(relatedEnd is EntityReference)) // related end is many side
{
entry = GetEntityEntryFromRelation(relationEntry, 1);
}
if (entry.State != EntityState.Deleted)
{
context.DeleteObject(entry.Entity);
}
}
}
return base.SaveChanges();
}
private ObjectStateEntry GetEntityEntryFromRelation(ObjectStateEntry relationEntry, int index)
{
var firstKey = (EntityKey) relationEntry.OriginalValues[index];
ObjectStateEntry entry = ObjectStateManager.GetObjectStateEntry(firstKey);
return entry;
}
private bool SkipDeletion(RelationshipType relationshipType)
{
return
// Many-to-many
relationshipType.RelationshipEndMembers.All(
r => r.RelationshipMultiplicity == RelationshipMultiplicity.Many) ||
// ZeroOrOne-to-many
relationshipType.RelationshipEndMembers.Any(
r => r.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne);
}
}
To make it work your entities must be enabled for dynamic change tracking (all properties must be virtual and entity must be proxied) or you must manually call DetectChanges.
In case of foreign key associations the situation will be probably much worse because you will not find any deleted relation in the state manager. You will have to track changes to collections or keys manually and compare them to find discrepancies (I'm not sure how to do it in generic way) Foreign key association IMHO requires the identifying relation. Using FK properties already means that you included additional persistence dependency into your model.
One way is to write a change handler in your data layer:
private void ContainersChanged(object sender,
CollectionChangeEventArgs e)
{
// Check for a related reference being removed.
if (e.Action == CollectionChangeAction.Remove)
{
Context.DeleteObject(e.Element);
}
}
There are many places you can wire this up -- in your object's constructor or repository get or SavingChanges or wherever:
entity.Containers.AssociationChanged += new CollectionChangeEventHandler(ContainersChanged);
Now you can remove the association from elsewhere and it will "cascade" to the entity.