MDriven批量插入B对象后Unload/Reload致关联A属性为空问题排查
MDriven中BulkInsert后Unload/Reload单关联属性为Null的问题
问题背景
模型关系为A 1 <==> n B,因B类对象数量庞大,直接调用UpdateDatabase()保存耗时过长,故采用BulkInsert批量存储B对象,A仍通过MDriven正常保存。为模拟MDriven的正常行为,需将B对象标记为clean,让后续操作识别其已保存状态,为此编写了MakeClean方法。
当前实现的MakeClean方法
/// <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); } }
问题现象
通过以下代码卸载并重新加载B实例时:
var persistenceService = objectParameter.AsIObject().ServiceProvider.GetEcoService<IPersistenceService>(); persistenceService.Unload(objectParameter);
卸载操作会标记对象为"需重新加载",重新加载时出现异常:
- 执行重载SQL能正常获取到A的外键值
- MDriven无法定位到对应的A对象,错误地将单关联属性A设为null(实际A对象存在,且重载前已赋值)
已完成的检查
- 模型与映射正确,使用
UpdateDatabase()保存时,Unload/Reload功能正常 - 自定义BulkInsert已正确保存数据,数据库中A对象的外键状态正常
- 内部操作追踪(LogSql、LogSqlMeta、LogPMapper)未发现异常
追踪日志
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
测试用例(执行失败)
var ecoSpace = new EcoSpace(); var a = new A(ecoSpace) { ID = GetNextIDForA()}; var b = new B(ecoSpace) { ID = GetNextIDForB(), A = a, } // 用MDriven原生保存的分支,此分支正常 // ecoSpace.UpdateDatabase(); // 自定义保存的分支,此分支失败 a.Save(); // 仅保存A(调用MDriven的UpdateDatabase(a)),B保持dirty状态 // 在此处设置断点,手动向数据库插入数据模拟BulkInsert b.MakeClean(); // 调用上述MakeClean方法 b.Unload(); // 卸载B实例 Assert.IsNotNull(b.A); // 原生保存分支正常,自定义分支失败
额外信息
- ORM映射通过自定义ORM文件实现,类与数据库为扁平1:1映射,无ECO_Type或TIMESTAMP列,ID通过自定义方法设置:
<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>
- 创建B对象后,ObjectId格式如下,调用
UpdateDatabase()后无变化:
136!D!AAEAAAD/////AQAAAAAAAAAEAQAAAA5TeXN0ZW0uRGVjaW1hbAQAAAAFZmxhZ3MCaGkCbG8DbWlkAAAAAAgICAgAAAAAAAAAAJ8BAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
- 调用
IExternalIdService.ObjectForId(string id)可直接获取B对象,无需执行SQL,该行为与KeyMapper="Attribute"相关,无需从数据库回读ID,但仍存在Unload后A属性为空的问题。
请问MakeClean方法是否存在缺失?若存在,需补充哪些内容?若不存在,问题根源是什么?
内容的提问来源于stack exchange,提问作者Peter Buchmann
相关产品推荐
相关产品推荐

