日期:2014-05-16  浏览次数:20350 次

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 )
  &nb