Can't flush second time after the first row is deleted - symfony

So I am basically trying to delete the existing row in my verification_code table and create new one. The deleting is fine but when the code reach $this>codeRepository->flush(); error is thrown:
A new entity was found through the relationship
'App\Entity\User#verificationCode' that was not configured to cascade
persist operations for entity: App\Entity\VerificationCode#374. To
solve this issue: Either explicitly call EntityManager#persist() on
this unknown entity or configure cascade persist this association in
the mapping for example #ManyToOne(..,cascade={"persist"}). If you
cannot find out which entity causes the problem implement
'App\Entity\VerificationCode#__toString()' to get a clue.
If I try to not flush when removing the old record, duplicate key error is thrown as expected.
#[Route(path: '/api/v1/resendVerificationCode', methods: 'POST')]
public function resendVerificationCode(Request $request):JsonResponse
{
$user = $this->userRepository->find($request->get('userId'));
$this->codeRepository->remove($user->getVerificationCode(), true);
$verificationCode = new VerificationCode();
$this->codeRepository->add($verificationCode);
$verificationCode->setUser($user);
$this->codeRepository->flush();
return new JsonResponse();
}

Related

How to replace EntityManager::merge in Doctrine 3?

I am working an Symfony 2.8 based web app project which currently uses Doctrine 2. The project is basically a simple ToDo list application which can be synced with a mobile app (iOS/Android).
While reading the Update notes of Doctrine 3 I discovered, that EntityManager::merge will no longer be supported.
An alternative to EntityManager#merge() is not provided by ORM 3.0,
since the merging semantics should be part of the business domain
rather than the persistence domain of an application. If your
application relies heavily on CRUD-alike interactions and/or PATCH
restful operations, you should look at alternatives such as
JMSSerializer.
I am not sure what is the best/correct way to replace EntityManager::merge?
Where do I use merge:
During the sync of the mobile apps with the web app the data is transferred as serialized JSON which is than de-serialized by JMSSerializer to an entity object. When the web app receives a ToDoEntry object this way, it can be a new ToDo-Entry (not known in the web app yet) or an updated existing entry. Either way, the received object is not managed by the EntityManager. Thus $em->persist($receivedObject) will always try to insert a new object. This will fail (due to the unique constraint of the id) if the ToDo-Entry already exists in the web app and needs to be updated.
Instead $em->merge($receivedObject) is used which automatically checks wether an insert or update is required.
Hot wo solve this?
Of course I could check for every received objects if an entity with the same ID already exists. In this case could load the existing object and update its properties manually. However this would be very cumbersome. The real project of course uses many different entities and each entity type/class would need its own handling to check which properties needs to be updated. Isn't there a better solution?
You can try to use registerManaged() method of Doctrine\ORM\UnitOfWork.
// $this->em <--- Doctrine Entity Manager
// $entity <--- detached Entity (and we know that this entity already exists in DB for example)
$id = [$entity->getId()]; //array
$data = $entity->toArray(); //array
$this->em->getUnitOfWork()->registerManaged($entity, $id, $data);
Of course, You can check the state of Your Entity using getEntityState() of Doctrine\ORM\UnitOfWork before/after perfoming needed actions.
$this->eM->getUnitOfWork()->getEntityState($entity, $assert = 3)
$assert <-- This parameter can be set to improve performance of entity state detection by potentially avoiding a database lookup if the distinction between NEW and DETACHED is either known or does not matter for the caller of the method.
While I have posted this question quite a while ago, it is still quite active. Until now my solution was to stick with Doctrine 2.9 and keep using the merge function. Now I am working on new project which should be Doctrine 3 ready and should thus not use the merge anymore.
My solution is of course specific for my special use case. However, maybe it is also useful for other:
My Solution:
As described in the question I use the merge method to sync deserialized, external entities into the web database where a version of this entity might already exist (UPDATE required) or not (INSERT required).
#Merge Annotation
In my case entities have different properties where some might be relevant for syncing and must be merged while others are only used for (web) internal housekeeping and must not be merged. To tell these properties appart, I have created a custom #Merge annotation:
use Doctrine\Common\Annotations\Annotation;
/**
* #Annotation
* #Target("PROPERTY")
*/
final class SyncMerge { }
This annotation is then be used to mark the entities properties which should be merged:
class ToDoEntry {
/*
* #Merge
*/
protected $date;
/*
* #Merge
*/
protected $title;
// only used internally, no need to merge
protected $someInternalValue;
...
}
Sync + Merge
During the sync process the annotation is used to merge the marked properties into existing entities:
public function mergeDeserialisedEntites(array $deserializedEntities, string $entityClass): void {
foreach ($deserializedEntities as $deserializedEntity) {
$classMergingInfos = $this->getMergingInfos($class);
$existingEntity = $this->entityManager->find($class, $deserializedEntity->getId());
if (null !== $existingEntity) {
// UPDATE existing entity
// ==> Apply all properties marked by the Merge annotation
foreach ($classMergingInfos as $propertyName => $reflectionProperty) {
$deserializedValue = $reflectionProperty->getValue($deserializedEntity);
$reflectionProperty->setValue($existingEntity, $deserializedEntity);
}
// Continue with existing entity to trigger update instead of insert on persist
$deserializedEntity = $existingEntity;
}
// If $existingEntity was used an UPDATE will be triggerd
// or an INSERT instead
$this->entityManager->persist($deserializedEntity);
}
$this->entityManager->flush();
}
private $mergingInfos = [];
private function getMergingInfos($class) {
if (!isset($this->mergingInfos[$class])) {
$reflectionClass = new \ReflectionClass($class);
$classProperties = $reflectionClass->getProperties();
$propertyInfos = [];
// Check which properties are marked by #Merge annotation and save information
foreach ($classProperties as $reflectionProperty) {
$annotation = $this->annotationReader->getPropertyAnnotation($reflectionProperty, Merge::class);
if ($annotation instanceof Merge) {
$reflectionProperty->setAccessible(true);
$propertyInfos[$reflectionProperty->getName()] = $reflectionProperty;
}
}
$this->mergingInfos[$class] = $propertyInfos;
}
return $this->mergingInfos[$class];
}
That's it. If new properties are added to an entity I have only to decide whether it should be merged or not and add the annotation if needed. No need to update the sync code.
Actually the code to handle this can be just a few lines. In background Doctrine will issue a query to search for your entity if not already in memory, so you can do the same by doing the query yourself with result cache enabled, and then just use PropertyAccessor to map the data.
https://symfony.com/doc/current/components/property_access.html
See this gist for a POC https://gist.github.com/stevro/99060106bbe54d64d3fbcf9a61e6a273

