Dapper数据库字段和model属性映射

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Zdelta/article/details/87636491

背景:

在.net core 2.0 Asp.mvc 项目中使用dapper 框架

一、

数据库字段名和model属性名一一对应时,直接只用dapper方法是没有问题的,比如:

//实体类
public class Books
    {
        [Dapper.Key]
        public int Id { get; set; }

        public string Name { get; set; }

        public string Author { get; set; }
}

数据库:book表

直接使用dapper对象的方法,传入model对象:

//默认映射
string sql = $"SELECT * FROM book ";
database.QueryListSQL<ReadBooks>(sql).ToList();

//直接传入实体对象
bool result = await database.CreateAsync(book) > 0;

好处是不需要在sql语句中将字段一个个写出来,直接使用table-model映射关系,非常便利!

二、

但是,也会有字段名和属性名不对应的情况,比如:

public class ReadBooks
{      

    public DateTime CreateTime { get; set; }
}

那么,在C#程序中,使用Dapper做查询时,如何配置数据表字段(列)和实体类属性之间的映射呢?

method 1:

查询时,可以在select语句中使用AS别名,别名与model对应:

var sql = @"select create_time AS CreateTime from book";

database.Single<Books>(sql);

插入/更新时,依然需要 列名-值 :

//dapper插入语句,表名和字段名
database.InsertSQL($@"book",new DataColumn("Author", book.Author),
                            new DataColumn("Name", book.Name),
                            new DataColumn("create_time", DateTime.Now)
                  );

 

method 2:

查询时,还可以使用Linq:

//搭配动态类型dynamic使用
 
List<Books> book= database.Query<dynamic>(sql)
                                  .Select(item => new Books()
                                  {
                                      CreatTime= creat_time
                                  }
                                  .ToList();

method 3:

如果有很多数据库表字段和model属性不对应的话,我们每次都使用别名和linq就有些麻烦,如果能像EF那样,在model属性上加一个Attribute来表明字段和属性映射关系就好了,EF:

class School
{
    /*
      若属性名和数据库字段不一致(不区分大小写)则查询不出数据,如果使用EF则可以通过Column特性
      建立属性和数据表字段之间的映射关系,Dapper则不行
    */
    //[Column("Name")]
    public string Title { set; get; }
    public string Address { set; get; }
}

Dapper虽然有colmun的特性,但是并不能完成字段-属性映射;

我们需要手动拓展一个colmun特性出来,以完成如EF的绑定。

1,添加一个类ColumnAttributeTypeMapper,用于字段-属性映射:

using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace 命名空间
{
   
        /// <summary>
        /// Uses the Name value of the <see cref="ColumnAttribute"/> specified to determine
        /// the association between the name of the column in the query results and the member to
        /// which it will be extracted. If no column mapping is present all members are mapped as
        /// usual.
        /// </summary>
        /// <typeparam name="T">The type of the object that this association between the mapper applies to.</typeparam>
        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))
                    })
            {
            }
        }

        [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
        public class ColumnAttribute : Attribute
        {
            public string Name { get; set; }
        }

        public class FallbackTypeMapper : SqlMapper.ITypeMap
        {
            private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;

            public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
            {
                _mappers = mappers;
            }


            public ConstructorInfo FindConstructor(string[] names, Type[] types)
            {
                foreach (var mapper in _mappers)
                {
                    try
                    {
                        ConstructorInfo result = mapper.FindConstructor(names, types);
                        if (result != null)
                        {
                            return result;
                        }
                    }
                    catch (NotImplementedException)
                    {
                    }
                }
                return null;
            }

            public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
            {
                foreach (var mapper in _mappers)
                {
                    try
                    {
                        var result = mapper.GetConstructorParameter(constructor, columnName);
                        if (result != null)
                        {
                            return result;
                        }
                    }
                    catch (NotImplementedException)
                    {
                    }
                }
                return null;
            }

            public SqlMapper.IMemberMap GetMember(string columnName)
            {
                foreach (var mapper in _mappers)
                {
                    try
                    {
                        var result = mapper.GetMember(columnName);
                        if (result != null)
                        {
                            return result;
                        }
                    }
                    catch (NotImplementedException)
                    {
                    }
                }
                return null;
            }


            public ConstructorInfo FindExplicitConstructor()
            {
                return _mappers
                    .Select(mapper => mapper.FindExplicitConstructor())
                    .FirstOrDefault(result => result != null);
            }
        }

    
}

2,再添加一个类ColumnMapper,用于添加映射关系:

using Dapper;
using 引入需要的.Models;

namespace 项目命名空间
{
    public class ColumnMapper
    {
        public static void SetMapper()
        {
            //数据库字段名和c#属性名不一致,手动添加映射关系
            SqlMapper.SetTypeMap(typeof(Books), new  ColumnAttributeTypeMapper<Books>());
            
            //每个需要用到[colmun(Name="")]特性的model,都要在这里添加映射
        }
    }
}

3,在starup.cs类的中方法注册:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddSession();
            
            //调用前面的静态方法,将映射关系注册
            ColumnMapper.SetMapper();
}

4,最后就可以在model的属性名上添加特性来映射到数据库字段名了:

using 引入新加的类.Helper;

public class Books
{
        [Column(Name = "create_time")]
        public DateTime CreateTime { get; set; }
}

这样我们就可以在所有的不与数据库对应的model中,方便的添加映射关系了!

猜你喜欢

转载自blog.csdn.net/Zdelta/article/details/87636491
今日推荐