手动将列名与类属性映射

发布于 2024-12-27 10:54:03 字数 795 浏览 1 评论 0原文

我是 Dapper micro 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;
}

上面的代码将不起作用,因为列名称与对象的(人)属性不匹配。在这种情况下,我可以在 Dapper 中做些什么来手动映射(例如 person_id => PersonId)列名称与对象属性吗?

I am new to the Dapper micro ORM. So far I am able to use it for simple ORM related stuff but I am not able to map the database column names with the class properties.

For example, I have the following database table:

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

and I have a class called Person:

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

Please note that my column names in the table are different from the property name of the class to which I am trying to map the data which I got from the query result.

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

The above code won't work as the column names don't match the object's (Person) properties. In this scenario, is there anything i can do in Dapper to manually map (e.g person_id => PersonId) the column names with object properties?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

窝囊感情。 2025-01-03 10:54:04

取自当前的 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);

用于从描述属性中获取名称的帮助程序类(我个人使用过类似 @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; }
}

Taken from the Dapper Tests which is currently on 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 class to get name off the Description attribute (I personally have used Column like @kalebs example)

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

Class

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

   [Description("A")]
   public string B { get; set; }
}
¢好甜 2025-01-03 10:54:04

在打开与数据库的连接之前,请为每个 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();
}

Before you open the connection to your database, execute this piece of code for each of your poco classes:

// 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))));

Then add the data annotations to your poco classes like this:

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

After that, you are all set. Just make a query call, something like:

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();
}
寒冷纷飞旳雪 2025-01-03 10:54:04

搞乱映射是进入真正的 ORM 领域的边缘。与其与之抗争并保持 Dapper 真正简单(快速)的形式,只需稍微修改您的 SQL,如下所示:

var sql = @"select top 1 person_id as PersonId,FirstName,LastName from Person";

Messing with mapping is borderline moving into real ORM land. Instead of fighting with it and keeping Dapper in its true simple (fast) form, just modify your SQL slightly like so:

var sql = @"select top 1 person_id as PersonId,FirstName,LastName from Person";
不再见 2025-01-03 10:54:04

如果您使用 .NET 4.5.1 或更高版本,请查看 Dapper.FluentColumnMapping 来映射LINQ 风格。它可以让您将数据库映射与模型完全分离(无需注释)

If you're using .NET 4.5.1 or higher checkout Dapper.FluentColumnMapping for mapping the LINQ style. It lets you fully separate the db mapping from your model (no need for annotations)

拥抱我好吗 2025-01-03 10:54:04

这是对其他答案的回避。这只是我管理查询字符串的一个想法。

Person.cs

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

    public static string Select() 
    {
        return $"select top 1 person_id {nameof(PersonId)}, first_name {nameof(FirstName)}, last_name {nameof(LastName)}from Person";
    }
}

API方法

using (var conn = ConnectionFactory.GetConnection())
{
    var person = conn.Query<Person>(Person.Select()).ToList();
    return person;
}

This is piggy backing off of other answers. It's just a thought I had for managing the query strings.

Person.cs

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

    public static string Select() 
    {
        return $"select top 1 person_id {nameof(PersonId)}, first_name {nameof(FirstName)}, last_name {nameof(LastName)}from Person";
    }
}

API Method

using (var conn = ConnectionFactory.GetConnection())
{
    var person = conn.Query<Person>(Person.Select()).ToList();
    return person;
}
瑾夏年华 2025-01-03 10:54:04

Kaleb 试图解决的问题的简单解决方案是,如果列属性不存在,则接受属性名称:

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

The simple solution to the problem Kaleb is trying to solve is just to accept the property name if the column attribute doesn't exist:

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

下雨或天晴 2025-01-03 10:54:04

更简单的方法(与@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;
       });
   };

The easier way (same as @Matt M's answer but corrected and added fallback to default map)

// 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;
       });
   };
锦上情书 2025-01-03 10:54:04

