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?
|
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.
|
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
|