ConcurrencyException on SaveEntityCollection

Posts   
 
    
Posts: 19
Joined: 27-Mar-2011
# Posted on: 15-Sep-2011 15:03:13   

LLBLGen 3.1, .NET 4, PostgreSQL 9

Hello,

I wanna create DB independent application, so I have created custom IConcurrencyPredicateFactory


    class OptimisticConcurrencyFactory : IConcurrencyPredicateFactory
    {
        public IPredicateExpression CreatePredicate(ConcurrencyPredicateType predicateTypeToCreate, object containingEntity)
        {
            IPredicateExpression toReturn = null;

            if (predicateTypeToCreate == ConcurrencyPredicateType.Save)
            {
                toReturn = new PredicateExpression();
                CommonEntityBase entity = (CommonEntityBase)containingEntity;
                EntityField2 concurrencyField = (EntityField2)entity.Fields["ConcurrencyGuid"];
                toReturn.Add(concurrencyField == concurrencyField.CurrentValue);
            }

            return toReturn;
        }
    }

and added in CommonEntityBase


        protected override IConcurrencyPredicateFactory CreateConcurrencyPredicateFactory()
        {
            return new CareSense.ORMEntities.DatabaseGeneric.HelperClasses.OptimisticConcurrencyFactory();
        }

I'm setting ConncurencyGuid field in Adapter before save


        public override bool SaveEntity(IEntity2 entityToSave, bool refetchAfterSave, IPredicateExpression updateRestriction, bool recurse)
        {
            AddConcurrencyGuidToEntity(entityToSave, recurse);

            return base.SaveEntity(entityToSave, refetchAfterSave, updateRestriction, recurse);
        }

        public override int SaveEntityCollection(IEntityCollection2 collectionToSave, bool refetchSavedEntitiesAfterSave, bool recurse)
        {
            foreach (IEntity2 entity in collectionToSave)
            {
                AddConcurrencyGuidToEntity(entity, recurse);
            }

            return base.SaveEntityCollection(collectionToSave, refetchSavedEntitiesAfterSave, recurse);
        }

        private void AddConcurrencyGuidToEntity(IEntity2 entityToSave, bool recurse)
        {
            if (recurse)
            {
                ObjectGraphUtils objectGraphUtils = new ObjectGraphUtils();

                System.Collections.Generic.List<IEntity2> entityList = objectGraphUtils.ProduceTopologyOrderedList(entityToSave);

                foreach (IEntity2 entity in entityList)
                {
                    if (entity.IsDirty)
                    {
                        entity.Fields["ConcurrencyGuid"].CurrentValue = System.Guid.NewGuid().ToString();
                    }
                }
            }
            else
            {
                if (entityToSave.IsDirty)
                {
                    entityToSave.Fields["ConcurrencyGuid"].CurrentValue = System.Guid.NewGuid().ToString();
                }
            }
        }

Everything is OK when I'm using SaveEntity method, but on SaveEntityCollecton ORMConcurrencyException is thrown every time.

Thanks in advance.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 15-Sep-2011 22:57:38   

There are a couple of things I don't understand in your code:

Your concurrency predicate

if (predicateTypeToCreate == ConcurrencyPredicateType.Save)
            {
                toReturn = new PredicateExpression();
                CommonEntityBase entity = (CommonEntityBase)containingEntity;
                EntityField2 concurrencyField = (EntityField2)entity.Fields["ConcurrencyGuid"];
                toReturn.Add(concurrencyField == concurrencyField.CurrentValue);
            }

Based on all your code and the way you set the GUID to dirty entities, that predicate will be always true.

entity.Fields["ConcurrencyGuid"].CurrentValue = System.Guid.NewGuid().ToString();

About the exception, please post the exact message and stack trace.

David Elizondo | LLBLGen Support Team
Posts: 19
Joined: 27-Mar-2011
# Posted on: 16-Sep-2011 11:57:43   

I seems that


adapter.SaveEntity(...) 

first calls


SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.GetConcurrencyPredicate

and then


public override bool SaveEntity

but


adapter.SaveEntityCollection 

first calls


public override int SaveEntityCollection

and then


SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.GetConcurrencyPredicate

That's why adapter.SaveEntity(...) is working fine in my case.

But adapter.SaveEntityCollection(...) creates concurrency predicate after new ConcurrencyGuid is set, and, or course, concurrency check fails.

Is there any way to call


SD.LLBLGen.Pro.ORMSupportClasses.EntityBase2.GetConcurrencyPredicate

before


public override int SaveEntityCollection

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 16-Sep-2011 13:10:23   

SaveEntityCollection loops on the entities to call SaveEntity for each one. So the GetConcurrencyPredicate should be called before each SaveEntity.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39908
Joined: 17-Aug-2003
# Posted on: 17-Sep-2011 13:53:34   

I don't really see why the order in which calls are made make a difference as well. You shouldn't need to override SaveEntityCollection to use concurrencypredicatefactory, it's called automatically when it's inside an entity to produce a predicate.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 19
Joined: 27-Mar-2011
# Posted on: 17-Sep-2011 13:55:53   

Walaa wrote:

SaveEntityCollection loops on the entities to call SvaeEntity for each one. So the GetConcurrencyPredicate should be called before each SaveEntity.

I see. But it calls base.SaveEntity, not my override.

So here is my workaround, which also solves same issue with recurse save:


        public override bool SaveEntity(IEntity2 entityToSave, bool refetchAfterSave, IPredicateExpression updateRestriction, bool recurse)
        {
            if (recurse)
            {
                ObjectGraphUtils objectGraphUtils = new ObjectGraphUtils();

                System.Collections.Generic.List<IEntity2> entityList = objectGraphUtils.ProduceTopologyOrderedList(entityToSave);

                foreach (IEntity2 entity in entityList)
                {
                    base.SaveEntity(entity, refetchAfterSave, false);
                }

                return true;
            }
            else
            {
                AddConcurrencyGuidToEntity(entityToSave);
                return base.SaveEntity(entityToSave, refetchAfterSave, updateRestriction, false);
            }

        }

        public override int SaveEntityCollection(IEntityCollection2 collectionToSave, bool refetchSavedEntitiesAfterSave, bool recurse)
        {
            int counter = 0;
            foreach (IEntity2 entity in collectionToSave)
            {
                if (entity.IsDirty)
                {
                    this.SaveEntity(entity, refetchSavedEntitiesAfterSave, recurse);
                    counter++;
                }
            }

            return counter;
        }

        private void AddConcurrencyGuidToEntity(IEntity2 entityToSave)
        {
            if (entityToSave.IsDirty)
            {
                entityToSave.Fields["ConcurrencyGuid"].CurrentValue = System.Guid.NewGuid().ToString();
            }
        }