对于所有使用 Dapper 1.12 的人,您需要执行以下操作才能完成此操作:

  • 添加新的列属性类:

     [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]
    
      公共类 ColumnAttribute :属性
      {
    
        公共字符串名称{获取;放; }
    
        公共ColumnAttribute(字符串名称)
        {
          this.Name = 名称;
        }
      }
    

  • 搜索这一行:

    map = new DefaultTypeMap(type);
    

    并将其注释掉。

  • 改为这样写:

     map = new CustomPropertyTypeMap(type, (t, columnName) =>;
            {
              PropertyInfo pi = t.GetProperties().FirstOrDefault(prop =>;
                                prop.GetCustomAttributes(false)
                                    .OfType()
                                    .Any(attr => attr.Name == 列名));
    
              返回 pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName);
            });
    

  • for all of you who use Dapper 1.12, Here's what you need to do to get this done:

  • Add a new column attribute class:

      [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]
    
      public class ColumnAttribute : Attribute
      {
    
        public string Name { get; set; }
    
        public ColumnAttribute(string name)
        {
          this.Name = name;
        }
      }
    
  • Search for this line:

    map = new DefaultTypeMap(type);
    

    and comment it out.

  • Write this instead:

            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);
            });
    
  • 甜宝宝 2025-01-03 10:54:04

    我知道这是一个相对较旧的线程,但我想我应该把我所做的事情扔在那里。

    我希望属性映射能够在全球范围内发挥作用。要么匹配属性名称(也称为默认值),要么匹配类属性上的列属性。我也不想为我映射到的每个类进行设置。因此,我创建了一个在应用程序启动时调用的 DapperStart 类:

    public static class DapperStart
    {
        public static void Bootstrap()
        {
            Dapper.SqlMapper.TypeMapProvider = type =>
            {
                return new CustomPropertyTypeMap(typeof(CreateChatRequestResponse),
                    (t, columnName) => t.GetProperties().FirstOrDefault(prop =>
                        {
                            return prop.Name == columnName || prop.GetCustomAttributes(false).OfType<ColumnAttribute>()
                                       .Any(attr => attr.Name == columnName);
                        }
                    ));
            };
        }
    }
    

    非常简单。当我刚刚写这篇文章时,不确定我会遇到什么问题,但它确实有效。

    I know this is a relatively old thread, but I thought I'd throw what I did out there.

    I wanted attribute-mapping to work globally. Either you match the property name (aka default) or you match a column attribute on the class property. I also didn't want to have to set this up for every single class I was mapping to. As such, I created a DapperStart class that I invoke on app start:

    public static class DapperStart
    {
        public static void Bootstrap()
        {
            Dapper.SqlMapper.TypeMapProvider = type =>
            {
                return new CustomPropertyTypeMap(typeof(CreateChatRequestResponse),
                    (t, columnName) => t.GetProperties().FirstOrDefault(prop =>
                        {
                            return prop.Name == columnName || prop.GetCustomAttributes(false).OfType<ColumnAttribute>()
                                       .Any(attr => attr.Name == columnName);
                        }
                    ));
            };
        }
    }
    

    Pretty simple. Not sure what issues I'll run into yet as I just wrote this, but it works.

    快乐很简单 2025-01-03 10:54:04

    卡莱布·佩德森的解决方案对我有用。我更新了 ColumnAttributeTypeMapper 以允许自定义属性(需要同一域对象上的两个不同映射)并更新属性以允许在需要派生字段且类型不同的情况下使用私有设置器。

    public class ColumnAttributeTypeMapper<T,A> : FallbackTypeMapper where A : ColumnAttribute
    {
        public ColumnAttributeTypeMapper()
            : base(new SqlMapper.ITypeMap[]
                {
                    new CustomPropertyTypeMap(
                       typeof(T),
                       (type, columnName) =>
                           type.GetProperties( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(prop =>
                               prop.GetCustomAttributes(true)
                                   .OfType<A>()
                                   .Any(attr => attr.Name == columnName)
                               )
                       ),
                    new DefaultTypeMap(typeof(T))
                })
        {
            //
        }
    }
    

    Kaleb Pederson's solution worked for me. I updated the ColumnAttributeTypeMapper to allow a custom attribute (had requirement for two different mappings on same domain object) and updated properties to allow private setters in cases where a field needed to be derived and the types differed.

    public class ColumnAttributeTypeMapper<T,A> : FallbackTypeMapper where A : ColumnAttribute
    {
        public ColumnAttributeTypeMapper()
            : base(new SqlMapper.ITypeMap[]
                {
                    new CustomPropertyTypeMap(
                       typeof(T),
                       (type, columnName) =>
                           type.GetProperties( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(prop =>
                               prop.GetCustomAttributes(true)
                                   .OfType<A>()
                                   .Any(attr => attr.Name == columnName)
                               )
                       ),
                    new DefaultTypeMap(typeof(T))
                })
        {
            //
        }
    }
    
    命硬 2025-01-03 10:54:04

    我建议类似于@liorafar的解决方案,但基于字典而不是动态:

    using var conn = ConnectionFactory.GetConnection();
    var person = conn.Query(sql)
        .Cast<IDictionary<string, object>>()
        .Select(record =>
            new Person
            {
                PersonId = (int)record["person_id"],
                FirstName = (string)record["first_name"],
                LastName = (string)record["last_name"],
            })
        .ToList();
    

    在我看来,这个选项对于重构更友好:例如,您可以将列名称声明为常量或从配置中读取它们。此外,与动态解决方案不同,它允许提取将字典转换为模型实例(Person类型的实例)的方法来分离方法,这对于具有多个字段的模型特别有用。

    I would suggest solution similar to @liorafar's, but based on dictionaries rather than on dynamics:

    using var conn = ConnectionFactory.GetConnection();
    var person = conn.Query(sql)
        .Cast<IDictionary<string, object>>()
        .Select(record =>
            new Person
            {
                PersonId = (int)record["person_id"],
                FirstName = (string)record["first_name"],
                LastName = (string)record["last_name"],
            })
        .ToList();
    

    In my opinion, this option is friendlier for refactoring: e.g. you can declare column names as constants or read them from configuration. Additionally, unlike solution with dynamics, it allows to extract method of transforming the dictionary to model instance (instance of Person type) to separate method, which is especially useful for models with many fields.

    你爱我像她 2025-01-03 10:54:03

    Dapper 现在支持自定义列到属性映射器。它通过 ITypeMap 接口执行此操作。 Dapper 提供了 CustomPropertyTypeMap 类,它可以执行大部分操作这项工作。例如:

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

    模型:

    public class TModel {
        [Column(Name="my_property")]
        public int MyProperty { get; set; }
    }
    

    需要注意的是,CustomPropertyTypeMap 的实现要求属性存在并与列名之一匹配,否则属性将不会被映射。 DefaultTypeMap 类提供标准功能,可用于更改此行为:

    public class FallbackTypeMapper : SqlMapper.ITypeMap
    {
        private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;
    
        public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
        {
            _mappers = mappers;
        }
    
        public SqlMapper.IMemberMap GetMember(string columnName)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.GetMember(columnName);
                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException nix)
                {
                // the CustomPropertyTypeMap only supports a no-args
                // constructor and throws a not implemented exception.
                // to work around that, catch and ignore.
                }
            }
            return null;
        }
        // implement other interface methods similarly
    
        // required sometime after version 1.13 of dapper
        public ConstructorInfo FindExplicitConstructor()
        {
            return _mappers
                .Select(mapper => mapper.FindExplicitConstructor())
                .FirstOrDefault(result => result != null);
        }
    }
    

    有了这个,创建一个自定义类型映射器将自动使用属性(如果存在),但否则将回退到标准行为:

    public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper
    {
        public ColumnAttributeTypeMapper()
            : base(new SqlMapper.ITypeMap[]
                {
                    new CustomPropertyTypeMap(
                       typeof(T),
                       (type, columnName) =>
                           type.GetProperties().FirstOrDefault(prop =>
                               prop.GetCustomAttributes(false)
                                   .OfType<ColumnAttribute>()
                                   .Any(attr => attr.Name == columnName)
                               )
                       ),
                    new DefaultTypeMap(typeof(T))
                })
        {
        }
    }
    

    意味着我们现在可以轻松支持需要使用属性进行映射的类型:

    Dapper.SqlMapper.SetTypeMap(
        typeof(MyModel),
        new ColumnAttributeTypeMapper<MyModel>());
    

    这 github.com/kalebpederson/5460509">完整源代码要点。

    Dapper now supports custom column to property mappers. It does so through the ITypeMap interface. A CustomPropertyTypeMap class is provided by Dapper that can do most of this work. For example:

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

    And the model:

    public class TModel {
        [Column(Name="my_property")]
        public int MyProperty { get; set; }
    }
    

    It's important to note that the implementation of CustomPropertyTypeMap requires that the attribute exist and match one of the column names or the property won't be mapped. The DefaultTypeMap class provides the standard functionality and can be leveraged to change this behavior:

    public class FallbackTypeMapper : SqlMapper.ITypeMap
    {
        private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;
    
        public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
        {
            _mappers = mappers;
        }
    
        public SqlMapper.IMemberMap GetMember(string columnName)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.GetMember(columnName);
                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException nix)
                {
                // the CustomPropertyTypeMap only supports a no-args
                // constructor and throws a not implemented exception.
                // to work around that, catch and ignore.
                }
            }
            return null;
        }
        // implement other interface methods similarly
    
        // required sometime after version 1.13 of dapper
        public ConstructorInfo FindExplicitConstructor()
        {
            return _mappers
                .Select(mapper => mapper.FindExplicitConstructor())
                .FirstOrDefault(result => result != null);
        }
    }
    

    And with that in place, it becomes easy to create a custom type mapper that will automatically use the attributes if they're present but will otherwise fall back to standard behavior:

    public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper
    {
        public ColumnAttributeTypeMapper()
            : base(new SqlMapper.ITypeMap[]
                {
                    new CustomPropertyTypeMap(
                       typeof(T),
                       (type, columnName) =>
                           type.GetProperties().FirstOrDefault(prop =>
                               prop.GetCustomAttributes(false)
                                   .OfType<ColumnAttribute>()
                                   .Any(attr => attr.Name == columnName)
                               )
                       ),
                    new DefaultTypeMap(typeof(T))
                })
        {
        }
    }
    

    That means we can now easily support types that require map using attributes:

    Dapper.SqlMapper.SetTypeMap(
        typeof(MyModel),
        new ColumnAttributeTypeMapper<MyModel>());
    

    Here's a Gist to the full source code.

    单挑你×的.吻 2025-01-03 10:54:03

    这工作正常:

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

    Dapper 没有允许您指定 列属性,我不反对添加对它的支持,只要我们不引入依赖项。

    This works fine:

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

    Dapper has no facility that allows you to specify a Column Attribute, I am not against adding support for it, providing we do not pull in the dependency.

    分分钟 2025-01-03 10:54:03

    在一段时间内,以下内容应该有效:

    Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
    

    For some time, the following should work:

    Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
    
    扛刀软妹 2025-01-03 10:54:03

    我使用动态和 LINQ 执行以下操作:

        var sql = @"select top 1 person_id, first_name, last_name from Person";
        using (var conn = ConnectionFactory.GetConnection())
        {
            List<Person> person = conn.Query<dynamic>(sql)
                                      .Select(item => new Person()
                                      {
                                          PersonId = item.person_id,
                                          FirstName = item.first_name,
                                          LastName = item.last_name
                                      }
                                      .ToList();
    
            return person;
        }
    

    I do the following using dynamic and LINQ:

        var sql = @"select top 1 person_id, first_name, last_name from Person";
        using (var conn = ConnectionFactory.GetConnection())
        {
            List<Person> person = conn.Query<dynamic>(sql)
                                      .Select(item => new Person()
                                      {
                                          PersonId = item.person_id,
                                          FirstName = item.first_name,
                                          LastName = item.last_name
                                      }
                                      .ToList();
    
            return person;
        }
    
    诠释孤独 2025-01-03 10:54:03

    这是一个不需要属性的简单解决方案,允许您将基础设施代码保留在 POCO 之外。

    这是一个处理映射的类。如果映射所有列,字典就可以工作,但此类允许您仅指定差异。此外,它还包括反向映射,因此您可以从列获取字段,从字段获取列,这在执行生成 sql 语句等操作时非常有用。

    public class ColumnMap
    {
        private readonly Dictionary<string, string> forward = new Dictionary<string, string>();
        private readonly Dictionary<string, string> reverse = new Dictionary<string, string>();
    
        public void Add(string t1, string t2)
        {
            forward.Add(t1, t2);
            reverse.Add(t2, t1);
        }
    
        public string this[string index]
        {
            get
            {
                // Check for a custom column map.
                if (forward.ContainsKey(index))
                    return forward[index];
                if (reverse.ContainsKey(index))
                    return reverse[index];
    
                // If no custom mapping exists, return the value passed in.
                return index;
            }
        }
    }
    

    设置 ColumnMap 对象并告诉 Dapper 使用该映射。

    var columnMap = new ColumnMap();
    columnMap.Add("Field1", "Column1");
    columnMap.Add("Field2", "Column2");
    columnMap.Add("Field3", "Column3");
    
    SqlMapper.SetTypeMap(typeof (MyClass), new CustomPropertyTypeMap(typeof (MyClass), (type, columnName) => type.GetProperty(columnMap[columnName])));
    

    Here is a simple solution that doesn't require attributes allowing you to keep infrastructure code out of your POCOs.

    This is a class to deal with the mappings. A dictionary would work if you mapped all the columns, but this class allows you to specify just the differences. In addition, it includes reverse maps so you can get the field from the column and the column from the field, which can be useful when doing things such as generating sql statements.

    public class ColumnMap
    {
        private readonly Dictionary<string, string> forward = new Dictionary<string, string>();
        private readonly Dictionary<string, string> reverse = new Dictionary<string, string>();
    
        public void Add(string t1, string t2)
        {
            forward.Add(t1, t2);
            reverse.Add(t2, t1);
        }
    
        public string this[string index]
        {
            get
            {
                // Check for a custom column map.
                if (forward.ContainsKey(index))
                    return forward[index];
                if (reverse.ContainsKey(index))
                    return reverse[index];
    
                // If no custom mapping exists, return the value passed in.
                return index;
            }
        }
    }
    

    Setup the ColumnMap object and tell Dapper to use the mapping.

    var columnMap = new ColumnMap();
    columnMap.Add("Field1", "Column1");
    columnMap.Add("Field2", "Column2");
    columnMap.Add("Field3", "Column3");
    
    SqlMapper.SetTypeMap(typeof (MyClass), new CustomPropertyTypeMap(typeof (MyClass), (type, columnName) => type.GetProperty(columnMap[columnName])));
    
    缺⑴份安定 2025-01-03 10:54:03

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

    如果您的数据库列是 PERSON_ID 并且您的对象的属性是 ID,您只需

    select PERSON_ID as Id ...
    

    在查询中执行操作,Dapper 就会按预期选取它。

    An easy way to achieve this is to just use aliases on the columns in your query.

    If your database column is PERSON_ID and your object's property is ID, you can just do

    select PERSON_ID as Id ...
    

    in your query and Dapper will pick it up as expected.

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文