使用ORM、反射、泛型书写通用的增删改查方法

想做这个事情的原因:

公司因为升级系统的原因将WCF作为整个系统的中间部分,负责连接数据库传递数据的工作。而这个任务落到了我的头上。

在使用ORM(CHLOE)的过程中,发现一个问题,就是每一个表如果要实现增删改查的话都需要写一个相应的方法。这是一个重复到极点的工作,是我不能忍受的事情。于是想通过ORM实现通用型的增删改查工作,减轻负担。只需要增加表类,不需要增加SQL语句。

这个项目中我用的是chole

先订立一个目标:
增删改查都只需要写一个方法,增加ORM表类的同时立刻能够使用这几个方法完成增删改查的操作!

为了完成这个目标需要解决的问题:

1、如何在不知道类对象是什么的情况下获得属性的值;

2、如何做到传递一个类,让方法知道它的条件和更新字段;

3、如果做到更新时,只更新可以更新的属性

解决问题1:
使用反射获得对象类的属性和值,typeof().getpropertys()

解决问题2:
在类中增加两个List字段,一个存储Where属性,一个存储Update属性,类型为List

解决问题3:
增加一个在程序运行的过程中不变的字符串数组,存储可更新字段

解决的代码如下:

    /// 
    /// 所有表的基类,主要用来区分条件和更新的属性
    /// 
    public class TableBase
    {
        [JsonProperty]//让jsonconvert在执行转换的时候,转换这两个内部变量
        private List<string> ListWhere { get { return _listW; } set { _listW = value; } }//保存条件的属性名称
        private List<string> _listW = new List<string>();

        [JsonProperty]
        private List<string> ListUpdate { get { return _listU; } set { _listU = value; } }//保存更改的属性名称
        private List<string> _listU = new List<string>();

        private int _isWhereOrUpdate = 0;//是否条件,为0既不是更新也不是条件

        /// 
        /// 设置接下来赋值的属性不写入_listWhere和_listUpdate
        /// 
        public void SetNothing()
        {
            _isWhereOrUpdate = 0;
        }
        /// 
        /// 设置接下来赋值的属性是条件
        /// 
        public void SetWhere()
        {
            _isWhereOrUpdate = 1;
        }
        /// 
        /// 设置接下来赋值的属性是更新值
        /// 
        public void SetUpdate()
        {
            _isWhereOrUpdate = 2;
        }
        /// 
        /// 返回条件属性队列
        /// 
        /// 
        public List<string> GetListWhere()
        {
            return ListWhere;
        }
        /// 
        /// 返回更新属性队列
        /// 
        /// 
        public List<string> GetListUpdate()
        {
            return ListUpdate;
        }

        /// 
        /// 将修改的数据增加到List中,注意此方法会将原来保存在LIST中的数据删除
        /// 
        /// 属性名称
        /// 更新的列名
        public void AddP(string value, string[] update)
        {

            for (int i = 0; i < ListUpdate.Count; i++)//时间复杂度O(2n-index)
            {
                if (ListUpdate[i] == value)
                {
                    ListUpdate.RemoveAt(i);
                }
            }
            for (int i = 0; i < ListWhere.Count; i++)//时间复杂度O(2n-index)
            {
                if (ListWhere[i] == value)
                {
                    ListWhere.RemoveAt(i);
                }
            }

            //添加数据到List中
            if (_isWhereOrUpdate == 1)
            {
                ListWhere.Add(value);
            }
            else if (_isWhereOrUpdate == 2 && update != null && update.Contains(value))  //如果是能更新的字段
            {
                ListUpdate.Add(value);
            }
        }
        /// 
        /// 清空更新和条件队列
        /// 
        public void Clear()
        {
            ListWhere.Clear();
            ListUpdate.Clear();
        }

        /// 
        /// 获得set方法所在的属性名称
        /// 若使用release版本,frame.GetMethod()获取的是执行的方法,不会获取类中的方法。
        /// release中会获取如TestSelect*()的方法,Debug获取SetProperity方法
        /// 
        /// 返回属性名称
        public string GetPN()
        {
            StackTrace trace = new StackTrace(true);
            StackFrame frame = trace.GetFrame(1);//1代表上级,2代表上上级,以此类推
            MethodBase method = frame.GetMethod();            //获得当前方法名称

            //Console.WriteLine(memberName);

            try
            {
                var property = (from f in method.DeclaringType.GetProperties()
                                where f.GetSetMethod() == method || f.GetGetMethod() == method
                                select f).FirstOrDefault();

                return property.Name;
            }
            catch
            {
                return null;
            }
        }
    }

上面的代码主要解决了获取属性名称、保存更新List和条件List的问题。能够更新的字段放在每一个Table表当中。

