Reputation: 128
Please assume the model below. We save objects of class B using a custom method and a following reload of an instance of B sets the single link property A to null:
A 1 <==> n B
We have the problem that we have to deal with a high number of objects of class B. If we simply call UpdateDatabase(), it takes far to long to save all Bs. So we use a BulkInsert functionality to store all objects of class B in the database. A is still saved via MDriven. To simulate the normal behaviour of MDriven, the objects of B must be made clean, so that following operations recognize that the Bs are already saved.
I wrote following method to make the Bs clean:
/// <summary>
/// Makes a dirty object clean.
/// </summary>
/// <param name="ecoObject">EcoObjekt that will be made clean.</param>
public static void MakeClean(this IEcoObject ecoObject)
{
var ecoServiceProvider = ecoObject.ServiceProvider();
var classifier = ecoObject.AsIObject().UmlClass;
var cache = ecoServiceProvider.GetEcoService<ICache>();
var locator = ecoObject.AsIObject().GetLocator();
cache.SetPersistenceState(locator, PersistenceState.Current);
cache.SetExistenceState(locator, Cache.ExistenceState.Existing);
foreach (var structuralFeature in classifier.AllStructuralFeatures.Cast<IStructuralFeature>().Where(x => x.Persistent))
{
cache.SetMemberPersistenceState(locator, structuralFeature, PersistenceState.Current);
}
}
My problem occurs when an instance ob B shall be reloaded from the database like this:
var persistenceService = objectParameter.AsIObject().ServiceProvider.GetEcoService<IPersistenceService>();
persistenceService.Unload(objectParameter);
This markes the object as "has to be reloaded" and when the reload occurs following happens:
What I have checked:
Eco.Sql: 00:00:00.0050037: SELECT B.ID, B.ATTRIBUTE1, B.ATTRIBUTE2, B.A FROM B B WHERE B.ID = ?
Eco.SqlParams:
ID1=344 (System.Decimal) (DbType: Decimal)
Eco.PMapper: Fetched 1 objects of class B
Eco.PMapper: ApplyDataBlock
Eco.PMapper: B.DefaultLoad : 1
I assume that my MakeClean is not complete and does not set the objects of B so that a Unload/Reload is possible. Here is a test that fails:
var ecoSpace = new EcoSpace();
var a = new A(ecoSpace) { ID = GetNextIDForA()};
var b = new B(ecoSpace)
{
ID = GetNextIDForB(),
A = a,
}
// Variant to update the database via MDriven. This works.
// ecoSpace.UpdateDatabase();
// Variant that does not work.
a.Save(); // Method only saves a (using MDriven UpdateDatabase(a)), b remains dirty.
// Set breakpoint here so that you can enter the data manually into the database to simulate the bulk insert.
b.MakeClean(); // see method above
b.Unload(); // Extension method that unloads the instance b
Assert.IsNotNull(b.A); // OK in MDriven variant, fails when using MakeClean
Additional Info: The OR-Mapping is custom via an ORM-File and each property is mapped into a according database column. IDs are also set via a custom method (added in code above so that it gets clearer). There are no ECO_Type or TIMESTAMP columns. I'd call this a flat 1:1 mapping between database and class.
Our OR-mapping is custom and looks like follows:
<ClassDef Name="B" >
<AliasDef Name="B" Table="B" ExtentRequiresDiscriminator="False">
<KeyImpl Name="EcoKey">
<KeyColumn Name="ID" />
</KeyImpl>
</AliasDef>
<KeyDef Name="EcoKey" Signature="System.Decimal" IsId="True" KeyMapper="Attribute" />
<AttributeDef Name="ID" Columns="ID" />
<SingleLinkDef Name="A" Columns="AID" Key="A.EcoKey" />
</ClassDef>
Directly after creating the B object, the ObjectId is something like this
136!D!AAEAAAD/////AQAAAAAAAAAEAQAAAA5TeXN0ZW0uRGVjaW1hbAQAAAAFZmxhZ3MCaGkCbG8DbWlkAAAAAAgICAgAAAAAAAAAAJ8BAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
and this value does not change after a UpdateDatabase. So I don't see a difference in the ObjectId between objects that are saved via MDirven and our BulkInsert. Indeed if I call IExternalIdService.ObjectForId(string id) using the ID above, no SQL is executed and I get the same b object. I think this behavior is specific to the used key mapper (KeyMapper="Attribute"). The process of "id is read back from DB" simply does not happen, because it is not necessary.
Still the b.Unload() nulls the association attribute to a.
So my question is:
Is the method makeClean the problem and if so, what is missing in my MakeClean method? If not, what is wrong?
Upvotes: 0
Views: 75
Reputation: 2435
What happens Internally is: An ObjectLocator with an ObjectId is created for each object. This ObjectId is a local id (a guid) until save has either:
After this step the object change PersistenceState from Modified to Current so that we know that the next save of this object should be an update.
Your MakeClean method skips the refinement of the ObjectLocators ObjectID - and I think that if you check it you would see a local id that does not exists in the db.
You then call Unload - but Unload most likely just invalidate all the members and object, the object does however have its local unsaved ObjectId in the identity list.
Suggested change: Forget about your b - and use IExternalIdService.ObjectForId(string id) to have the real b - with the real id - be fetched from the db.
Upvotes: 0