我是Dapper微型ORM的新手。到目前为止,我能够将它用于简单的ORM相关的东西,但我不能将数据库列名与类属性映射。

例如,我有如下的数据库表:

Table Name: Person
person_id  int
first_name varchar(50)
last_name  varchar(50)

我有一个叫Person的类:

public class Person 
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

请注意,表中的列名与我试图将从查询结果中获得的数据映射到的类的属性名不同。

var sql = @"select top 1 PersonId,FirstName,LastName from Person";
using (var conn = ConnectionFactory.GetConnection())
{
    var person = conn.Query<Person>(sql).ToList();
    return person;
}

上面的代码将无法工作,因为列名不匹配对象的(Person)属性。在这种情况下,有什么我可以在Dapper手动映射(例如person_id => PersonId)与对象属性的列名?


当前回答

在打开到数据库的连接之前,为每个poco类执行这段代码:

// Section
SqlMapper.SetTypeMap(typeof(Section), new CustomPropertyTypeMap(
    typeof(Section), (type, columnName) => type.GetProperties().FirstOrDefault(prop =>
    prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))));

然后像这样将数据注释添加到poco类中:

public class Section
{
    [Column("db_column_name1")] // Side note: if you create aliases, then they would match this.
    public int Id { get; set; }
    [Column("db_column_name2")]
    public string Title { get; set; }
}

之后,你就万事俱备了。只需要进行查询调用,就像这样:

using (var sqlConnection = new SqlConnection("your_connection_string"))
{
    var sqlStatement = "SELECT " +
                "db_column_name1, " +
                "db_column_name2 " +
                "FROM your_table";

    return sqlConnection.Query<Section>(sqlStatement).AsList();
}

其他回答

对于所有使用Dapper 1.12的人,以下是你需要做的事情:

添加一个新的列属性类:

  [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]

  public class ColumnAttribute : Attribute
  {

    public string Name { get; set; }

    public ColumnAttribute(string name)
    {
      this.Name = name;
    }
  }

搜索这一行:

map = new DefaultTypeMap(type);

然后注释掉。

你可以这样写:

        map = new CustomPropertyTypeMap(type, (t, columnName) =>
        {
          PropertyInfo pi = t.GetProperties().FirstOrDefault(prop =>
                            prop.GetCustomAttributes(false)
                                .OfType<ColumnAttribute>()
                                .Any(attr => attr.Name == columnName));

          return pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName);
        });

实现这一点的一个简单方法是在查询中的列上使用别名。

如果您的数据库列是PERSON_ID,对象的属性是ID,您可以这样做

select PERSON_ID as Id ...

在您的查询中,Dapper将按预期将其拾取。

取自Dapper测试,目前在Dapper 1.42上。

// custom mapping
var map = new CustomPropertyTypeMap(typeof(TypeWithMapping), 
                                    (type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName));
Dapper.SqlMapper.SetTypeMap(typeof(TypeWithMapping), map);

Helper类从Description属性中获取名称(我个人使用类似@kalebs的Column示例)

static string GetDescriptionFromAttribute(MemberInfo member)
{
   if (member == null) return null;

   var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false);
   return attrib == null ? null : attrib.Description;
}

public class TypeWithMapping
{
   [Description("B")]
   public string A { get; set; }

   [Description("A")]
   public string B { get; set; }
}

如果您使用的是。net 4.5.1或更高版本的结帐Dapper。FluentColumnMapping用于映射LINQ样式。它可以让你完全将db映射与你的模型分开(不需要注释)

更简单的方法(与@Matt M的答案相同,但更正并添加了默认地图的回退)

// override TypeMapProvider to return custom map for every requested type
Dapper.SqlMapper.TypeMapProvider = type =>
   {
       // create fallback default type map
       var fallback = new DefaultTypeMap(type);
       return new CustomPropertyTypeMap(type, (t, column) =>
       {
           var property = t.GetProperties().FirstOrDefault(prop =>
               prop.GetCustomAttributes(typeof(ColumnAttribute))
                   .Cast<ColumnAttribute>()
                   .Any(attr => attr.Name == column));

           // if no property matched - fall back to default type map
           if (property == null)
           {
               property = fallback.GetMember(column)?.Property;
           }

           return property;
       });
   };