尝试手写orm框架

前言:

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

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

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

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

摸索步骤:

step1

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

step2:

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

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

public class SysUser
{
        public long Id { get; set; }
        public string Login_Name { get; set; }
        public string Name { get; set; }
        public string Icon { get; set; }
        public string Password { get; set; }
        public string Salt { get; set; }
        public string Tel { get; set; }
        public string Email { get; set; }
        public SByte Locked { get; set; }
        public DateTime Create_Date { get; set; }
        public long Create_By { get; set; }
        public DateTime Update_Date { get; set; }
        public long Update_By { get; set; }
        public string Remarks { get; set; }
        public SByte Del_Flag { get; set; }
 }
public SysUser QueryUser(string id)
  {
            Type type = typeof(SysUser);
            SysUser sysUser = new SysUser();
            using (var con = new MySqlConnection(strConnection))
            {
                con.Open();
                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}";
                MySqlCommand sqlCommand = new MySqlCommand(strSql, con);

                MySqlDataReader mySqlDataReader = sqlCommand.ExecuteReader();

                if(mySqlDataReader.Read())
                {
                    if (mySqlDataReader.Read())
                    {
                        foreach (var item in type.GetProperties())
                        {
                            item.SetValue(sysUser, mySqlDataReader[item.Name] is DBNull ? null : mySqlDataReader[item.Name]);
                        } 
                    }
                   
                }

            }

            return sysUser;
 }

 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.可空值类型处理

实现代码

public T QueryById<T>(string id)
  {
            Type type = typeof(T);

            T t = Activator.CreateInstance<T>();//创建实体
            string tableName = type.Name;
            string colums = string.Join(",", type.GetProperties().Select(p => $"{p.Name}"));//拼接查询字段

            using (var con = new MySqlConnection(strConnection))
            {
                con.Open();
                string strSql = $"select {colums} from {tableName}  where id={id}";

                MySqlCommand sqlCommand = new MySqlCommand(strSql, con);
                MySqlDataReader mySqlDataReader = sqlCommand.ExecuteReader();

                if (mySqlDataReader.Read())
                {
                    foreach (var item in type.GetProperties())
                    {
                        item.SetValue(t, mySqlDataReader[item.Name] is DBNull ? null : mySqlDataReader[item.Name]);//需要添加Null 判断
                    }
                    return t;
                }
                else
                {
                    return default(T);
                }
            }
}

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

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

 相关代码如下

 抽象类

public abstract class AbstractMappingAttribute: Attribute
 {
        private string _mappingName;

        public AbstractMappingAttribute(string mappingName)
        {
            this._mappingName = mappingName;
        }

        public string GetMappingName()
        {
            return this._mappingName;
        }
  }

实体类名与表名不一致时 

[AttributeUsage(AttributeTargets.Class)]
  public class MappingTableAttribute : AbstractMappingAttribute
 {
        public MappingTableAttribute(string tableName) : base(tableName)
        {

        }
 }

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

[AttributeUsage(AttributeTargets.Property)]
 public class MappingColumAttribute : AbstractMappingAttribute
 {
        public MappingColumAttribute(string colName) : base(colName)
        {

        }
 }

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

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

[MappingTableAttribute("sys_user")]
    public class SysUser
    {
        public long Id { get; set; }
        public string Login_Name { get; set; }
        [MappingColumAttribute("nick_name")]
        public string Name { get; set; }
        public string Icon { get; set; }
        public string Password { get; set; }
        public string Salt { get; set; }
        public string Tel { get; set; }
        public string Email { get; set; }
        public SByte Locked { get; set; }
        public DateTime Create_Date { get; set; }
        public long Create_By { get; set; }
        public DateTime Update_Date { get; set; }
        public long Update_By { get; set; }
        public string Remarks { get; set; }
        public SByte Del_Flag { get; set; }
    }

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

  这里面需要用到扩展方法

public static class MappingAttributeExtend
{
        public static string GetMappingName<T>(this T t) where T : MemberInfo
        {
            if (t.IsDefined(typeof(AbstractMappingAttribute), true))
            {
                AbstractMappingAttribute abstractMappingAttribute = t.GetCustomAttribute<AbstractMappingAttribute>();
                return abstractMappingAttribute.GetMappingName();
            }
            else
            {
                return t.Name;
            }
        }
}

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

public T QueryById<T>(string id)
 {
            Type type = typeof(T);

            T t = Activator.CreateInstance<T>();//创建实体
            string tableName = type.GetMappingName();
            string colums = string.Join(",", type.GetProperties().Select(p => $"{p.GetMappingName()}"));//拼接查询字段

            using (var con = new MySqlConnection(strConnection))
            {
                con.Open();
                string strSql = $"select {colums} from {tableName}  where id={id}";

                MySqlCommand sqlCommand = new MySqlCommand(strSql, con);
                MySqlDataReader mySqlDataReader = sqlCommand.ExecuteReader();

                if (mySqlDataReader.Read())
                {
                    foreach (var item in type.GetProperties())
                    {
                        item.SetValue(t, mySqlDataReader[item.GetMappingName()] is DBNull ? null : mySqlDataReader[item.GetMappingName()]);
                    }
                    return t;
                }
                else
                {
                    return default(T);
                }
            }
 }

step3:

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

public bool Insert<T>(T t)
 {
            Type type = typeof(T);

            string table = type.GetMappingName();
            string colum = string.Join(",", type.GetProperties().Select(p => $"{p.GetMappingName()}"));
            string value = string.Join(",", type.GetProperties().Select(p => $"‘{p.GetValue(t)}‘"));

            using (var con = new MySqlConnection(strConnection))
            {
                con.Open();
                string strSql = $"Insert into {table} ({colum}) values ({value}) ";

                MySqlCommand mySqlCommand = new MySqlCommand(strSql, con);

                return mySqlCommand.ExecuteNonQuery() == 1;
            }

 }