Dapper

Dapper目前的Bug以已经很少了。可以被正式的项目中使用。所以算是一个值得使用的ORM了。

github上的代码有段时间没有更新了,也懒得等官方去修正,所以自己动手丰衣足食吧。个人修改后的代码在点这里下载。懒得读下文的直接下载更新包吧。修改的地方不多,并不影响官方的更新。

大体的BUG有这些:

Bug1:Mysql和Mssql中的Bit类型的问题

直接修改代码,代码的第795行,增加以下代码。

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This method is for internal usage only", false)]
        public static bool ReadBool(object value)
        {
            if (value.GetType() != typeof(bool))
            {
                return Convert.ToBoolean(value);
            }
            else
                return (bool)value;
        }

代码的第1204行增加这一段代码。

    if (memberType == typeof(Boolean))
                    {
                        il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ReadBool", BindingFlags.Static | BindingFlags.Public), null); 
                    }

错误的原因就是之前的代码对于Bool未做处理,所以导致在Bit字段被当成UInt64处理而在Map的布尔时候类型转化异常。


BUG2:其实这个并不属于BUG,只是不同的数据库对应的类型不同。比如Oracle数据库中的VARCHAR2字段对应的 DbType.AnsiString。侧用以下代码来实例化字段即可。有些版本上没有这个部分。如没有加上即可。

  public sealed class DbString
    {
        public DbString() { Length = -1; }

        public bool IsAnsi { get; set; }
      
        public bool IsFixedLength { get; set; }

        public int Length { get; set; }

        public string Value { get; set; }

        public void AddParameter(IDbCommand command, string name)
        {
            if (IsFixedLength && Length == -1)
            {
                throw new InvalidOperationException("If specifying IsFixedLength,  a Length must also be specified");
            }
            var param = command.CreateParameter();
            param.ParameterName = name;
            param.Value = (object)Value ?? DBNull.Value;
            if (Length == -1 && Value != null && Value.Length <= 4000)
            {
                param.Size = 4000;
            }
            else
            {
                param.Size = Length;
            }
            param.DbType = IsAnsi ? (IsFixedLength ? DbType.AnsiStringFixedLength : DbType.AnsiString) : (IsFixedLength ? DbType.StringFixedLength : DbType.String);
            command.Parameters.Add(param);
        }
    }

操作代码:

            IDbCommand idc = new OracleCommand();
            DbString ds = new DbString();
            ds.IsAnsi = true;
            ds.Value = "";
            ds.AddParameter(idc, "@Content");

不少喜欢POCO以及IRepositories的朋友可以装这个扩展.

