New Posts New Posts RSS Feed: Temp Id Conflicts
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Temp Id Conflicts

 Post Reply Post Reply
Author
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Topic: Temp Id Conflicts
    Posted: 24-Jan-2010 at 6:18pm
My Entities are bound to Oracle tables where the Primary Key is populated from a sequence by a trigger. I have added the StoreGeneratedPattern="Identity" attribute in the model.Edml:

e.g.
<EntityType Name="DEAL_CONFIRMATION">
     <Key>
          <PropertyRef Name="DEAL_CONFIRM_ID" />
     </Key>
     <Property Name="DEAL_CONFIRM_ID" Type="int64" Nullable="false" StoreGeneratedPattern="Identity" />
     <Property Name="DEAL_ID" Type="int64" Nullable="false" />

My BaseEntity implements a Create method that calls EntityAspect.EntityManager.GenerateId(this, key) to get the new temporary Id's like so:

    [DataContract]
    public abstract class CreatableBaseEntity : Entity
    {
        /// <summary>
        /// Call to create a new instance of this type in the provided EntityManager.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="emProvider"></param>
        /// <returns></returns>
        public static T Create<T>(IEntityManagerProvider emProvider) where T : CreatableBaseEntity
        {
            var entity = emProvider.EntityManager.CreateEntity<T>();
            entity.Initialize();
            return entity;
        }

        #region Initialize

        internal void Initialize()
        {
            Initializing = true;
            GetATemporaryKey();
            InitializeDefaults();
            EntityAspect.AddToManager();
            AfterAddToManager();
            Initializing = false;
        }

        private void GetATemporaryKey()
        {
            var key = EntityAspect.EntityMetadata.KeyProperties[0];
            if (key.IsAutoIncrementing)
            {
               try
               {
                    EntityAspect.EntityManager.GenerateId(this, key);
               }
               catch (IdeaBladeException ex)
               {
                    //eat the exception thrown if this class does not have a Generator
                    Logging.Logging.Log(ex);
               }
               catch (Exception ex)
               {
                    Logging.Logging.Log(ex);
                    throw;
               }
            }
        }

        protected virtual void InitializeDefaults() { }

        protected virtual void BeforeAddToManager() { }
        protected virtual void AfterAddToManager() { }

        [DataMember]
        public bool Initializing { get; protected set; }

        #endregion
       
    }

Now, the process to create these Deal Confirmations involves several steps and can bog down so that it takes several seconds. What I am experiencing, is that if I kick off a save of several deals, each of which spawns the process to create the confirmation for that deal, then some time later, the confirmation EntityManager complains because when the EntityAspect.AddToManager() is called, it errors because the temporary Id already exists. I.e. the first Deal progresses and creates Confirmation -100, and then saves it. Meanwhile, the second deal progresses at a different pace, creates Confirmation - 106 and saves it. Likewise, the third deal progresses, but Conf -100 has been saved and is now Conf 18765, so the EM creates a new Conf -100, but when it tries to add it to the EM, it errors because Conf -100 is already in the EM.

I have tried to explain this clearly, but it has taken me several hours to figure out what is going on. I have no idea how to fix this on my end? Why is the EntityManager.GenerateId returning -100 twice?
       
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jan-2010 at 6:20pm
I'm wondering if the EntityAspect.EntityManager.GenerateId method in this Confirmation calls on something that does not know that the EntityAspect.EntityManager.GenerateID in some other Confirmation has already pulled Id -100?
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jan-2010 at 6:59pm
This actually sounds like a threading issue.  Are you creating these records on the server as part of save processing?  There is a known threading bug in our sample IdGenerator, but I have a sinking feeling the problem may also exist in the internal "StoreGeneratedIdGenerator"...  On an EntityServer, there's only a single instance of the IdGenerator yet if you're creating Ids there then multiple threads may be accessing it.  The issue is not a locking problem, but a need for thread local storage.  
Back to Top
skingaby View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 23-Apr-2008
Location: United States
Posts: 146
Post Options Post Options   Quote skingaby Quote  Post ReplyReply Direct Link To This Post Posted: 25-Jan-2010 at 4:40am
I am creating these records on the client, but they are created in the latter sections of a set of daisy-chained asynchronous calls. I seem to have worked around the problem in my BaseEntity class by repeating the call to GetATemporaryKey if the AddToManager fails:

        internal void Initialize()
        {
            Initializing = true;
            GetATemporaryKey();
            var addedToManager = false;
            do
            {
               try
               {
                    EntityAspect.AddToManager();
                    addedToManager = true;
               }
               catch (ArgumentException)
               {
                    GetATemporaryKey();
               }
            } while (!addedToManager);
            InitializeDefaults();
            AfterAddToManager();
            Initializing = false;
        }

The only problem with this approach is that the ArgumentException causes the debugger to show itself so I have to F5 to resume whenever a bad key is found, but since the only way I can get a bad key is if I run a stress test, this should be more-or-less manageable.

Do you guys have any plans to fix this in your IdGenerator?
Thanks
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down