• 尝试手写orm框架


    前言:

        在使用各种的orm框架的过程中,菜鸟的我始终没有搞懂底层实现技术,最近刚好没事找了些视频和资料了解一点皮毛,想记录下,大家勿喷。

        所谓的ORM(Object Relational Mapping) 对象关系映射 官方解释是通过使用描述对象和数据库之间映射的元数据,将面向对象程序的对象自动持久化到关系数据库中。

        个人理解就是一个数据库访问的帮助类,可以让我们不用手写sql,就完成数据库的访问

        使用的技术: 泛型、反射、特性、扩展

    摸索步骤:

    step1

     新建项目,建几个类库,大家熟悉的三层。

    step2:

     在数据访问层新建一个sqlhelper 类;

     2.1 添加一个数据查询的方法,还需要添加model层添加SysUser,完成实体与表映射,实现代码 如下

     1  public class SysUser
     2 {
     3         public long Id { get; set; }
     4         public string Login_Name { get; set; }
     5         public string Name { get; set; }
     6         public string Icon { get; set; }
     7         public string Password { get; set; }
     8         public string Salt { get; set; }
     9         public string Tel { get; set; }
    10         public string Email { get; set; }
    11         public SByte Locked { get; set; }
    12         public DateTime Create_Date { get; set; }
    13         public long Create_By { get; set; }
    14         public DateTime Update_Date { get; set; }
    15         public long Update_By { get; set; }
    16         public string Remarks { get; set; }
    17         public SByte Del_Flag { get; set; }
    18  }
    View Code
     1  public SysUser QueryUser(string id)
     2   {
     3             Type type = typeof(SysUser);
     4             SysUser sysUser = new SysUser();
     5             using (var con = new MySqlConnection(strConnection))
     6             {
     7                 con.Open();
     8                 string strSql = $"select Id,Login_Name,nick_name,Icon,Password,Salt,Tel,Email,Locked,Create_Date,Create_By,Update_Date,Update_By,Remarks,Del_Flag from sys_user wherer id={id}";
     9                 MySqlCommand sqlCommand = new MySqlCommand(strSql, con);
    10 
    11                 MySqlDataReader mySqlDataReader = sqlCommand.ExecuteReader();
    12 
    13                 if(mySqlDataReader.Read())
    14                 {
    15                     if (mySqlDataReader.Read())
    16                     {
    17                         foreach (var item in type.GetProperties())
    18                         {
    19                             item.SetValue(sysUser, mySqlDataReader[item.Name] is DBNull ? null : mySqlDataReader[item.Name]);
    20                         } 
    21                     }
    22                    
    23                 }
    24 
    25             }
    26 
    27             return sysUser;
    28  }
    View Code

     2.2   上述代码只能对一个表进行查询,需要满足不同表查询可以使用泛型

     a. 反射完成动态sql的拼接

    Type type=typeof(T);
    string tableNam=type.Name;
    string colums=string.join(",",type.GetProperties().Select(p=>$"{p.Name}"));
    string strSql = $"select {colums} from {tableName}  where id={id}";

    b.ado.net 完成数据库查询

    c.反射完成数据动态绑定

    e.可空值类型处理

    实现代码

     1  public T QueryById<T>(string id)
     2   {
     3             Type type = typeof(T);
     4 
     5             T t = Activator.CreateInstance<T>();//创建实体
     6             string tableName = type.Name;
     7             string colums = string.Join(",", type.GetProperties().Select(p => $"{p.Name}"));//拼接查询字段
     8 
     9             using (var con = new MySqlConnection(strConnection))
    10             {
    11                 con.Open();
    12                 string strSql = $"select {colums} from {tableName}  where id={id}";
    13 
    14                 MySqlCommand sqlCommand = new MySqlCommand(strSql, con);
    15                 MySqlDataReader mySqlDataReader = sqlCommand.ExecuteReader();
    16 
    17                 if (mySqlDataReader.Read())
    18                 {
    19                     foreach (var item in type.GetProperties())
    20                     {
    21                         item.SetValue(t, mySqlDataReader[item.Name] is DBNull ? null : mySqlDataReader[item.Name]);//需要添加Null 判断
    22                     }
    23                     return t;
    24                 }
    25                 else
    26                 {
    27                     return default(T);
    28                 }
    29             }
    30 }
    View Code

     存在问题: 实体类名与数据库表名不一致、实体属性名与数据表字段名不一致 

     解决上面两个问题 可以使用特性(解释: 特性本质就是一个类,间接或直接继承Attribute 就是一个特性,其为目标元素提供关联的附加信息,并在运行时以反射的方式来获取附加信息)

     相关代码如下

     抽象类

     1  public abstract class AbstractMappingAttribute: Attribute
     2  {
     3         private string _mappingName;
     4 
     5         public AbstractMappingAttribute(string mappingName)
     6         {
     7             this._mappingName = mappingName;
     8         }
     9 
    10         public string GetMappingName()
    11         {
    12             return this._mappingName;
    13         }
    14   }
    View Code

    实体类名与表名不一致时 

    1  [AttributeUsage(AttributeTargets.Class)]
    2   public class MappingTableAttribute : AbstractMappingAttribute
    3  {
    4         public MappingTableAttribute(string tableName) : base(tableName)
    5         {
    6 
    7         }
    8  }
    View Code

    实体属性与表字段不一致时 

    1  [AttributeUsage(AttributeTargets.Property)]
    2  public class MappingColumAttribute : AbstractMappingAttribute
    3  {
    4         public MappingColumAttribute(string colName) : base(colName)
    5         {
    6 
    7         }
    8  }
    View Code

    注意: 在使用特性须加上本特性类作用的范围,简单理解就是用在类上面还是类的属性或行为上。

    实体类使用自定义特性代码如下

     1     [MappingTableAttribute("sys_user")]
     2     public class SysUser
     3     {
     4         public long Id { get; set; }
     5         public string Login_Name { get; set; }
     6         [MappingColumAttribute("nick_name")]
     7         public string Name { get; set; }
     8         public string Icon { get; set; }
     9         public string Password { get; set; }
    10         public string Salt { get; set; }
    11         public string Tel { get; set; }
    12         public string Email { get; set; }
    13         public SByte Locked { get; set; }
    14         public DateTime Create_Date { get; set; }
    15         public long Create_By { get; set; }
    16         public DateTime Update_Date { get; set; }
    17         public long Update_By { get; set; }
    18         public string Remarks { get; set; }
    19         public SByte Del_Flag { get; set; }
    20     }
    View Code

     怎么获取实体的自定义特性描述的信息? 

      这里面需要用到扩展方法

     1  public static class MappingAttributeExtend
     2 {
     3         public static string GetMappingName<T>(this T t) where T : MemberInfo
     4         {
     5             if (t.IsDefined(typeof(AbstractMappingAttribute), true))
     6             {
     7                 AbstractMappingAttribute abstractMappingAttribute = t.GetCustomAttribute<AbstractMappingAttribute>();
     8                 return abstractMappingAttribute.GetMappingName();
     9             }
    10             else
    11             {
    12                 return t.Name;
    13             }
    14         }
    15 }
    View Code

     2.3   经过2.2一步步优化最终通用查询方法代码如下

     1 public T QueryById<T>(string id)
     2  {
     3             Type type = typeof(T);
     4 
     5             T t = Activator.CreateInstance<T>();//创建实体
     6             string tableName = type.GetMappingName();
     7             string colums = string.Join(",", type.GetProperties().Select(p => $"{p.GetMappingName()}"));//拼接查询字段
     8 
     9             using (var con = new MySqlConnection(strConnection))
    10             {
    11                 con.Open();
    12                 string strSql = $"select {colums} from {tableName}  where id={id}";
    13 
    14                 MySqlCommand sqlCommand = new MySqlCommand(strSql, con);
    15                 MySqlDataReader mySqlDataReader = sqlCommand.ExecuteReader();
    16 
    17                 if (mySqlDataReader.Read())
    18                 {
    19                     foreach (var item in type.GetProperties())
    20                     {
    21                         item.SetValue(t, mySqlDataReader[item.GetMappingName()] is DBNull ? null : mySqlDataReader[item.GetMappingName()]);
    22                     }
    23                     return t;
    24                 }
    25                 else
    26                 {
    27                     return default(T);
    28                 }
    29             }
    30  }
    View Code

    step3:

    完成了简单查询,新增大同小异,实现代码如下

     1 public bool Insert<T>(T t)
     2  {
     3             Type type = typeof(T);
     4 
     5             string table = type.GetMappingName();
     6             string colum = string.Join(",", type.GetProperties().Select(p => $"{p.GetMappingName()}"));
     7             string value = string.Join(",", type.GetProperties().Select(p => $"'{p.GetValue(t)}'"));
     8 
     9             using (var con = new MySqlConnection(strConnection))
    10             {
    11                 con.Open();
    12                 string strSql = $"Insert into {table} ({colum}) values ({value}) ";
    13 
    14                 MySqlCommand mySqlCommand = new MySqlCommand(strSql, con);
    15 
    16                 return mySqlCommand.ExecuteNonQuery() == 1;
    17             }
    18 
    19  }
    View Code
  • 相关阅读:
    <script>元素
    女朋友问什么是动态规划,应该怎么回答?
    从输入URL到页面展示,这中间都发生了什么?
    TypeScript之父:JS不是竞争对手,曾在惧怕开源的微软文化中艰难求生
    Flash 终将谢幕:微软将于年底停止对 Flash 的支持
    尤雨溪:TypeScript不会取代JavaScript
    JVM参数设置、分析(转发)
    -XX:PermSize -XX:MaxPermSize 永久区参数设置
    堆的分配参数
    -Xmx 和 –Xms 设置最大堆和最小堆
  • 原文地址:https://www.cnblogs.com/wktang/p/12231886.html
Copyright © 2020-2023  润新知