zoukankan      html  css  js  c++  java
  • 也来写写基于单表的Orm(使用Dapper)

    前言

      这两天看园子里有个朋友写Dapper的拓展,想到自己之前也尝试用过,但不顺手,曾写过几个方法来完成自动的Insert操作。而对于Update、Delete、Select等,我一直对Dictionary<string,object>参数类型有很深的感情,旨在页面传递的条件基本上都是Json,很容易用字典类型存储,想过在遵循某种命名规则的前提下,自动完成查询,简单的业务,直接能得到相应条件的数据,为此研究过一段时间,如果需要详细的了解,请看这里:

      理想中的SQL条件拼接方式

      理想中的SQL条件拼接方式(二)

      看了下文章的时间,吓我一条,居然在2个月前,事实上,这个念头在心里也一直有很长时间了,却觉得自己很难去处理好它,封装的没问题,但用起来特别不顺手。如今新年来临,再来挑战自己。

      之前遇到的最大的问题就是处理值与类型的映射。由于页面传递的是Json数据源,后来序列化成Dictionary<string,object>后,很多类型就丢失了。如直接引起程序错误的DateTime类型了。原来的处理方式是各种手动的映射,更改数据类型,再提交ado.net操作。

      如今查看Poto这个Orm的源代码,它在实体增删改的时候将类型缓存起来,而且还有该类型的属性,在使用的时候,做过几次转换,而我像发现新大陆似得,觉得这完全可以解决我的问题呀。于是照搬了它的缓存部分代码,并结合Dapper实现了实体的增删改查,当然查询是通过Dictionary的传参。

    对Dapper和Poto的拓展与修改

      1.在Dapper的1219行代码处有方法 GetCacheInfo,在其中增加IDictionary参数支持。修改后的方法为:

            private static CacheInfo GetCacheInfo(Identity identity)
            {
                CacheInfo info;
                if (!TryGetQueryCache(identity, out info))
                {
                    info = new CacheInfo();
                    if (identity.parametersType != null)
                    {
                        if (typeof(IDynamicParameters).IsAssignableFrom(identity.parametersType))
                        {
                            info.ParamReader = (cmd, obj) => { (obj as IDynamicParameters).AddParameters(cmd, identity); };
                        }
    #if !CSHARP30
                        else if ( //支持传递Dictionary<K,V>为参数
                            typeof(IDictionary).IsAssignableFrom(identity.parametersType) ||
                            typeof(IEnumerable<KeyValuePair<string, object>>).IsAssignableFrom(identity.parametersType)
                            && typeof(System.Dynamic.IDynamicMetaObjectProvider).IsAssignableFrom(identity.parametersType)
                            )
                        {
                            info.ParamReader = (cmd, obj) =>
                            {
                                IDynamicParameters mapped = new DynamicParameters(obj);
                                mapped.AddParameters(cmd, identity);
                            };
                        }
    #endif
                        else
                        {
                            info.ParamReader = CreateParamInfoGenerator(identity, false);
                        }
                    }
                    SetQueryCache(identity, info);
                }
                return info;
            }
    GetCacheInfo

      2.在Poto中,增加了OrderAttribute,定义在实体上,指定自动生成的Select排序规则。

    再谈Orm的具体实现

      基于实体的增删改,参数是object,则可以根据对象获取到它的类型、属性,还有定义在类上的Attribute。很容易就可以构造出 insert、update、delete。另外由于Dapper支持一次SQL,在多个对象上执行,我对方法做了这样的改进,以充分利用Dapper。解释下,一次SQL,在多个对象上执行,如批量Insert,批量Update,批量Delete。SQL语句使用参数化,则是同一条,执行时参数来自于不同的对象而已。所以增删改查,参数为object时,还可以传递List、数组等。

      在Update和Delete方法中,我还希望通过条件来操作,运用到上面说的基于命名规则拼接SQL的方式,当然Select操作也不例外。主要实现代码:

            private readonly Regex reg = new Regex(@"^(?<tab>w+.)?(?<Pre>(PK|Begin|End|Like|UnLike|Null|In))?(?<Key>w+)$");
    
            private string CreateWhere(PocoData t, IDictionary<string, object> condition)
            {
                var sql = new StringBuilder();
    
                //遍历条件
                foreach (var key in condition.Keys.ToArray())
                {
                    var match = reg.Match(key); //正则获取到属性对应的字段名
                    var pre = match.Groups["Pre"].Value;//获取命名规则中的比较谓词
                    var colName = match.Groups["Key"].Value;//获取字段名称
                    //if (string.IsNullOrEmpty(tableName))
                    //    tableName = match.Groups["tab"].Value;//获取字段表名限定
    
                    var paraName = string.Format("{0}{1}", pre, colName);//获取该条件被分配的过程参数名
    
                    var value = condition[key];
    
                    //拼接Sql语句
                    sql.AppendFormat(" {0} AND",
                        Builder(pre, colName, paraName, t.TableInfo.TableName, value));//拼接SQL条件
    
                    if (pre == "In") //In拼接没有使用过程参数,直接拼接了,将参数从字典中移除
                    {
                        condition.Remove(key);
                        continue;
                    }
    
                    //若该字段值的类型为字符串,检测实体中的类型
                    if (value.GetType() == typeof(string))
                    {
                        PocoColumn prop;
                        if (!t.Columns.TryGetValue(colName, out prop))
                            throw new Exception(string.Format("can't find any property in object.propertyName:{0},object:{1}", key, t.TableInfo.TableName));
    
                        if (prop.PropertyInfo.PropertyType != typeof(string))
                            condition[key] = prop.ChangeType(value);
                    }
                }
    
                if (sql.Length > 3) sql.Length -= 3;
                return sql.ToString();
            }
    正则拆分SQL

     说到这里,有个小插曲,我发现当我在SQL和Oracle下测试成功后,我传递的Dictionary,并没有指定参数符号,如@或者: 。最后执行的DbCommand对象使用的参数名当然也没有,可居然没有出现任何问题。不知道该怎么解释。这里Sql的库使用的System.Data.SqlClient, net4.0 。而Oracle是使用的官方的,Oracle.ManagedDataAccess.dll ,net4.0。如果有朋友知道,还请告知一二。虽然我想偷懒,但这种突如其来的幸福,还是警惕下。

      最后在Update方法上增加参数,指定更新的列。

    其他

      解释下什么是基于单表的Orm。单表操作,增删改查,所有的方法都是在同一个表或者实体上。由于目前我还没有看Dapper或者Poco在多表下是怎么操作的,而暂时我又需要紧急的使用在项目中,因为我不想在写增删改查,简单业务下linq式的查询我也不想写。好吧,我承认我有点鲁莽,不稳定的东西就投入使用,可实践就是检验真理的唯一标准啊。说到实践,关于Poto这个Orm,我看其他人的介绍,经过各种测试,当我用了,我只想说,这测试不仔细,Oracle下各种错误,连自动分页都是错的。

      写到这里,发现具体的实现方式,已然不值一提。在写之前,各种困难,写之后,想要介绍它的时候,发现太简单,没什么可说的。代码中有一点注释,想看的朋友如果看不明白可以咨询。

      先贴下封装后的调用方式。

                var db = new DbHelp(Program.facotry, Program.connStr);
    
                ///////////////////////////////////////////////////////////////////////
                var tabAdd = new tab1()
                {
                    Name = "ggggg",
                    Home = "jjjjjj"
                };
                var row = db.Add(tabAdd);
                //批量增
                var tabAddArrary = new tab1[] { 
                new tab1()
                {
                    Name = "批量增1",
                    Home = "批量增1"
                },
                 new tab1()
                {
                    Name = "批量增2",
                    Home = "批量增2"
                }};
                row = db.Add(tabAddArrary);
    
                ////////////////////////////////////////////////////////////////////////////
                var tabUpdate = new tab1()
                {
                    Name = "fffffff",
                    Home = "fffffff",
                    ID = 5
                };
                row = db.Update(tabUpdate);
                //批量改
                var tabUpdateArrary = new tab1[] { 
                new tab1()
                {
                    Name = "批量改1",
                    Home = "批量改1",
                    ID=10
                },
                 new tab1()
                {
                    Name = "批量改2",
                    Home = "批量改2",
                    ID=11
                }};
                row = db.Update(tabUpdateArrary);
                //条件修改
                //1 定义要修改的内容
                var dic = new Dictionary<string, object>();
                dic["Home"] = DateTime.Now.ToString() + ":修改了";
                //定义条件 这里使用了命名规则
                var condtion = new Dictionary<string, object>();
                condtion["InID"] = "1,3,5";
                row = db.Update<tab1>(dic, condtion);
    
                //删除///////////////////////////////////////////////////
                var tabDelete = new tab1() { ID = 10 };
                db.Remove(tabDelete);
                //条件删除
                var dicDelete = new Dictionary<string, object>();
                dicDelete["BeginID"] = 50;
                row = db.Remove<tab1>(dicDelete);
    
                //查询/////////////////////////////////////////////////////
                var dicQuery = new Dictionary<string, object>();
                dicQuery["EndID"] = 50;
                var qtab1 = db.Query<tab1>(dicQuery);
    
                //分页///////////////////////////////////////////////////////////
                int count;
                var dicPage = new Dictionary<string, object>();
                dicPage["BeginID"] = 50;
                dicPage["EndID"] = 10;
                var qtabPage = db.Query<tab1>(dicQuery, 3, 5, out count);
    封装后的调用

      测试代码下载

       最后祝大家马上有钱。

  • 相关阅读:
    python---自定义分页类
    python---正则中的(?P<name>group)
    学习windows编程 day6 之模拟记事本
    学习windows编程 day5 之按键消息
    some websit
    android/iphone/windows/linux声波通讯库
    无线点餐系统
    android实现弧形进度表盘效果
    与Sevice实现双向通信
    android code bbs for developer
  • 原文地址:https://www.cnblogs.com/codealone/p/3521563.html
Copyright © 2011-2022 走看看