一直使用 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
}
}

MongoDb Samus 驱动的改进的更多相关文章

  1. Mongodb的Samus驱动

    最近开始学习Mongodb方面的东西.. 看到有很多博主都说MongoDB的第三方驱动 Samus 对Linq的支持比较好..能够降低学习的成本..所以就想从这里开始.. 但是弊端在我学习了一半的时候 ...

  2. 通过MongoDB的samus驱动实现基本数据操作

    一.MongoDB的驱动 MongoDB支持多种语言的驱动: 在此我们只介绍 C# 的驱动.仅C#驱动都有很多种,每种驱动的形式大致相同,但是细节各有千秋,因此代码不能通用.比较常用的是官方驱动和sa ...

  3. MongoDB学习笔记(二) 通过samus驱动实现基本数据操作

    传统的关系数据库一般由数据库(database).表(table).记录(record)三个层次概念组成,MongoDB是由(database).集合(collection).文档对象(documen ...

  4. MongoDB C#samus驱动

    MongoDB的c#驱动有两种,官方驱动和samus驱动,不过我更喜欢samus驱动,因为samus驱动提供了丰富的linq操作. 官方驱动:https://github.com/mongodb/mo ...

  5. Samus驱动中的Document条件

    今天要说一个东西就是Samus驱动里的 Document  和他的一个子类 Op 在Samus驱动的增删改查方法中都有这类的参数传递.. 大致的使用方法是这样.. MongoU.Find<Per ...

  6. 通过samus驱动实现基本数据操作

    传统的关系数据库一般由数据库(database).表(table).记录(record)三个层次概念组成,MongoDB是由(database).集合(collection).文档对象(documen ...

  7. (转载)MongoDB C#驱动中Query几个方法

    MongoDB C#驱动中Query几个方法 Query.All("name", "a", "b");//通过多个元素来匹配数组 Query ...

  8. 使用VS2010编译MongoDB C++驱动详解

    最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...

  9. MongoDB C#驱动:

    MongoDB C#驱动: http://xiaosheng.me/2016/09/15/article24 http://www.cnblogs.com/wuhuacong/p/5098348.ht ...

随机推荐

  1. C++ 坑人系列(1): 让面试官晕倒的题目

     今天和几位同仁一起探讨了一下C++的一些基础知识,在座的同仁都是行家了,有的多次当过C++技术面试官.不过我出的题过于刁钻: 不是看起来太难,而是看起来极其容易,但是其实非常难! 结果一圈下来,3道 ...

  2. ByteBuf(图解1)

    目录 源码工程 写在前面 Netty ByteBuf 优势 手动获取与释放ByteBuf 自动获取和释放 ByteBuf 方式一:TailHandler 自动释放 方式二:SimpleChannelI ...

  3. JAVA工厂方法模式(Factory Method)

    1.普通工厂模式 普通工厂模式:就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 1-1.建立Sender接口 public interface Sender { public void ...

  4. 20170314 OO ALV 出现双滚动条

    1.出现双进度条,用户改变屏幕大小操作出现问题: 解决方法:  [园童]BJ-ABAP-可乐(708925365)  16:08:55240 * 200改为240 200,然后将滚动条的步进改为1即可 ...

  5. hibernate入门(-)

    1.struts2的支持 在web.xml中配置struts2的支持 <?xml version="1.0" encoding="UTF-8"?> ...

  6. html5--5-5 绘制填充矩形

    html5--5-5 绘制填充矩形 学习要点 掌握绘制矩形的方法:strkeRect()/fillRect() 掌握绘制路径的 beginPath()和closePath() 矩形的绘制方法 rect ...

  7. SQL server 备份/恢复/压缩 进度查询

    第一步,用 sp_who2 查出备份的sid(或在窗口中的连接属性中看) exec sp_who2 第二步,用以下查询获得运行情况(看 percent_complete列) SELECT sessio ...

  8. C++日志之获取函数的名字,行号,文件名

    在后台程序运行出问题时,详尽的日志是抓错不可缺少的帮手,这里提供一个能自动记录日志触发点文件名.行号.函数名的方法,关键是利用C99新增的预处理标识符__VA_ARGS__ 先介绍几个编译器内置的宏定 ...

  9. C++基础--完善Socket C/S ,实现客户端,服务器端断开重连

    // WindowsSocketServer.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h"#include <iostream> ...

  10. 给YUI Compressor添加右键命令,完成快捷压缩

    YUI Compressor默认不带右键安装功能 YUI Compressor非常好用,特别是JS的混淆是众多JS Coding的最爱.可惜官网提供的版本都不具备右键功能,每次压缩都要cmd输入一些命 ...