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."); }
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 } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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 加持,客户体验更智能