数据库表类结构如下图所示:

    public class T_TRACK : TableBase
    {
        private static readonly string[] UPDATE = {"SFZMHM" };//能够更新的字段
        public string SFZMHM { get { return _sfzmhm; } set { _sfzmhm = value; AddP(GetPN(), UPDATE); } }
        private string _sfzmhm;
        public string TESTDATE { get { return _testdate; } set { _testdate = value; AddP(GetPN(), UPDATE); } }
        private string _testdate;
        public int? KSCS { get { return _kscs; } set { _kscs = value; AddP(GetPN(), UPDATE); } }
        private int? _kscs;
        public string DATA { get { return _data; } set { _data = value; AddP(GetPN(), UPDATE); } }
        private string _data;
    }

上面的代码展示的是一个表的结构,其中UPDATE字段存储了此表可以由客户端更新的字段名称,如果为空的话表示客户端不能更新任何字段。

既然获得属性和值得问题解决了,那么现在就可以利用ORM和上面的TABLE内容写通用的更新代码了。如下:


        /// 
        /// 获得当前ListWhere或者ListUpdate的对象的属性
        /// 
        /// 
        /// 
        /// 返回PropertyInfo的列表
        private static List<PropertyInfo> GetPropertys<T>(T value, bool isWhere = true) where T : TableBase
        {
            //直接利用基类得到listStr
            List<string> ListPropertyStr = null;
            if (isWhere)
                ListPropertyStr = value.GetListWhere();
            else
                ListPropertyStr = value.GetListUpdate();

            //如果不能获得List的话,返回空
            if (ListPropertyStr == null || ListPropertyStr.Count == 0)
            {
                return null;
            }

            //使用属性方法获得变量值,原来的方法是先获得所有属性,接着判断属性是否在listStr中
            List<PropertyInfo> listProperty = new List<PropertyInfo>();//属性
            for (int i = 0; i < ListPropertyStr.Count; i++)
            {
                listProperty.Add(typeof(T).GetProperty(ListPropertyStr[i]));
            }
            return listProperty;
        }
        
    
        /// 
        /// 通用型数据更新方
        /// 
        /// 表类
        /// 表类实体
        /// 
        public static int UpdateData<T>(T value) where T : TableBase
        {

            List<PropertyInfo> listWhereP = GetPropertys(value);
            List<PropertyInfo> listUpdateP = GetPropertys(value, false);

            //如果没有相应的属性的话,返回空
            if (listWhereP == null || listUpdateP == null || listWhereP.Count == 0 || listUpdateP.Count == 0)
            {
                return 0;
            }

            int updateRes = 0;//返回结果

            //更新语句
            try
            {
                OracleContext context = new OracleContext(new OracleConnectionFactory(GetConnectionStr()));
                //查询SQL语句与变量
                DbParam[] paras = new DbParam[listWhereP.Count + listUpdateP.Count];//SQL变量保存的地方

                //update
                string sql = "update " + typeof(T).Name + " set ";
                for (int i = 0; i < listUpdateP.Count - 1; i++)
                {
                    sql += listUpdateP[i].Name + " = " + " :U" + i + ", ";
                    paras[i] = DbParam.Create(":U" + i, listUpdateP[i].GetValue(value, null));
                }
                sql += listUpdateP[listUpdateP.Count - 1].Name + " = " + " :U" + (listUpdateP.Count - 1) + " where ";
                paras[listUpdateP.Count - 1] = DbParam.Create(":U" + (listUpdateP.Count - 1), listUpdateP[listUpdateP.Count - 1].GetValue(value, null));

                //where
                for (int i = 0; i < listWhereP.Count - 1; i++)
                {
                    sql += listWhereP[i].Name + " = " + " :W" + i + " AND ";
                    paras[i + listUpdateP.Count] = DbParam.Create(":W" + i, listWhereP[i].GetValue(value, null));
                }
                sql += listWhereP[listWhereP.Count - 1].Name + " = " + " :W" + (listWhereP.Count - 1);
                paras[listWhereP.Count - 1 + listUpdateP.Count] = DbParam.Create(":W" + (listWhereP.Count - 1), listWhereP[listWhereP.Count - 1].GetValue(value, null));

                //查询SQL语句的执行之处
                updateRes = context.Session.ExecuteNonQuery(sql, paras);
                context.Dispose();
                return updateRes;
            }
            catch (Exception ex)
            {
                LogHelper.WriteLogWcf(ex.Message, "UpdateData()");
                throw new Exception(ex.Message);
            }
        }
        

比较遗憾的地方是通用型的方法依旧使用了SQL语句,并没有利用到ORM的易用性。但目前我还没有想到更符合更简洁的方案。

另外一点说明是关于TableBase类中SetWhere(),SetUpdate(),SetNothing()三个方法。

主要含义和命名方式一个意思,在使用了SetWhere()后,后面赋值的属性自动定义为条件,使用SetUpdate()后,后面赋值的属性定义为更新字段,SetNothing后,后面赋值的属性不做定义,主要用于写入数据。

调用的代码如下:

T_TRACK track=new T_TRACK();
track.SetWhere();
track.KSCS=2;
track.SetUpdate();
track.SFZMHM="1202222211111111233";
UpdateData(track);

这就是通用性更新、查询、删除、插入的思路和方案。

你可能感兴趣的:(c#,orm,泛型)