首页 > 其他分享 >使用泛型和反射实现一个简单的ORM框架

使用泛型和反射实现一个简单的ORM框架

时间:2022-10-29 16:47:47浏览次数:73  
标签:反射 return Name List ORM sql 泛型 property public

什么是ORM框架?

  ORM框架是连接数据库与实体类帮助程序对接数据库的框架,类似于三层架构的数据访问层,但ORM框架可以根据数据库生成实体类,或者根据实体类生成数据库,解决了数据库与实体类不匹配的问题,而且,使用ORM框架可以很大程度的提高开发效率,省去了开发人员用在编写数据访问层花费的时间。

泛型与反射概述

  关于泛型在之前的文章就有说过,还不了解的同学请点这里,这里重点说说反射,反射可以使程序读取自身并进行运行时修改,且可以调用读取到的程序内的方法,或者创建读取到的类。先说一下一个程序是如何从编译到运行的,C#代码首先会被编译器编译为DLL或者EXE程序然后经过CLR/JIT的编译成为计算机可以理解的二进制编码程序,而编译为DLL/EXE时会列出程序清单(元数据清单)matedata和中间语言IL,反射利用的就是这个DLL/EXE文件获得程序的数据。

  基本用法:

  获取DLL:

//(1)LoadFrom:dll全名称,需要后缀                        
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//(2)LoadFile:全路径,需要dll后缀
//Assembly assembly1 = Assembly.LoadFile(@"dll文件全路径");
//(3)Load:dll名称 不需要后缀
//Assembly assembly2 = Assembly.Load("Business.DB.SqlServer");
View Code

  获取类型:

//2、获取某一个具体的类型,参数需要是类的全名称
Type type1 = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");
View Code

  创建对象:

//3、创建对象
//(1)直接传类型
object? oInstance = Activator.CreateInstance(type1);
//(2)重载方法,传dll的全名称
//object? oInstanc1= Activator.CreateInstance("Business.DB.SqlServer.dll", "Business.DB.SqlServer.SqlServerHelper");
//a.oInstance.Query();//报错了:因为oInstance当做是一个object类型,object类型是没有Query方法;C#语言是一种强类型语言;编译时决定你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper;
//b.如果使用dynamic 作为类型的声明,在调用的时候,没有限制;
//c.dynamic :动态类型:不是编译时决定类型,避开编译器的检查;运行时决定是什么类型
//d.dynamic dInstance = Activator.CreateInstance(type);
//e.dInstance.Query();
//f.dInstance.Get(); //报错了--因为SqlServerHelper没有Get方法
View Code

  类型转化:

//4、类型转换
// SqlServerHelper helper = (SqlServerHelper)oInstance; //不建议这样转换--如果真实类型不一致--会报报错; 
IDBHelper helper = oInstance as IDBHelper;//如果类型一直,就转换,如果不一致;就返回null
View Code

  调用方法:

//5、调用方法
helper.Query();
View Code

不过本文并没有用到反射的一些功能,只是用到了反射相关的Type类。