Linq-To-Sql SubmitChanges Not Updating Database

I've read multiple questions similar to this one but none are exactly my situation.
Using linq-to-sql I insert a new record and submit changes. Then, in the same web request, I pull that same record, and update it, then submit changes. The changes are not saved. The DatabaseContext is the same across both these operations.
Insert:
var transaction = _factory.CreateTransaction(siteId, userId, questionId, type, amount, transactionId, processor);
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.Amount = amount;
_transactionRepository.Add(transaction);
unitOfWork.Commit();
}
Select and Update:
ITransaction transaction = _transactionRepository.FindById(transactionId);
if (transaction == null) throw new Exception(Constants.ErrorCannotFindTransactionWithId.FormatWith(transactionId));
using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
{
transaction.CrmId = crmId;
transaction.UpdatedAt = SystemTime.Now();
unitOfWork.Commit();
}
Here's the unit of work code:
public virtual void Commit()
{
if (_isDisposed)
{
throw new ObjectDisposedException(GetType().Name);
}
_database.SubmitChanges();
}
I even went into the designer.cs file and put a breakpoint on the field that is being set but not updated. I stepped through and it entered and execute the set code, so the Entity should be getting "notified" of the change to this field:
public string CrmId
{
get
{
return this._CrmId;
}
set
{
if ((this._CrmId != value))
{
this.OnCrmIdChanging(value);
this.SendPropertyChanging();
this._CrmId = value;
this.SendPropertyChanged("CrmId");
this.OnCrmIdChanged();
}
}
}
Other useful information:
ObjectTracking is enabled
No errors or exceptions when second SubmitChanges is called (just silently fails update)
SQL profiler shows insert and select but not the subsequent update statement. Linq-To-Sql is not generating the update statement.
There is only one database, one database string, so the update is not going to another database
The table has a primary key.
I don't know what would cause Linq-To-Sql to not issue the update command and not raise some kind of error. Perhaps the problem stems from using the same DataContext instance? I've even refreshed the object from the database using the DataContact.Refresh method before it is pulled for the update, but that didn't help.
I have found what is likely to be the root cause. I am using Unity. The initial insert is being performed in a service class with a PerWebRequest lifetime. The select and update is happening in a class with a Singleton lifetime. So my assumption that the DataContext instances are the same was incorrect.
So, in my class with the Singleton lifetime, I get a fresh instance of the database repository and perform the update and no problem.
Now I still don't know why the original code didn't work and my approach could still be considered more a workaround than a solution, but it did solve my problem and hopefully will be useful to others.

