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的代码度量值。
标签: