Saving changes with Entity Framework causes detached entity duplicate - asp.net

I'm making a survey program that has a list of questions that are pulled from the database as entities. During the course of the survey the properties of the entity may be edited, but I do not want these changes persisted to the database. So, I detach the questions after they are loaded into the dbcontext and access them via a dictionary.
The problem arises when I attempt to save an answer to the database. Since the answer entity is related to the question entity (that has been detached) a completely new question entity is then added to the database. I'm not sure why this is occurring as the answer entity being added contains the proper ID of the detached entity (when I look at it in the debugger). However, upon saving changes it gets updated to the ID of the newly created duplicate entity.
Is there a way to stop this behavior? Or alternatively, is there a way to make changes to an entity and not have them persist to the database even when SaveChanges is called on the context for writing a answer or a log entry to the database?
Here is the code that loads, then detatches the questions (items):
public FixedLengthBlockElementRuntime(FixedLengthBlock block, RuntimeContext context) : base(block, context)
{
this._items = ((FixedLengthBlock)this.Element).ItemBank.Items.OfType<FixedLengthItem>().ToDictionary(x => x.ItemName, x => x);
foreach(FixedLengthItem fixedLengthItem in this._items.Values)
{
this.Context.DetachEntity(fixedLengthItem);
}
}
And here is the code that adds the duplicate entry:
public void SetResponse(Response response)
{
response.Session = this.Session;
this.DbContext.Responses.AddObject(response);
this.DbContext.SaveChanges();
}
Note that Response has the proper ItemId until the point of Saving Changes.

You can set the MergeOption on your questions DbSet to be OverwriteChanges.
Context.Questions.MergeOption = MergeOption.OverwriteChanges;
You'll need to be sure you set that BEFORE you make the query for the questions for this to work properly however.
More reading on how MergeOptions work

Related

Symfony2 - Doctrine2 store changeset for later (or alternative solution to approve changes)

