2017-07-11 5 views
0

J'ai un class qui met en œuvre un certain interface:Exception avec le message « Aucune colonne n'a été mis en correspondance » à l'aide Dapper Extensions

public interface IDb {} 
public class DbModel : IDb {} 

Après cela, j'utilise des extensions Dapper pour insérer l'objet dans le DB. Ce code fonctionne bien:

var obj = new DbModel(); 
sqlConnection.Insert(obj); 

Mais lorsque je tente d'insérer une instance de cette class, coulée sur la correspondante interface, il donne une exception:

IDb obj = new DbModel(); 
sqlConnection.Insert(obj); // exception here 

System.ArgumentException: ' Aucune colonne n'a été mappée. '

Répondre

0

Raison pour laquelle il fonctionne pas:

Partant du fait que, la méthode DapperExtensions.Insert est générique, DapperExstensions ne peut pas trouver la carte correspondante pour IDb

Solution:

Créer une classe de mappeur pour IDb et enregistrer DbModel propriétés

public sealed class IDbMapper : ClassMapper<IDb> 
{ 
    public IDbMapper() 
    { 
     base.Table("TableName"); 
     Map(m => new DbModel().Title); 
     // and such mapping for other properties 
    } 
} 

Après cela, il sera le travail:

IDb obj = new DbModel(); 
sqlConnection.Insert(obj); 

Mais voici un problème, quand nous avons de nombreuses implémentations de IDb interface, dans IDbMapper config nous ne pouvons pas la carte colonnes correspondant tables ici:

base.Table("TableName"); 
Map(m => new DbModel().Title); 

Parce que nous ne connaissons pas le type d'instance d'objet lorsque nous faisons mapp ing.

Edit:

Après quelques debuging je remarque que, dans chaque appel de méthode Insert, faire pimpant cartographie et construire correspondante ClassMapper<> classe. Nous pouvons utiliser cette étrangeté. Pour cela, nous devons créer SharedState et stocker le type d'instance avant d'appeler la méthode Insert.

public static class DapperExstensionsExstensions 
{ 
    public static Type SharedState_ModelInstanceType { get; set; } 
    ... 
} 

IDb obj = new DbModel(); 
DapperExstensionsExstensions.SharedState_ModelInstanceType = obj.GetType(); 
sqlConnection.Insert(obj); 

Après cela, nous pouvons accéder à cette propriété quand Dapper fera la cartographie

public sealed class IDbMapper : ClassMapper<IDb> 
{ 
    public IDbMapper() 
    { 
     // here we can retrieve Type of model instance and do mapping using reflection 
     DapperExstensionsExstensions.SharedState_ModelInstanceType 
    } 
} 

extrait de code entier:

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; 
    } 
} 

Il travaille pour moi

IDb obj = new DbModel(); 
sqlConnection.Insert(obj);