使用 Dapper Extensions 时出现消息 'No columns were mapped' 的异常
Exception with message 'No columns were mapped' using Dapper Extensions
我有一个 class
实现了一些 interface
:
public interface IDb {}
public class DbModel : IDb {}
在此之后,我使用 Dapper Extensions 将对象插入到数据库中。此代码运行良好:
var obj = new DbModel();
sqlConnection.Insert(obj);
但是当我尝试插入这个 class
的实例时,在相应的 interface
上进行转换时,它给出了一个异常:
IDb obj = new DbModel();
sqlConnection.Insert(obj); // exception here
System.ArgumentException: 'No columns were mapped.'
不工作的原因:
从DapperExtensions.Insert
方法是通用的这一事实出发,DapperExstensions无法为IDb
找到对应的映射
解法:
为 IDb
创建映射器 class 并注册 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);
But here is a problem, when we have many implementations of IDb
interface, in IDbMapper
config we can't map columns to corresponding
tables here:
base.Table("TableName");
Map(m => new DbModel().Title);
Because we don't know type of object instance when we do mapping.
编辑:
调试后发现,在每次Insert
方法调用中,dapper都会做映射,并构造对应的ClassMapper<>
class。我们可以利用这个怪癖。
为此,我们应该在调用 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);
我有一个 class
实现了一些 interface
:
public interface IDb {}
public class DbModel : IDb {}
在此之后,我使用 Dapper Extensions 将对象插入到数据库中。此代码运行良好:
var obj = new DbModel();
sqlConnection.Insert(obj);
但是当我尝试插入这个 class
的实例时,在相应的 interface
上进行转换时,它给出了一个异常:
IDb obj = new DbModel();
sqlConnection.Insert(obj); // exception here
System.ArgumentException: 'No columns were mapped.'
不工作的原因:
从DapperExtensions.Insert
方法是通用的这一事实出发,DapperExstensions无法为IDb
解法:
为 IDb
创建映射器 class 并注册 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);
But here is a problem, when we have many implementations of
IDb
interface, inIDbMapper
config we can't map columns to corresponding tables here:base.Table("TableName"); Map(m => new DbModel().Title);
Because we don't know type of object instance when we do mapping.
编辑:
调试后发现,在每次Insert
方法调用中,dapper都会做映射,并构造对应的ClassMapper<>
class。我们可以利用这个怪癖。
为此,我们应该在调用 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);