I have several entities, each with its form type. I want to be able, instead of saving the entity straight away on save, to save a copy of the changes we want to perform and store it in DB.
We'd send a message to the user who can approve the change, who will review the original and the changed field(s) and will approve or not. If approved the entity would be properly flushed.
To solve the issue I was thinking about:
1) doing a persist
2) getting the changesets (both the one related to "normal" fields, and the one relative to collections)
3) storing it in DB
4) Performing $em->refresh() to discard changes.
Later what I need is to get the changset(s) back, ask the (other) user to approve it and flush it.
Is this doable? What I'm especially concerned about is that the entity manager that generated the first changeset is not the same we are going to use to perform the flush, I basically need to "load" a changeset.
Any idea on how to solve the issue (this way, or another way ;) )
Another solution (working only for "normal" fields, not reference ones that come from other entities to the current one, like a many to many) would be to clone the current entity, store it, and then once approved copy the field(s) from the cloned to the original one. But it does not work for all fields (if the previous solution does not work we'd limit the feature just to "normal" fields).
Thank you!
SN
Well, you could just treat the modifications as entities themselves, so that every change is stored in the database, and then all the changes that were approved are executed against the entity.
So, for example, if you have some Books stored in the database, and you want to make sure that all the modifications made to these are approved, just add a model that would contain the changeset that has to be processed, and a handler that would apply these changes:
<?php
class UpdateBookCommand
{
// If you'll store these commands in a database, perhaps this field would be a relation,
// or you could just store the ID
public $bookId;
public $newTitle;
public $newAuthor;
// Perhaps this field should be somehow protected from unauthorized changes
public $isApproved;
}
class UpdateBookHandler
{
private $bookRepository;
private $em;
public function handle(UpdateBookCommand $command)
{
if (!$command->isApproved) {
throw new NotAuthorizedException();
}
$book = $this->bookRepository->find($command->bookId);
$book->setTitle($command->newTitle);
$book->setAuthor($command->newAuthor);
$this->em->persist($book);
$this->em->flush();
}
}
Next, in your controller you would just have to make sure that the commands are somehow stored (in a database or maybe even in a message queue), and the handler gets called when the changesets could possibly get applied.
P.S. Perhaps I could have explained this a bit better, but mostly the inspiration for this solution comes from the CQRS pattern that's explained quite well by Martin Fowler. However, I guess in your case a full-blown CQRS implementation is unnecessary and a simpler solution should work.

When does objectify verify existing data in a related entity?

I am writing this out of my head now, so correct me if I'm wrong. But I think to remember that the following code verifies if the entity behind a Ref<> actually exists:
Ref<User> user; // we have a reference field to a user in another entity
public void referenceUser(Long userId) {
Key<User> key = Key.create(User.class, userId); // this is protobuf as it looks, and no db access happens
this.user = Ref.create(key); // this looks up if an existing record exists, and it fails if it does not, correct?
}
What happens if the Ref<> field is already set during an update? Is it correct that it is not verified again if the record still exists?
I would like to clarify when objectify ensures data integrity and when it does not cross-check if a referenced record exists.
Objectify never ensures any kind of referential integrity. Neither does the low-level API.
Ref objects are just Key objects with a little more behavior (most importantly the get() method).

How do you use optimistic concurrency with WebAPI OData controller

I've got a WebAPI OData controller which is using the Delta to do partial updates of my entity.
In my entity framework model I've got a Version field. This is a rowversion in the SQL Server database and is mapped to a byte array in Entity Framework with its concurrency mode set to Fixed (it's using database first).
I'm using fiddler to send back a partial update using a stale value for the Version field. I load the current record from my context and then I patch my changed fields over the top which changes the values in the Version column without throwing an error and then when I save changes on my context everything is saved without error. Obviously this is expected, the entity which is being saved has not been detacched from the context so how can I implement optimistic concurrency with a Delta.
I'm using the very latest versions of everything (or was just before christmas) so Entity Framework 6.0.1 and OData 5.6.0
public IHttpActionResult Put([FromODataUri]int key, [FromBody]Delta<Job> delta)
{
using (var tran = new TransactionScope())
{
Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);
delta.Patch(j);
this._context.SaveChanges();
tran.Complete();
return Ok(j);
}
}
Thanks
I've just come across this too using Entity Framework 6 and Web API 2 OData controllers.
The EF DbContext seems to use the original value of the timestamp obtained when the entity was loaded at the start of the PUT/PATCH methods for the concurrency check when the subsequent update takes place.
Updating the current value of the timestamp to a value different to that in the database before saving changes does not result in a concurrency error.
I've found you can "fix" this behaviour by forcing the original value of the timestamp to be that of the current in the context.
For example, you can do this by overriding SaveChanges on the context, e.g.:
public partial class DataContext
{
public override int SaveChanges()
{
foreach (DbEntityEntry<Job> entry in ChangeTracker.Entries<Job>().Where(u => u.State == EntityState.Modified))
entry.Property("Timestamp").OriginalValue = entry.Property("Timestamp").CurrentValue;
return base.SaveChanges();
}
}
(Assuming the concurrency column is named "Timestamp" and the concurrency mode for this column is set to "Fixed" in the EDMX)
A further improvement to this would be to write and apply a custom interface to all your models requiring this fix and just replace "Job" with the interface in the code above.
Feedback from Rowan in the Entity Framework Team (4th August 2015):
This is by design. In some cases it is perfectly valid to update a
concurrency token, in which case we need the current value to hold the
value it should be set to and the original value to contain the value
we should check against. For example, you could configure
Person.LastName as a concurrency token. This is one of the downsides
of the "query and update" pattern being used in this action.
The logic
you added to set the correct original value is the right approach to
use in this scenario.
When you're posting the data to server, you need to send RowVersion field as well. If you're testing it with fiddler, get the latest RowVersion value from your database and add the value to your Request Body.
Should be something like;
RowVersion: "AAAAAAAAB9E="
If it's a web page, while you're loading the data from the server, again get RowVersion field from server, keep it in a hidden field and send it back to server along with the other changes.
Basically, when you call PATCH method, RowField needs to be in your patch object.
Then update your code like this;
Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);
// Concurrency check
if (!j.RowVersion.SequenceEqual(patch.GetEntity().RowVersion))
{
return Conflict();
}
this._context.Entry(entity).State = EntityState.Modified; // Probably you need this line as well?
this._context.SaveChanges();
Simple, the way you always do it with Entity Framework: you add a Timestamp field and put that field's Concurrency Mode to Fixed. That makes sure EF knows this timestamp field is not part of any queries but is used to determine versioning.
See also http://blogs.msdn.com/b/alexj/archive/2009/05/20/tip-19-how-to-use-optimistic-concurrency-in-the-entity-framework.aspx

What's the proper use of $unitOfWork->getScheduledCollectionDeletions() in Doctrine 2 (and Symfony)?

