不起作用的原因:
从方法是通用的事实DapperExtensions.Insert
出发,DapperExstensions 找不到对应的映射IDb
解决方案:
为属性创建映射器类IDb
并注册DbModel
public sealed class IDbMapper : ClassMapper<IDb>
{
public IDbMapper()
{
base.Table("TableName");
Map(m => new DbModel().Title);
// and such mapping for other properties
}
}
在此之后它将起作用:
IDb obj = new DbModel();
sqlConnection.Insert(obj);
但是这里有一个问题,当我们有很多IDb
接口的实现时,在IDbMapper
配置中我们不能在这里将列映射到对应的表:
base.Table("TableName");
Map(m => new DbModel().Title);
因为我们在进行映射时不知道对象实例的类型。
编辑:
经过一些调试,我注意到,在每个Insert
方法调用中,dapper 都会进行映射,并构造相应ClassMapper<>
的类。我们可以利用这种怪异。
为此,我们应该在调用Insert
方法之前创建 SharedState 并在其中存储实例类型。
public static class DapperExstensionsExstensions
{
public static Type SharedState_ModelInstanceType { get; set; }
...
}
IDb obj = new DbModel();
DapperExstensionsExstensions.SharedState_ModelInstanceType = obj.GetType();
sqlConnection.Insert(obj);
在此之后,我们可以在 Dapper 进行映射时访问此属性
public sealed class IDbMapper : ClassMapper<IDb>
{
public IDbMapper()
{
// here we can retrieve Type of model instance and do mapping using reflection
DapperExstensionsExstensions.SharedState_ModelInstanceType
}
}
整个代码片段:
public interface IDb { }
[MapConfig("TableName", "Schema")]
public class DbTemp : IDb
{
public string Title { get; set; }
}
public class MapConfigAttribute : Attribute
{
public MapConfigAttribute(string name, string schema)
{
Name = name;
Schema = schema;
}
public string Name { get; }
public string Schema { get; }
}
public sealed class DbMapper : ClassMapper<IDb>
{
public DbMapper()
{
DapperExstensionsExstensions.CorrespondingTypeMapper<IDb>((tableName, sechemaName, exprs) =>
{
Table(tableName);
Schema(SchemaName);
return exprs.Select(Map);
});
}
}
public static class DapperExstensionsExstensions
{
private static readonly object _LOCK = new object();
public static Type SharedState_ModelInstanceType { get; set; }
public static List<PropertyMap> CorrespondingTypeMapper<T>(Func<string, string, IEnumerable<Expression<Func<T, object>>>, IEnumerable<PropertyMap>> callback)
{
var tableNameAttribute = (MapConfigAttribute)SharedState_ModelInstanceType.GetCustomAttribute(typeof(MapConfigAttribute));
var tableName = tableNameAttribute.Name;
var schemaName = tableNameAttribute.Schema;
var result = callback(tableName, schemaName, new GetPropertyExpressions<T>(SharedState_ModelInstanceType));
Monitor.Exit(_LOCK);
return result.ToList();
}
public static object Insert<TInput>(this IDbConnection connection, TInput entity) where TInput : class
{
Monitor.Enter(_LOCK);
SharedState_ModelInstanceType = entity.GetType();
return DapperExtensions.DapperExtensions.Insert(connection, entity);
}
}
public class GetPropertyExpressions<TInput> : IEnumerable<Expression<Func<TInput, object>>>
{
private readonly Type _instanceType;
public GetPropertyExpressions(Type instanceType)
{
_instanceType = instanceType;
}
public IEnumerator<Expression<Func<TInput, object>>> GetEnumerator()
{
return _instanceType
.GetProperties()
.Select(p => new GetPropertyExpression<TInput>(_instanceType, p.Name).Content()).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class GetPropertyExpression<TInput> : IContent<Expression<Func<TInput, object>>>
{
private readonly Type _instanceType;
private readonly string _propertyName;
public GetPropertyExpression(Type instanceType, string propertyName)
{
_instanceType = instanceType;
_propertyName = propertyName;
}
public Expression<Func<TInput, object>> Content()
{
// Expression<Func<IDb, object>> :: model => (object)(new DbModel().Title)
var newInstance = Expression.New(_instanceType);
var parameter = Expression.Parameter(typeof(TInput), "model");
var getPropertyExpression = Expression.Property(newInstance, _propertyName);
var convertedProperty = Expression.Convert(getPropertyExpression, typeof(object));
var lambdaExpression = Expression.Lambda(convertedProperty, parameter);
return (Expression<Func<TInput, object>>)lambdaExpression;
}
}
这个对我有用
IDb obj = new DbModel();
sqlConnection.Insert(obj);