New Posts New Posts RSS Feed: Handling Concurrency Failures
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Handling Concurrency Failures

 Post Reply Post Reply Page  <12
Author
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Topic: Handling Concurrency Failures
    Posted: 07-Jan-2010 at 8:03am
Since there is no Silverlight example I have been examining the HandlingConcurrencyConflicts Winforms sample included in the Learning Resources.
 
Could someone explain to me where the "BaseEntity" class came from that the Customer class inherits from in the code-behind for the domain model?
 
Examining the "BaseEntity" code the comments state that the class was generated.  Is this the case?  If so what options in the DevForce Object Mapper were selected to create this class?
 
When I generate a domain model all of the objects inherit from "Entity" and I don't get a "BaseEntity" class generated.
 
I am assuming that the "BaseEntity" class was custom written and the author went in and manual edited the code-behind for the domain model by changing the inheritance from "Entity" to "BaseEntity" on the objects in the model.
 
Is this assumption correct?
Back to Top
GregD View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 374
Post Options Post Options   Quote GregD Quote  Post ReplyReply Direct Link To This Post Posted: 07-Jan-2010 at 9:57am
You can inject base classes into your model using the Object Mapper, and then specify which other classes you want to inherit from them. The base classes so injected will then be generated by the Object Mapper, and all classes designated to inherit from them will be generated with that inheritance.

I haven't looked at the Concurrency solution for a while, but rather doubt that the use of a BaseEntity is in any way necessary to handle concurrency conflicts. In this as in other uses, a base class is simply a good, economical place to put any methods or properties you'd like all your entities to have.

Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 07-Jan-2010 at 10:01am
Thank you Greg for explaining.  I'm slowly figuring all of this out!
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: 07-Jan-2010 at 6:05pm
The default will be for your entities to inherit from Entity. However, if you want to implement any common base entity logic, you will need to add a BaseEntity class (you can change the name) that inherits from Entity and tell the Ideablade Object Mapper to use that as your default Base class.
For example, all of our Oracle tables need a temporary key when they are created, so our BaseEntity class looks like this:

    public abstract class BaseEntity : 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 : BaseEntity
        {
            var entity = emProvider.EntityManager.CreateEntity<T>();
            entity.Initialize();
            return entity;
        }

        #region Initialize

        internal void Initialize()
        {
            Initializing = true;
            GetATemporaryKey();
            InitializeDefaults();
            BeforeAddToManager();
            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() { }

        public bool Initializing { get; protected set; }

        #endregion
       
    }

Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 11-Jan-2010 at 6:03am
I've run across an issue in trying to implement concurrency conflict handling in my Silverlight application (DevForce 5.2.4.2).  I have modeled my concurrency handler after the concurrency handler in the "HandlingConcurrencyConflicts" WinForms sample project in the Learning Resources.
 
I am trying to verify the case where the local user is attempting to update a record that has already been deleted by another user.  I have opened the database in SQL Server management studio and I have also started my application at the same time.  I delete a record from the table and then in my application I make an update to the record and then attempt to save.  I get a concurrency conflict as expected but I find that because the entity has an original value I end up falling into the block of code that handles the case where the local user and remote user both made changes to the record instead of falling into the block of code that handles the scenario I'm trying to verify.
 
For further clarification, my code is essentially the same as that in the "ResolveMyConflicts" routine in the "ConcurrencyHandler" class in the Learning Resources sample.
 
Looking at that routine, the first thing that is done is RefetchEntity is called with the MergeStrategy set to "PreserveChangesUpdateOriginal".  If you read the documentation on this MergeStrategy it states:
 
"The refetch replaces the cached entity's original version with the values from the current data source entity but it preserves the cached entity's current version values thus retaining the pending changes"
 
I can see where that makes sense for the case where two users make changes to the same record but how would this work in the case where the record doesn't exist in the data source because it was deleted?
 
What happens to the original version values of the cached entity?  Is there a flag I should be looking at that tells me the record in the data source was not found and so the original version values of the cached entity were not replaced from the data source so that I can then move into the correct block of code to handle this particular type of concurrency conflict?
 
I tried switching to the "PreserveChangesUnlessOriginalObsolete" merge strategy but that takes me into the block of code that handles the case where the local user tries to delete a record that was modified by another user.
 
Could someone explain to me how this scenario works and where I am going wrong and the Learning Resource sample seems to be going wrong as well?
 
 
Back to Top
GregD View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 374
Post Options Post Options   Quote GregD Quote  Post ReplyReply Direct Link To This Post Posted: 11-Jan-2010 at 11:25am
I can't swear that I tested (or even addressed) this particular thing in the current WinForm example, but at least back in pre-EntityFramework days (DevForce Classic), the Original values would get a bunch of either nulls or default values (can't remember which) when replaced based on a database record that no longer exists. You could use that to identify the situation you're targetting.

What are you seeing for Original values for your deleted record after the replacement?

Back to Top
cjohnson84 View Drop Down
Groupie
Groupie


Joined: 24-Sep-2009
Location: Akron, Ohio
Posts: 44
Post Options Post Options   Quote cjohnson84 Quote  Post ReplyReply Direct Link To This Post Posted: 11-Jan-2010 at 11:44am
Here is my code for the refetch

_entityManager.RefetchEntitiesAsync(_entityKeyList, MergeStrategy.PreserveChangesUpdateOriginal, RefetchCallback, null);

Here is what I see in my "RefetchCallback" method for the entity in question.  Note my entity in question is a "Client" comprised of ID, FirstName, LastName, TimeStamp (this is my concurrency column on the table):
 
_currentValues
[0] 19
[1] Chad
[2] Johnson CCJ
 
_originalValuesBackup
[0] 19
[1] Chad
[2] Johnson
 
_proposedValuesBackup
[0] 19
[1] Chad
[2] Johnson
 
CurrentValues
[0] 19
[1] Chad
[2] Johnson CCJ
 
Nothing was nulled out from what I can tell.
Back to Top
GregD View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 374
Post Options Post Options   Quote GregD Quote  Post ReplyReply Direct Link To This Post Posted: 11-Jan-2010 at 4:42pm
Well, it may be that Entity Framework handles this situation differently. All I can really tell you is to comb over the conflicted entity (and drill into the exception that gets thrown) to see if you can identify anything in either that gets set to a value that enables you to identify the conflict uniquely as resulting from an attempt update a remotely deleted entity.

You're looking for anything in the state of the regurgitated entity that is characteristic of that situation only. (That's what I did when I wrote the original version.)
Back to Top
 Post Reply Post Reply Page  <12

Forum Jump Forum Permissions View Drop Down