I'm trying to detect changes in a many-to-many relation in an onFlush event.
If new entities are added to the relation or the relation is updated (always keeping an element), I can detect changes using $unitOfWork->getScheduledCollectionUpdates() and then check for getInsertDiff() or getDeleteDiff(). So far so good.
The problem comes when I take all the entities out of the relation: "There were two related entities before but there are NO related entities now."
When the relation is left empty I can access $unitOfWork->getScheduledCollectionDeletions(), but there is no way of knowing which entities were deleted:
getDeleteDiff() for this collections doesn't tell anything.
getSnapshot() doesn't tell me which entities were there before
How should I know which entities were taken out of the many-to-many relation?
I've added a Gist with the full implementation: everything works ok (it may need some optimization) except $uow->getScheduledCollectionDeletions() (line 101)
https://gist.github.com/eillarra/5127606
The cause of this problem is twofold:
1) When the method clear() is called on a Doctrine\ORM\PersistentCollection, it will:
clear its internal collection of entities.
call scheduleCollectionDeletion() on the Doctrine\ORM\UnitOfWork.
take a new snapshot of itself.
Number 2 is the reason your collection shows up in $uow->getScheduledCollectionDeletions() (and not in $uow->getScheduledCollectionUpdates()). Number 3 is the reason why you cannot determine what was in the collection before it was cleared.
2) When using the Symfony2 Form component, specifically the ChoiceType or CollectionType types in combination with the option multiple, that clear() method will get called when all entities should be removed from the collection.
This is due to the MergeDoctrineCollectionListener which is added here:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php#L55
This is done as optimization: It's faster to clear a collection this way, in stead of checking which entities should be removed from it.
I can think of two possible solutions:
1) Create a fork symfony/symfony and implement an option in order to not add the MergeDoctrineCollectionListener. Maybe something like no_clear to prevent the listener from being added. This won't introduce a BC break and would solve your problem because the clear() method of a collection won't get called when all entities should be removed.
2) Redesign your counter: Maybe also listen to the OnLoad event which can count the amount of entities in the collection at the time it's fetched from the db. That way your OnFlush listener can use that number to know how many entities where removed from the collection when it was cleared.
I found that if set 'by_reference' => false, option to EntityType form, then UnitOfWork detect changes of collection.
See difference state in UnitOfWork at OnFlush event:
'by_reference' => false
'by_reference' => true
In case the last item gets removed (like on a form submission), the "getDeleteDiff" sometimes turns out to be empty but in reality, items were there, the solution is to fetch the original data from the database. In my example we use a clone of the collection to achieve it. So the original collection stays untouched and everything still works.
public function onFlush(OnFlushEventArgs $args)
{
$uow = $args->getEntityManager()->getUnitOfWork();
foreach ($uow->getScheduledCollectionDeletions() as $collection) {
/**
* "getDeleteDiff" is not reliable, collection->clear on PersistentCollection also clears the original snapshot
* A reliable way to get removed items is: clone collection, fetch original data
*/
$removedData = $collection->getDeleteDiff();
if (!$removedData) {
$clone = clone $collection;
$clone->setOwner($collection->getOwner(), $collection->getMapping());
// This gets the real data from the database into the clone
$uow->loadCollection($clone);
// The actual removed items!
$removedData = $clone->toArray();
}
}
}
The reason the ->getDeleteDiff() is sometimes empty is because the "onSubmit" function of a form calls the ->clear() function on a PersistentCollection. And by clearing it, the original "snapshot" gets cleared too (for performance reasons I guess). And the "getDeleteDiff" function actually relies on that snapshot, but now it's empty.
There are multiple issues on Github about this problem:
https://github.com/doctrine/orm/issues/2272
https://github.com/doctrine/orm/issues/4173

Side effects of SessionFactory.Evict?

Every so often I am tasked with making alterations to one or more business entities in our database that may already be cached in our internal application. To get the application to reflect these changes without cycling the app pool, I figured I'd embed the ability for dev/administrators to evict the cache from within the app's UI (either entirely or for certain objects), but I noticed the comments for the method state the following...
/// <summary>
/// Evict an entry from the process-level cache. This method occurs outside
/// of any transaction; it performs an immediate "hard" remove, so does not respect
/// any transaction isolation semantics of the usage strategy. Use with care.
/// </summary>
void ISessionFactory.Evict(Type persistentClass, object id);
What exactly does that mean? What could go wrong if I try to evict one or more objects that may be involved in a transaction, and is there anyway to avoid these side effects if they are destructive? I'm currently using SysCache2 and am looking for implementation details on how to use SqlDependency but I'm still curious about the Evict effects in the mean time.
UPDATE: Upon looking closer at the comments, it appears that SessionFactory.Evict() and SessionFactory.EvictCollection() remove from the process level cache, and SessionFactory.EvictEntity() remove from the second level cache. However the same disclaimer exists on either flavor. So my original question still stands. What dangers are there of evicting an entity from the cache (process or second level) if it's currently in use in another transaction?
Evict detaches the entity from session.
private static Book CreateAndSaveBook(ISessionFactory sessionFactory)
{
var book = new Book()
{
Name = "Book1",
};
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
session.Save(book);
tx.Commit();
session.Evict(book);
}
}
return book;
}
In CreateAndSaveBook, we create a book and save it to the database. We commit our
transaction, evict the book from session, close the session, and return the book. This sets up our problem. We now have an entity without a session. Changes to this entity are not being tracked. It's just a plain ordinary book object.
We continue to change the book object, and now we want to save those changes. NHibernate
doesn't know what we've done to this book. It could have been passed through other layers or tiers of a large application. We don't know with which session it's associated, if any. We may not even know if the book exists in the database.

Resources