天天看點

c# .net core 屬性跟蹤的幾種方式

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 此代碼未成熟 不公開

繼續閱讀