using System;
using System.Linq;
using System.Data;
using System.Text;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace SqlMapper
{

    [AttributeUsage(AttributeTargets.Property)]
    public class KeyAttribute : Attribute
    {
    }

    public static class DapperExtensions
    {
        public static void Insert<T>(this IDbConnection connection, T entityvalue)
        {
            Insert(connection, null, entityvalue);
        }

        public static void Insert<T>(this IDbConnection connection, string sql, T entityvalue)
        {
            var name = entityvalue.GetType().Name;
            var sb = new StringBuilder(sql);
            if (sql == null)
            {
                sb.AppendFormat("insert into {0}", name);
                sb.Append(" (");
                BuildInsertParameters(entityvalue, sb);
                sb.Append(") values (");
                BuildInsertValues(entityvalue, sb);
                sb.Append(")");

            }
            connection.Execute(sb.ToString(), entityvalue);
        }

        public static int Update<T>(this IDbConnection connection, T entityvalue)
        {
            return Update(connection, null, entityvalue);
        }

        public static int Update<T>(this IDbConnection connection, string sql, T entityvalue)
        {
            var idProps = GetIdProperties(entityvalue);
            if (idProps.Count() == 0)
                throw new ArgumentException("Entity must have at least one [Key] property");

            var name = entityvalue.GetType().Name;

            var sb = new StringBuilder(sql);
            if (sql == null)
                sb.AppendFormat("update {0}", name);

            sb.AppendFormat(" set ");
            BuildUpdateSet(entityvalue, sb);
            sb.Append(" where ");
            BuildWhere(sb, idProps.ToArray());

            return connection.Execute(sb.ToString(), entityvalue);
        }

        public static int Delete<T>(this IDbConnection connection, T entityvalue)
        {
            return Delete(connection, null, entityvalue);
        }

        public static int Delete<T>(this IDbConnection connection, string sql, T entityvalue)
        {
            var idProps = typeof(T).IsAnonymousType() ?
                GetAllProperties(entityvalue) :
                GetIdProperties(entityvalue);

            if (idProps.Count() == 0)
                throw new ArgumentException("Entity must have at least one [Key] property");

            var name = entityvalue.GetType().Name;

            var sb = new StringBuilder(sql);
            if (sql == null)
                sb.AppendFormat("delete from {0}", name);

            sb.Append(" where ");
            BuildWhere(sb, idProps);

            return connection.Execute(sb.ToString(), entityvalue);
        }

        private static void BuildUpdateSet(object entityToUpdate, StringBuilder sb)
        {
            var nonIdProps = GetNonIdProperties(entityToUpdate).ToArray();

            for (var i = 0; i < nonIdProps.Length; i++)
            {
                var property = nonIdProps[i];

                sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
                if (i < nonIdProps.Length - 1)
                    sb.AppendFormat(", ");
            }
        }

        private static void BuildWhere(StringBuilder sb, IEnumerable<PropertyInfo> idProps)
        {
            for (var i = 0; i < idProps.Count(); i++)
            {
                sb.AppendFormat("{0} = @{1}", idProps.ElementAt(i).Name, idProps.ElementAt(i).Name);
                if (i < idProps.Count() - 1)
                    sb.AppendFormat(" and ");
            }
        }

        private static void BuildInsertValues(object entityToInsert, StringBuilder sb)
        {
            var props = GetAllProperties(entityToInsert);

            for (var i = 0; i < props.Count(); i++)
            {
                var property = props.ElementAt(i);
                if (property.GetCustomAttributes(true).Where(a => a is KeyAttribute).Any()) continue;
                sb.AppendFormat("@{0}", property.Name);
                if (i < props.Count() - 1)
                    sb.Append(", ");
            }
        }

        private static void BuildInsertParameters(object entityToInsert, StringBuilder sb)
        {
            var props = GetAllProperties(entityToInsert);

            for (var i = 0; i < props.Count(); i++)
            {
                var property = props.ElementAt(i);
                if (property.GetCustomAttributes(true).Where(a => a is KeyAttribute).Any()) continue;
                sb.Append(property.Name);
                if (i < props.Count() - 1)
                    sb.Append(", ");
            }
        }

        private static IEnumerable<PropertyInfo> GetAllProperties(object entity)
        {
            return entity.GetType().GetProperties();
        }

        private static IEnumerable<PropertyInfo> GetNonIdProperties(object entity)
        {
            return GetAllProperties(entity).Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute) == false);
        }

        private static IEnumerable<PropertyInfo> GetIdProperties(object entity)
        {
            return GetAllProperties(entity).Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute));
        }
    }

    public static class TypeExtension
    {
        public static Boolean IsAnonymousType(this Type type)
        {
            if (type == null) return false;

            var hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
            var nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
            var isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;

            return isAnonymousType;
        }
    }
}

于是数据库的操作就简化为了这样。从这里可以看出其实Dapper在扩展方面完全要超过PetaPoco,并且更加适合DIY。对于关于数据库主从分离,读写分离以及扩展可以参考我上篇的博文。点此进入

            var dogs = conn.Query<Dog>("select * from dog where id = 1", null, null, true, 30, CommandType.Text).SingleOrDefault();
            conn.Insert<Dog>(dogs);
            conn.Update<dogs>(dogs);
            conn.Delete<Dog>(dogs);

Dapper简单并且高效,效率接近于手写的IDateReader,高于DateTable,所以对于效率极其推崇的可以使用下。

附Dapper的代码度量值。

原文地址:https://www.cnblogs.com/Leo_wl/p/5992198.html