接口实现

  撸代码之前我们先分析一下具体要实现什么效果,既然是操作数据库的类,自然要实现基本的增删改查功能,而ORM可以根据数据库生成实体类或者根据实体类生成数据库,仔细想想的话似乎根据实体类生成数据库跟容易些,那接口大致就是这样:

  (先声明一下,本人也是个C#小白,方法命名和封装都不是很规范,代码也只是提供个思路,很多功能实现的不完整,请大佬门嘴下留情,友好交流。)

interface IBaseHelper<T> where T : BaseId
    {
        List<T> Query();
        List<T> Query(int id);
        List<T> Query(Func<T,bool> func);
        bool Edit(T t);
        bool CreateRow(T t);
        bool CreateTable();
        bool Delete(int id);
    }
View Code

BaseId类:

public class BaseId
    {
        public BaseId(int id)
        {
            Id = id;
        }
        public BaseId(){ }

        public int Id { get; set; }

    }
View Code

MySqlHelper类实现

  因为本人用的MySql数据库,所以写的是MySql的Helper类,用其他数据库的可以尝试根据我的修改为其他数据库,核心实现都写好了注释,改写应该不会太难:

public class BaseMySqlHelper<T> : IBaseHelper<T> where T : BaseId, new()
    {
        /// <summary>
        /// 数据库查询方法
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <returns>查询结果</returns>
        public List<T> SqlQuery(string sql)
        {
            List<T> listT = new List<T>();
            MySqlConnectionStringBuilder sBuilder = new MySqlConnectionStringBuilder();
            sBuilder.Server = "localhost";
            sBuilder.UserID = "root";
            sBuilder.Password = "000124lrpLRP";
            sBuilder.Database = "lrp";

            using (MySqlConnection conn = new MySqlConnection(sBuilder.ConnectionString))
            {
                conn.Open();
                MySqlCommand mySqlCommand = new MySqlCommand(sql, conn);
                var result = mySqlCommand.ExecuteReader();
                while (result.Read())
                {
                    T t = new T();
                    foreach (PropertyInfo property in typeof(T).GetProperties())
                    {
                        //由于数据库的空值类型与C#的空值类型不同,所以需要先做个判断
                        property.SetValue(t, DBNull.Value.Equals(result[property.Name]) ? null : result[property.Name]);
                    }
                    listT.Add(t);
                }
            }
            return listT;
        }

        /// <summary>
        /// 将C#的类型转换为MySql的类型,用于建表
        /// </summary>
        /// <param name="propertyInfo">传入一个数据类型</param>
        /// <returns>转换后的Sql类型</returns>
        public string PropertySwap(PropertyInfo propertyInfo)
        {
            switch (propertyInfo.PropertyType.Name)
            {
                case "Int32":return "int";
                case "String":return "varchar(100)";
                case "Double":return "float";
                case "Decimal":return "decimal";
                case "DateTime":return "datetime";
                default:return "";
            }
        }

        /// <summary>
        /// 修改数据库方法
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <returns>true为执行成功,false为失败</returns>
        public bool SqlSet(string sql)
        {
            int result = 0;
            MySqlConnectionStringBuilder sBuilder = new MySqlConnectionStringBuilder();
            sBuilder.Server = "localhost";
            sBuilder.UserID = "root";
            sBuilder.Password = "000124lrpLRP";
            sBuilder.Database = "lrp";

            using (MySqlConnection conn = new MySqlConnection(sBuilder.ConnectionString))
            {
                conn.Open();
                MySqlCommand mySqlCommand = new MySqlCommand(sql, conn);
                result = mySqlCommand.ExecuteNonQuery();
            }
            return result!=-1;
        }

        public List<T> Query()
        {
            string sql = $"select * from {typeof(T).Name}";
            return SqlQuery(sql);
        }

        public List<T> Query(int id)
        {
            string sql = $"select * from {typeof(T).Name}";
            return SqlQuery(sql).Where<T>(t=>  t.Id == id ).ToList();
        }

        public List<T> Query(Func<T,bool> func)
        {
            string sql = $"select * from {typeof(T).Name}";
            return SqlQuery(sql).Where<T>(func).ToList();
        }

        public bool Edit(T t)
        {
            string sql = $"update {typeof(T).Name} set ";
            Type type = typeof(T);
            List<string> sqlData = new List<string>();
            foreach (PropertyInfo property in type.GetProperties())
            {
                if (property.PropertyType.Name == "String") sqlData.Add($@"{property.Name}='{property.GetValue(t)}'");
                else sqlData.Add($"{property.Name}={property.GetValue(t)}");
            }
            sql += string.Join(',', sqlData);
            sql += " where id=" + t.Id;
            return SqlSet(sql);
        }

        public bool CreateRow(T t)
        {
            string sql = $"insert into {typeof(T).Name} set ";
            Type type = typeof(T);
            List<string> sqlData = new List<string>();
            foreach (PropertyInfo property in type.GetProperties())
            {
                if(property.PropertyType.Name=="String")sqlData.Add($@"{property.Name}='{property.GetValue(t)}'");
                else sqlData.Add($"{property.Name}={property.GetValue(t)}");
            }
            sql+=string.Join(',', sqlData);
            return SqlSet(sql);
        }

        public bool Delete(int id)
        {
            string sql = $"delete from {typeof(T).Name} where id="+id;
            return SqlSet(sql);
        }

        public bool CreateTable()
        {
            string sql = $"CREATE TABLE IF NOT EXISTS {typeof(T).Name} ( "; 
            List<string> sqlData = new List<string>();
            foreach (PropertyInfo property in typeof(T).GetProperties())
            {
                sqlData.Add($"{property.Name} {PropertySwap(property)}");
            }
            sql += string.Join(',', sqlData);
            sql += ")";
            return SqlSet(sql);
        }
    }
View Code

  难点在于从泛型实例上读取属性值和修改泛型类型实例的属性值,使用了property.SetValve()和property.GetValue(),因为直接用实例.属性会无法通过编译,且无法实现读取不同类型的不同属性。

总结

  最后可以创建一个App.config存储数据库的连接语句,实现可配置。不过这次只是写了一个小Demo,真正的ORM框架需要用更多更加严谨的代码来完成,不过如果你还不太理解ORM看完我的代码对你理解ORM会有很大帮助。

标签:反射,return,Name,List,ORM,sql,泛型,property,public
From: https://www.cnblogs.com/lrplrplrp/p/16839008.html

相关文章

  • Terraform 语法 变量的使用
    变量分为两种类型,一种为输入变量,另外一种为输出变量。之前在写tf的模板文件的时候用了很多变量,比如在写认证信息的时候,定义的变量存放了阿里云的ak和sk,最后就是region的信息......
  • Terraform 语法 resource(1)
    resource是我们的资源,一般在terraform里面定义的都是资源,是个非常重要的角色。关键字resource+资源类型(比如dns,ecs,vpc,交换机)+资源的名称。模块下资源的名称要保证唯一,不要冲......
  • Terraform 语法 provider插件
     语法是terraform最重要的一点,有两个部分来讲解,第一部分是基础,第二部分是更加高级的扩展。后面各种实践其实就是对terraform语法编写。第一部分是provider插件,provider提供......
  • Terraform DataSource 数据源
    有些时候在创建资源的时候,会用到一些数据,比如在创建ecs的时候,我可能会用到一些镜像。这个我们可以去浏览云供应商提供的文档去查询,其实我们也可以通过provider来拿到。provi......
  • Terraform 基础 申请阿里云资源
    之前,资源都定义好了,现在就是去申请资源了。申请这些资源就需要使用terraform的命令行了,开始初始化后端,后端是有存储文件的,默认情况下是在本地存储的,然后会多一些文件。 (下......
  • 泛型方法
    packagecom.msb.test03;importjava.util.Collection;/***@author:liu*日期:15:18:34*1。描述:什么是泛型方法*不是带泛型的方法就是泛型方法*泛型......
  • c#Winform自定义控件-信号灯(工业)-HZHControls UCSignalLamp 使用
     想要官网这个效果,自己琢磨了下,记录一下   一、拖一个UCSignalLamp控件   二、设置指示灯的属性//设置指示灯颜色(不闪烁只用写一个......
  • L10U4-3 Presenting information
    VocabularyMorebusinesspresentationsDialogue[JOAN]Asyouknow,I'vebeenspendingalotoftimeatSunset'sheadquarters.AndI'vebeenveryimpressed.It's......
  • winform 弹框几秒自动消失
    winfrom弹框在右下角或者左下角显示看页面效果。 需要源码的找我。......
  • 2021-02-02 winform 定时器加异步 Task任务执行异步方法。
     定时器加Task任务,执行异步方法。privatevoidtimer1_test_Tick(objectsender,EventArgse){ss(DateTime.Now.Second);}Taskq......