MongoDb Samus 驱动的改进

一直使用 MongoDb 的 Samus C#驱动。

其有一个缺陷,就是无法支持struct的读写。

但是一般数据都用Class包装,所以也没有太在意。

随着这些天尝试写入 KLineData 时,遇到了非常龌龊的问题。

KLineData这个Class内部有一个TICK[4] 这样一个数组,TICK是一个结构类型

Samus可以顺利的写入KLineData

但是读取时,立刻发生了异常。


查看内部实现,发现其用Emit做的ORM,代码如下:

1.创建Map

 private ExtendedPropertiesMap CreateExtendedPropertiesMap(Type classType){
            var extPropMember = _profile.FindExtendedPropertiesMember(classType);
            if(extPropMember == null)
                return null;

            return new ExtendedPropertiesMap(
                extPropMember.Name,
                extPropMember.GetReturnType(),
                MemberReflectionOptimizer.GetGetter(extPropMember),
                MemberReflectionOptimizer.GetSetter(extPropMember));
        }
其中GetSetter代码如下

 public static Action<object, object> GetSetter(MemberInfo memberInfo)
        {
            if(memberInfo == null)
                throw new ArgumentNullException("memberInfo");
            if(memberInfo.MemberType != MemberTypes.Field && memberInfo.MemberType != MemberTypes.Property)
                throw new ArgumentException("Only fields and properties are supported.", "memberInfo");

            if(memberInfo.MemberType == MemberTypes.Field)
                return GetFieldSetter(memberInfo as FieldInfo);

            if(memberInfo.MemberType == MemberTypes.Property)
                return GetPropertySetter(memberInfo as PropertyInfo);

            throw new InvalidOperationException("Can only create setters for fields or properties.");
        }


我们关注其中的Field的Emit反射

  public static Action<object, object> GetFieldSetter(FieldInfo fieldInfo)
        {
            if(fieldInfo == null)
                throw new ArgumentNullException("fieldInfo");

            var key = CreateKey(fieldInfo);

            Action<object, object> setter;

            lock (SyncObject)
            {
                if (SetterCache.TryGetValue(key, out setter))
                    return setter;
            }
            
            if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
                    throw new InvalidOperationException("Cannot create a setter for a readonly field.");

            var sourceType = fieldInfo.DeclaringType;
            var method = new DynamicMethod("Set" + fieldInfo.Name, null, new[] {typeof (object), typeof (object)}, true);
            var gen = method.GetILGenerator();

            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Castclass, sourceType);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
            gen.Emit(OpCodes.Stfld, fieldInfo);
            gen.Emit(OpCodes.Ret);

            setter = (Action<object, object>) method.CreateDelegate(typeof (Action<object, object>));

            lock (SyncObject)
            {
                SetterCache[key] = setter;
            }

            return setter;
        }
    gen.Emit(OpCodes.Ldarg_0); // 把参数0入栈
            gen.Emit(OpCodes.Castclass, sourceType);//把参数0的类型转为sourceType :   x as NewType
            gen.Emit(OpCodes.Ldarg_1);// 把参数1入栈
            gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);//把参数1的类型强制转换为FieldType:  (NewType)x
            gen.Emit(OpCodes.Stfld, fieldInfo);// SetField( 参数0,参数1 )
            gen.Emit(OpCodes.Ret);// 返回

以上代码有一个问题,就是要求参数0是Class,如果是Value是有异常的,即使可行也是没有意义的,因为ValueType是指传递

setter函数的设计模式(<object,object>) 天然就无法处理Value


解决办法:

 public void AddProperty(string name, object value)
        {
            #region Original Codes
            //var memberMap = _classMap.GetMemberMapFromAlias(name);
            //if (memberMap != null)
            //    memberMap.SetValue(_instance, value);
            //else if ((!_classMap.HasDiscriminator || _classMap.DiscriminatorAlias != name) && _extendedProperties != null)
            //    _extendedProperties.Add(name, value);
            #endregion
            #region norsd Codes
            if(_bIsClass)
            {
                var memberMap = _classMap.GetMemberMapFromAlias(name);
                if (memberMap != null)
                    memberMap.SetValue(_instance, value);
                else if ((!_classMap.HasDiscriminator || _classMap.DiscriminatorAlias != name) && _extendedProperties != null)
                    _extendedProperties.Add(name, value);
            }
            else
            {
                _SetStructValue(name, ref _instance, value);
            }
            #endregion
        }

注意_bIsClass就走原始流程,如果是Struct就走 _SetStructValue 流程

下面贴出整个类的代码

架构:

设置一个全局静态变量

readonly static System.Collections.Generic.Dictionary<System.Reflection.FieldInfo, SETSTVALUE> _s_dtStSetter = new Dictionary<System.Reflection.FieldInfo, SETSTVALUE>();

用于缓存SetStructValue函数

同时一个类私有变量readonly System.Collections.Generic.Dictionary<string, SETSTVALUE> _dtStSetter = new Dictionary<string, SETSTVALUE>();

用于快速在FieldInfo.Name -> SetStructValue

_CreateStSetter是动态创建函数

using System;

using MongoDB.Configuration.Mapping.Model;
using System.Collections.Generic;

namespace MongoDB.Serialization.Builders
{
    internal class ConcreteClassMapBuilder : IObjectBuilder
    {
        private readonly IClassMap _classMap;
        #region Original
        //private readonly object _instance;
        #endregion
        #region norsd
        //由于struct需要装箱拆箱,_instance无法设为Null
        private object _instance = null;
        #endregion
        private readonly IDictionary<string, object> _extendedProperties;

        public ConcreteClassMapBuilder(IClassMap classMap)
        {
            _classMap = classMap;
            _instance = classMap.CreateInstance();

            #region norsd
            _bIsClass = _instance.GetType().IsClass;
            if (!_bIsClass)
            {
                var fields = _instance.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
                foreach (var fi in fields)
                {
                    SETSTVALUE setter = null;
                    _s_dtStSetter.TryGetValue(fi, out setter);                  
                    if(setter == null)
                    {
                        setter = _CreateStSetter(_instance.GetType(),fi);
                        if(null == setter)
                            continue;
                        _s_dtStSetter.Add(fi, setter);
                    }                  
                    _dtStSetter.Add(fi.Name, setter);
                }
            }
            #endregion

            if(!_classMap.HasExtendedProperties)
                return;
            
            var extPropType = _classMap.ExtendedPropertiesMap.MemberReturnType;
            if (extPropType == typeof(IDictionary<string, object>))
                extPropType = typeof(Dictionary<string, object>);
            _extendedProperties = (IDictionary<string, object>)Activator.CreateInstance(extPropType);
            _classMap.ExtendedPropertiesMap.SetValue(_instance, _extendedProperties);
           
        }

        public void AddProperty(string name, object value)
        {
            #region Original Codes
            //var memberMap = _classMap.GetMemberMapFromAlias(name);
            //if (memberMap != null)
            //    memberMap.SetValue(_instance, value);
            //else if ((!_classMap.HasDiscriminator || _classMap.DiscriminatorAlias != name) && _extendedProperties != null)
            //    _extendedProperties.Add(name, value);
            #endregion
            #region norsd Codes
            if(_bIsClass)
            {
                var memberMap = _classMap.GetMemberMapFromAlias(name);
                if (memberMap != null)
                    memberMap.SetValue(_instance, value);
                else if ((!_classMap.HasDiscriminator || _classMap.DiscriminatorAlias != name) && _extendedProperties != null)
                    _extendedProperties.Add(name, value);
            }
            else
            {
                _SetStructValue(name, ref _instance, value);
            }
            #endregion
        }

        public object BuildObject()
        {
            return _instance;           
        }

        public PropertyDescriptor GetPropertyDescriptor(string name)
        {
            var memberMap = _classMap.GetMemberMapFromAlias(name);
            if (memberMap == null)
                return null;

            var type = memberMap.MemberReturnType;
            var isDictionary = false;
            if (memberMap is CollectionMemberMap)
                type = ((CollectionMemberMap)memberMap).ElementType;
            else if (memberMap is DictionaryMemberMap)
            {
                type = ((DictionaryMemberMap)memberMap).ValueType;
                isDictionary = true;
            }

            return new PropertyDescriptor { Type = type, IsDictionary = isDictionary };
        }

        #region norsd
        delegate void SETSTVALUE(ref object instance, object value);
        void _SetStructValue(string arg_strName , ref object arg_rInstance , object value)
        {
            SETSTVALUE setter = null;
            _dtStSetter.TryGetValue(arg_strName ,out setter);
            if (setter == null)
            {
                //throw new NotImplementedException(); 
            }
            else
            {
                setter(ref arg_rInstance, value);
            }
        }
        SETSTVALUE _CreateStSetter(Type arg_sourceType, System.Reflection.FieldInfo arg_fieldinfo)
        {
            var sourceType = arg_sourceType;
            var fieldInfo = arg_fieldinfo;
            var method2 = new System.Reflection.Emit.DynamicMethod("Set" + fieldInfo.Name, null, new[] { typeof(object).MakeByRefType(), typeof(object) }, true);
            var g = method2.GetILGenerator();
            var local = g.DeclareLocal(sourceType, true);
            g.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
            g.Emit(System.Reflection.Emit.OpCodes.Ldind_Ref);
            g.Emit(System.Reflection.Emit.OpCodes.Unbox_Any, sourceType);
            g.Emit(System.Reflection.Emit.OpCodes.Stloc_0);//将前面Load的数据Set到Local 0
            g.Emit(System.Reflection.Emit.OpCodes.Ldloca_S, local);
            g.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
            g.Emit(System.Reflection.Emit.OpCodes.Unbox_Any, fieldInfo.FieldType);
            g.Emit(System.Reflection.Emit.OpCodes.Stfld, fieldInfo);
            g.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
            g.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
            g.Emit(System.Reflection.Emit.OpCodes.Box, sourceType);
            g.Emit(System.Reflection.Emit.OpCodes.Stind_Ref);
            g.Emit(System.Reflection.Emit.OpCodes.Ret);

            var setter = (SETSTVALUE)method2.CreateDelegate(typeof(SETSTVALUE));
            return setter;
        }
        bool _bIsClass = false;
        readonly System.Collections.Generic.Dictionary<string, SETSTVALUE> _dtStSetter = new Dictionary<string, SETSTVALUE>();
        //全局缓存,
        readonly static System.Collections.Generic.Dictionary<System.Reflection.FieldInfo, SETSTVALUE> _s_dtStSetter = new Dictionary<System.Reflection.FieldInfo, SETSTVALUE>();
        #endregion
    }
}


        


       

posted on   norsd  阅读(225)  评论(0编辑  收藏  举报

编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示