Mark:jiaguoxinzhi Linyee 屬性跟蹤
首先聲明,,此篇文章内容還處于研究階段,不確定,内容正确性。。
1、Ef core方式 Microsoft.EntityFrameworkCore 3.0.0
EF core源碼翻來覆去N多次,,始終沒有明白具體是怎麼監控屬性變更的。。
大緻上是,一個實體類分,跟蹤映射類、原始類。
主要是通過使用 ValueComparer 來判斷是否已更新
db.SaveChanges()時
TryDetectChanges();//狀态跟蹤
var entitiesSaved = DbContextDependencies.StateManager.SaveChanges(acceptAllChangesOnSuccess);//儲存
return entitiesSaved;
可見實際的儲存動作是在SaveChanges(acceptAllChangesOnSuccess)
var entriesToSave = GetEntriesToSave();
var result = SaveChanges(entriesToSave);
public abstract partial class InternalEntityEntry : IUpdateEntry
SetPropertyModified SetEntityState 會增加 ChangedCount;
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual void DetectChanges(IStateManager stateManager)
{
_logger.DetectChangesStarting(stateManager.Context);
foreach (var entry in stateManager.ToList()) // Might be too big, but usually _all_ entities are using Snapshot tracking
{
if (entry.EntityType.GetChangeTrackingStrategy() == ChangeTrackingStrategy.Snapshot
&& entry.EntityState != EntityState.Detached)
{
LocalDetectChanges(entry);
}
}
_logger.DetectChangesCompleted(stateManager.Context);
}
private void LocalDetectChanges(InternalEntityEntry entry)
{
var entityType = entry.EntityType;
foreach (var property in entityType.GetProperties())
{
if (property.GetOriginalValueIndex() >= 0
&& !entry.IsModified(property)
&& !entry.IsConceptualNull(property))
{
var current = entry[property];
var original = entry.GetOriginalValue(property);
var comparer = property.GetValueComparer() ?? property.FindMapping()?.Comparer;
if (comparer == null)
{
if (!Equals(current, original))
{
LogChangeDetected(entry, property, original, current);
entry.SetPropertyModified(property);
}
}
else
{
if (!comparer.Equals(current, original))
{
LogChangeDetected(entry, property, original, current);
entry.SetPropertyModified(property);
}
}
}
}
foreach (var property in entityType.GetProperties())
{
DetectKeyChange(entry, property);
}
if (entry.HasRelationshipSnapshot)
{
foreach (var navigation in entityType.GetNavigations())
{
DetectNavigationChange(entry, navigation);
}
}
}
由以上代碼可見。。
var current = entry[property];
var original = entry.GetOriginalValue(property);
主要是通過比較以上兩個值來判斷是否已修改
其中 GetProperties 掃了下相關的代碼,,基本上就是一個屬性袋的概念,,用有序字典來儲存。
Entity 是目前值
_values[index] = SnapshotValue(property, value); 是快照原始值
_values 是個 ISnapshot,,是個對象 從InternalEntityEntry生成的一個動态表達式樹數組
這裡看得有點朦,,不确定是否正确
return Expression.Lambda<Func<TInput, ISnapshot>>(
CreateConstructorExpression(entityType, parameter),
parameter)
.Compile();
2 emit
emit 大約是要求實體類的屬性需要帶 virtual,,,否則不好用。。或許是我還沒找到生成帶new的方式吧。。
關鍵代碼
//動态建立類代理
TypeBuilder typeBuilderProxy = moduleBuilder.DefineType($"{inType.Name}_Proxy_{inKey}", System.Reflection.TypeAttributes.Public, inType);
PropertyBuilder propertyBuilder = typeBuilderProxy.DefineProperty("a", System.Reflection.PropertyAttributes.SpecialName, typeof(string), null);
//FieldBuilder fieldBuilder = typeBuilderProxy.DefineField("_a", typeof(string), FieldAttributes.Private);
//重寫屬性的Get Set方法
var methodGet = typeBuilderProxy.DefineMethod("get_a" , GetSetMethodAttributes, typeof(string), Type.EmptyTypes);
var methodSet = typeBuilderProxy.DefineMethod("set_a" , GetSetMethodAttributes, null, new Type[] { typeof(string) });
var ilGetMethod = methodGet.GetILGenerator();
ilGetMethod.Emit(OpCodes.Ret);
//il of set method
ILGenerator ilSetMethod = methodSet.GetILGenerator();
ilSetMethod.Emit(OpCodes.Ret);
//設定屬性的Get Set方法
propertyBuilder.SetGetMethod(methodGet);
propertyBuilder.SetSetMethod(methodSet);
//建立類執行個體
var instance = Activator.CreateInstance(dict[inKey]);
return (T)instance;
private const System.Reflection.MethodAttributes GetSetMethodAttributes = System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.HideBySig
| System.Reflection.MethodAttributes.NewSlot | System.Reflection.MethodAttributes.Virtual | System.Reflection.MethodAttributes.SpecialName | System.Reflection.MethodAttributes.CheckAccessOnOverride
;
但這樣寫完後 發現 一直執行不到子類的方法,,一直是擷取父類的方法,,不知道哪裡寫挂了。。還是因為3.0有bug
3、INotifyPropertyChanged
建立一個基類實作 INotifyPropertyChanged ,其它實體從這個基類繼承。
4、Roslyc CSharpSyntaxTree
動态建立方式。。
但跟Emit一樣,,強制轉換為父級實體類型後,,無法監控屬性變更。
總結:仿efCore方式才是實體ORM,屬性監控的王道。其它用途則視情況選型。
仿EFcore實體ORM
主要是有個原值,緩存表。。
外面應該有個模型緩存表。
然後是上下文關聯模型,關聯實體,關聯原值緩存表。
https://dev.tencent.com/u/jiaguoxinzhi/p/code_module/git/blob/master/LikeEfCore 此代碼未成熟 不公開