1、默认的类型映射,直接以实体属性(或字段)映射到SQL中字段,(名称可以忽略大小写)
2、如果字段带下划线的,设置Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 即可。
3、正因为有这个DefaultTypeMap默认的映射策略,所以,一般情况下,可以直接通过SQL查询,生成实体List.
4、Dapper还是以数据库为中心的思考问题模式,不同于Entity framework和nhibernate,提倡以实体到DB的思考问题方式。
5、Dapper在处理实体关联方面算是硬伤了,只能程序员自已根据DTO的模型去自已动手组装了。
附上代码
/// <summary> /// 表示Dapper默认的类型映射策略 /// Represents default type mapping strategy used by Dapper /// </summary> sealed partial class DefaultTypeMap : SqlMapper.ITypeMap { /// <summary> /// 要映射的实体,所有的字段 /// </summary> private readonly List<FieldInfo> _fields; /// <summary> /// 要映射的实体,所有的属性 /// </summary> private readonly List<PropertyInfo> _properties; /// <summary> /// 要映射的实体类型 /// </summary> private readonly Type _type; /// <summary> /// 构造函数,创建缺省的类型映射策略 /// Creates default type map /// </summary> /// <param name="type">Entity type</param> public DefaultTypeMap(Type type) { if (type == null) throw new ArgumentNullException("type"); _fields = GetSettableFields(type); _properties = GetSettableProps(type); _type = type; } /// <summary> /// 获取属性的setter访问器 /// </summary> /// <param name="propertyInfo"></param> /// <param name="type"></param> /// <returns></returns> internal static MethodInfo GetPropertySetter(PropertyInfo propertyInfo, Type type) { return propertyInfo.DeclaringType == type ? propertyInfo.GetSetMethod(true) : propertyInfo.DeclaringType.GetProperty( propertyInfo.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, Type.DefaultBinder, propertyInfo.PropertyType, propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(), null).GetSetMethod(true); } /// <summary> /// 获取某类型所有可写(有set,包括私有)的成员属性 /// </summary> /// <param name="t"></param> /// <returns></returns> internal static List<PropertyInfo> GetSettableProps(Type t) { return t .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Where(p => GetPropertySetter(p, t) != null) .ToList(); } /// <summary> /// 获取某类型所有的成员字段 /// </summary> /// <param name="t"></param> /// <returns></returns> internal static List<FieldInfo> GetSettableFields(Type t) { return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList(); } /// <summary> /// 获取最佳的成员构造器,优先顺序:public的构造器0, 其它构造器1, private的构造器2,然后再按参数匹配 /// Finds best constructor /// </summary> /// <param name="names">DataReader column names</param> /// <param name="types">DataReader column types</param> /// <returns>Matching constructor or default one</returns> public ConstructorInfo FindConstructor(string[] names, Type[] types) { var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (ConstructorInfo ctor in constructors.OrderBy(c => c.IsPublic ? 0 : (c.IsPrivate ? 2 : 1)).ThenBy(c => c.GetParameters().Length)) { ParameterInfo[] ctorParameters = ctor.GetParameters(); if (ctorParameters.Length == 0) return ctor; if (ctorParameters.Length != types.Length) continue; int i = 0; for (; i < ctorParameters.Length; i++) { if (!String.Equals(ctorParameters[i].Name, names[i], StringComparison.OrdinalIgnoreCase)) break; if (types[i] == typeof(byte[]) && ctorParameters[i].ParameterType.FullName == SqlMapper.LinqBinary) continue; var unboxedType = Nullable.GetUnderlyingType(ctorParameters[i].ParameterType) ?? ctorParameters[i].ParameterType; if (unboxedType != types[i] && !(unboxedType.IsEnum && Enum.GetUnderlyingType(unboxedType) == types[i]) && !(unboxedType == typeof(char) && types[i] == typeof(string))) break; } if (i == ctorParameters.Length) return ctor; } return null; } /// <summary> /// 查找带ExplicitConstructorAttribute特性标识的构造器 /// Returns the constructor, if any, that has the ExplicitConstructorAttribute on it. /// </summary> public ConstructorInfo FindExplicitConstructor() { var constructors = _type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); var withAttr = constructors.Where(c => c.GetCustomAttributes(typeof(ExplicitConstructorAttribute), true).Length > 0).ToList(); if (withAttr.Count == 1) { return withAttr[0]; } return null; } /// <summary> /// 根据构造函数和列名 /// Gets mapping for constructor parameter /// </summary> /// <param name="constructor">Constructor to resolve</param> /// <param name="columnName">DataReader column name</param> /// <returns>Mapping implementation</returns> public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName) { var parameters = constructor.GetParameters(); //注意构造器参数名与列名是相同的(忽略大小写) return new SimpleMemberMap(columnName, parameters.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase))); } /// <summary> /// 为某列找到映射关系,返回IMemberMap /// Gets member mapping for column /// </summary> /// <param name="columnName">DataReader column name</param> /// <returns>Mapping implementation</returns> public SqlMapper.IMemberMap GetMember(string columnName) { //优先找与列名相同的属性名(优先找大小写相同的,再找不同的)。 var property = _properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal)) ?? _properties.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)); //如果找不到,就尝试去掉列名下划线,再找一次。 if (property == null && MatchNamesWithUnderscores) { property = _properties.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal)) ?? _properties.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase)); } //如果找到就返回一个成员映射对象 if (property != null) return new SimpleMemberMap(columnName, property); //如果没有找到匹配的属性,就找DomainObject.Field,按照上面的套路再找一次。 var field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.Ordinal)) ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName, StringComparison.OrdinalIgnoreCase)); if (field == null && MatchNamesWithUnderscores) { field = _fields.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.Ordinal)) ?? _fields.FirstOrDefault(p => string.Equals(p.Name, columnName.Replace("_", ""), StringComparison.OrdinalIgnoreCase)); } if (field != null) return new SimpleMemberMap(columnName, field); return null; } /// <summary> /// 按列名匹配属性时,忽略下划线(有些时习惯在数据库中使用下划线)。 /// Should column names like User_Id be allowed to match properties/fields like UserId ? /// </summary> public static bool MatchNamesWithUnderscores { get; set; } }
时间: 2024-10-08 02:07:28