Multiplicity constraint violated. The role '...' of the relationship '...' has multiplicity 1 or 0..1

I'm getting the following error from my DbContext: "Multiplicity constraint violated. The role 'MyEntity' of the relationship 'MyModel.FK_ChildEntities_MyEntities' has multiplicity 1 or 0..1."
using ASP.NET, Entity Framework 4
Working with a detached entity
The error happens the second time I try to reattach an entity to the dbcontext. The scenario is an unsuccessful save followed by a reattempt.
I have a detached entity in session. The user changes properties in a form, add things, removes things and finally clicks save. I get an attached copy of the entity from a new instance of the dbcontext, apply changes from the detached entity to the attached entity, validate, find an error and abort. The user changes whatever and saves again.
On the second save, the whole save process repeats, only this time it all goes to hell. Pretty much everything is duplicated, causing one error or another or all of them. Values from views and lookup tables that are only supposed to be references are created new and reassigned id's. Most of those issues I've been able to resolve, but I'm left with the multiplicity error. Child elements are being created as exact copies of other child elements, down to the unique id, only in the Added state. Or, if I reference certain properties, instead of cloning an unmodified child, it drops the new one. Either way, none of the code is executing as it did the first time around.
I'm discarding the instance of the dbcontext and the attached entity each save attempt. I thought that would be enough to revert any changes but something must be sticking around. The only thing not discared or reset is the detached entity, which is in session, but I dont make any changes to it. At least not directly.
The code (very simplified) is something like this:
void Save()
{
using (var context = new MyContext())
{
// detached entity from session
MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];
// attached entity from context
MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);
// <remove children representing lookup table elements from detachedEntity to prevent duplicates>
// <remove children representing view elements from detachedEntity to prevent duplicates>
// <apply changes from detachedEntity to attachedEntity>
// <add new children>
// <remove deleted children>
// <update modified children>
// <set entity state to unchanged on view and lookup elements of attachedEntity to ensure no duplicates...>
// <validate>
if (errors.count>0)
// <report errors>
else
context.SaveChanges();
}
}
as an example, this generates a multiplicity error:
// represents first save:
using (var context = new MyContext())
{
// detached entity from session
MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];
// attached entity from context
MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);
int debug1 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug1 == 0;
attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());
int debug2 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug2 == 1;
}
// represents second save:
using (var context = new MyContext())
{
// detached entity from session
MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];
// attached entity from context
MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);
int debug1 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug1 == 0;
attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());
int debug2 = context.ChangeTracker.Entries<ChildEntity>().Count(); // multiplicity error;
}
somehow the dbcontext remembers what objects were added to it. if the exact same object shows up twice, it... blows
instead of adding child entities from my detached entity to the attached one, i should've been creating new copies of each child
ChildEntity detachedChild = detachedEntity.ChildEntities.First();
attachedEntity.ChildEntities.Add(new ChildEntity {
propertyA = detachedChild.propertyA,
propertyB = detachedChild.propertyB
});
instead of
attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());
The problem is that detachedChild.parent should be assigned attachedParent.
foreach(var detachedEntity in detachedEntities)
{
attachedEntity.ChildEntities.Add(detachedEntity);
detachedEntity.ParentEntity = attachedEntity;
}
What you are trying to do is something like:
ChildEntity childEntity = new ChildEntity()
{
//do mapping or provide data EXCEPt THE PRIMARY KEY
}
foreach(ParentEntity parentEntity in parentEntities)
{
parentEntity.Add(childEntity);
}
_dbContext.SaveChanges();
Result
Multiplicity constraint violated. The role '…' of the relationship '…' has multiplicity 1 or 0..1
The reason of the error message is
that everytime the _dbContext adds the childEntity to some parentEntity, it sets the generated primary key to the childEntity, so in the second loop of the foreach the primary key will be duplicated
The fix is - Method #1 - for simple scenarios
foreach(ParentEntity parentEntity in parentEntities)
{
//Make a new object every time
ChildEntity childEntity = new ChildEntity()
{
//do mapping or provide data EXCEPt THE PRIMARY KEY
}
parentEntity.Add(childEntity);
}
_dbContext.SaveChanges();
The fix is - Method #2 - for complex scenarios
using YOUR_PROJECT.ANY_FOLDER.DeepCopyExtensions;
ChildEntity childEntity = new ChildEntity()
{
//do mapping or provide data EXCEPt THE PRIMARY KEY
}
foreach(ParentEntity parentEntity in parentEntities)
{
//makes a copy of the childEntity object and pass it to the _dbContext, after saving each copy will be separated and the original object childEntity wont be touched
parentEntity.Add(DeepCopyByExpressionTrees.DeepCopyByExpressionTree(childEntity));
}
_dbContext.SaveChanges();
What is this method "DeepCopyByExpressionTrees.DeepCopyByExpressionTree(childEntity)" ?
Check this project here, download the source code, and only include the class file "DeepCopyByExpressionTrees.cs" to your project as a helper class and start using it any where.
Thanks
Make sure to inspect the properties of the object you are trying to add. In my case it was mistakenly referencing the same invalid object on each add which it didn't like and thus threw the same error you have here.
EF 6 Update
For me setting object state to added worked on sounds logical also
ChildEntity detachedChild = detachedEntity.ChildEntities.First();
var newChild = new ChildEntity {
propertyA = detachedChild.propertyA,
propertyB = detachedChild.propertyB
});
// Mark all added entity entity state to Added
attachedEntity.ChildEntities.Add(newChild );
db.Entry(newChild ).State = EntityState.Added;
http://www.entityframeworktutorial.net/EntityFramework4.3/update-one-to-many-entity-using-dbcontext.aspx
I experienced this error when I had navigation properties that had not been set or navigation properties that belonged to the wrong Code First DBContext
I fixed this by making the child collections in the parent entity virtual. This allows one to easily Update the entity when its child collections don't change, which, for me, was most of the time.
I had a similar issue, but mine arose from a AsNoTracking() after my query.
I had something like this
var myObject = dbContext.GetRepo<myType>().Query().AsNoTracking().SingleOrDefault()
And then later on I use that object to set anther object.
var myChild = new Child { parent = myObect }
and apparently EntityFramework tries to create a brand new object and hence causes a multiplicity error.

Updating object with related entities from detached state

When I query from the entity framework I always query in a detached state so that the records retrieved can be stored in cache for subsequent requests.
Right now I have a form that the user can edit which contains a parent record, and then two lists of parent records.
When the data is POSTed to the server, I take my view models and map them into the entity framework objects using AutoMapper. The data looks fine; AutoMapper is mapping the data correctly.
When I attach the object so that I can update it, an exception is thrown: 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.
public static void UpdateOrder(ShippingOrder shippingOrder) {
using (OrderEntity orderContext = new OrderEntity()) {
//Exception happens here
orderContext.ShippingOrders.Attach(shippingOrder);
//Update the order itself; mark the order has being modified so the EF will update it.
orderContext.ObjectStateManager.ChangeObjectState(shippingOrder, System.Data.EntityState.Modified);
//Perform the update.
orderContext.SaveChanges();
}
}
The EntityFramework (EF) seems to think that my keys aren't lining up, but I'm not sure what isn't correct. The foreign key property does have the correct value, so I'm not sure what it's checking. Does anyone have any ideas?
You might try something like this:
ShippingOrder existingShippingOrder = orderContext.ShippingOrders.Find(shippingOrder.ID);
orderContext.Entry(existingShippingOrder ).CurrentValues.SetValues(shippingOrder);
Instead of
orderContext.ObjectStateManager.ChangeObjectState(shippingOrder, System.Data.EntityState.Modified);
try this
orderContext.Entry(ShippingOrder).State = EntityState.Modified;
as explained here
Insert or update pattern A common pattern for some applications is to
either Add an entity as new (resulting in a database insert) or Attach
an entity as existing and mark it as modified (resulting in a database
update) depending on the value of the primary key. For example, when
using database generated integer primary keys it is common to treat an
entity with a zero key as new and an entity with a non-zero key as
existing. This pattern can be achieved by setting the entity state
based on a check of the primary key value. For example:
public void InsertOrUpdate(DbContext context, Unicorn unicorn)
{
context.Entry(unicorn).State = unicorn.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
you can try
public static void UpdateOrder(ShippingOrder shippingOrder) {
using (OrderEntity orderContext = new OrderEntity()) {
orderContext.Entry(shippingOrder).State = shippingOrder.Id==0?
EntityState.Added :
EntityState.Modified;
orderContext.SaveChanges();
}
}
UPDATE:
for ObjectContext class you can try
public static void UpdateOrder(ShippingOrder shippingOrder) {
using (OrderEntity orderContext = new OrderEntity()) {
orderContext.ObjectStateManager.ChangeObjectState(shippingOrder, EntityState.Modified);
orderContext.SaveChanges();
}
}

attaching an entity with a related entity to a new entity framework context

Im trying to get my head around attaching an entity with a related entity to a new context when I want to update the entity.
I have a Person Table (Generalised to Personnel), which has a LanguageID field. This field is linked as a FK via the EF to another table Language with LanguageID as the primary key (1-M). I need to update a particular Persons language preference, however, the relationship seems to remain linked to the old context as i get a "Object cannot be referenced by multiple instances of IEntityChangeTracker" error on the line marked below. Is there any way to attach the Language entity to the new context as a relationship of the Personnel (Person) entity???
The entities were not detached in the orginal GetPersonnel() method which uses an .Include() method to return the PreferredLanguage
PreferredLanguage is the NavigationProperty name on the Person table...
public static void UpdateUser(Personnel originalUser, Personnel newUser )
{
using (AdminModel TheModel = new AdminModel())
{
((IEntityWithChangeTracker)originalUser).SetChangeTracker(null);
((IEntityWithChangeTracker)originalUser.PreferredLanguage).SetChangeTracker(null);
TheModel.Attach(originalUser);--Error Line
TheModel.ApplyPropertyChanges("Person", newUser);
TheModel.SaveChanges();
}
}
Thanks
Sean
To avoid these sort of problems you should make GetPersonnel() do a NoTracking query.
I.e.
ctx.Person.MergeOption = MergeOption.NoTracking;
// and then query as per normal.
This way you can get a graph of connected entities (assuming you use .Include()) that is NOT attached. Note this won't work if you try to manually detach entities, because doing so schreds your graph.
Hope this helps
Alex

Resources