zoukankan      html  css  js  c++  java
  • Dapper数据库字段和model属性映射

    背景:
    在.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中,方便的添加映射关系了!
    ————————————————
    版权声明:本文为CSDN博主「Zdelta」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Zdelta/java/article/details/87636491

  • 相关阅读:
    webpack基本使用
    vue-路由-显示名称
    vue-父组件和路由
    vue-路由
    vue-父子组件和ref
    vue-组件
    go-面向对象编程(上)
    JavaScript的历史
    vue-列表动画
    钩子函数实现小球弹落
  • 原文地址:https://www.cnblogs.com/deepalley/p/13307445.html
Copyright © 2011-2022 走看看