初学C#和MVC的一些心得,弯路,总结,还有教训(3)--Dapper

经过一番深思熟虑,决定先用Dapper吧.....

以下是我感觉比较有用的一些东西

Dapper项目地址  https://github.com/StackExchange/dapper-dot-net

初次接触Dapper,简单的范例  https://github.com/xliang/dapper-net-sample

园子里树上的蜗牛 大大写的扩展系列 http://www.cnblogs.com/cyb331/p/3514555.html   文中的下载连接都失效了,这是扩展1.1的下载连接 http://pan.baidu.com/s/1bngXOAz

如果以前写过sql语句,Dapper用起来算是比较顺手的,基本上还是以前那套 数据库连接,sql语句,记录集 的流程,只不过返回结果的时候就不用去记录集一个字段一个字段的读了了,结果可以直接绑定到模型上....

Dapper项目自带了Contrib 和 Rainbow两个插件,我还没深入去了解,不知道还有没有什么高级功能,暂且当成语法糖吧,随着学习的深入,可能会有更深的理解

从Dapper.net sample 可以看到直接用Dapper和两个插件完成增删改查的区别,如果你也是新手,没接触过,建议把这个范例下回去看看,基本大概就明白了

我这里就简单的把插入数据的代码拿出来,简单做个比较

Dapper直接插入一条数据是这样的

                var supplier = new Supplier()
                {
                    Address = "10 Main Street",
                    CompanyName = "DEF Corporation"
                };

                sqlConnection.Execute(
                                    @"
                                       insert Suppliers(CompanyName, Address)
                                       values (@CompanyName, @Address)
                                    ",
                    supplier); 

Contrib插件是这样的

                var supplier = new Supplier()
                                   {
                                       Address = "10 Main Street",
                                       CompanyName = "ABC Corporation"
                                   };

                var supplierId = sqlConnection.Insert<Supplier>(supplier); 

Rainbow是这样的

                int? supplierId = db.Suppliers.Insert(new
                                                          {
                                                              CompanyName = Guid.NewGuid().ToString()

                                                          });

感觉上Rainbow在用的时候跟EF的流程差不多,定义Database的时候要把表名和实体(模型)都定义好...感觉这样用就不如直接去用EF了....所以,我的代码里面大都用了Contrib来做

    public class NorthwindDatabase : Database<NorthwindDatabase>
    {
        public Table<Supplier> Suppliers { get; set;  }
    }

另外还要介绍就是 树上的蜗牛 写的扩展,看了一下介绍和扩展的内容,感觉以后有些地方可能会用的上,于是也都导入到解决方案里面了...

这里要说明一下的是,蜗牛大大写的DapperEx是从DbBase扩展的,而Contrib是从DbConnecttion扩展的....

为了优化性能,实现一个请求使用一个数据库连接,所以我写了一个DBFactory,用CallContext来存储数据库连接实例,有关CallContext的说明,请msdn或者google,做为初学者,暂时只知道这样做可以就行了...

代码如下

    public static class DBFactory
    {

        /// <summary>
        /// 获取主数据库连接
        /// </summary>
        /// <returns></returns>
        public static DbBase GetSDDB()
        {
            DbBase db = CallContext.GetData("SDDB") as DbBase;
            if (db == null)
            {
                db = new DbBase("SdConnection");
                CallContext.SetData("SDDB", db);
            }
            return db;
        }

    }

需要数据库的时候这样写就可以了

    public class ServiceBase
    {
       protected DbBase DB;
       public ServiceBase()
        {
            //取数据库对象
            DB = DBFactory.GetSDDB();
        }
    }

具体操作的时候这样就OK了

        public IEnumerable<StaffUser> GetUserAll(int page = 0, int rows = 0)
        {
            IEnumerable<StaffUser> m = DB.DbConnecttion.Query<StaffUser>(@"
            Select *
            From StaffUser
            order by IsEnable Desc,ID" + SDUtility.GetPageSql(page, rows));
            return m;
        }

Contrib插件有一个地方很别扭,就是在增删改查的时候,会把Model后面加个s当作表名,这个行为也是学EF,不过人家EF更智能的一点是,会自动把英文单词转换为正确的复数形式,而Contrib就是简单粗暴的在后面加了个s,让我感觉很是不爽,于是乎就粗暴的打开源代码,找到 GetTableName 函数,改之......这样我model什么名字跟数据库的表名相同就OK了....

当时打算用蜗牛大大写的DapperEx,还有一个原因就是原生的dapper不提供翻页支持....这个也让人很是恼火,以前用mssql的时候最恼火的也是这个....不如用mysql,直接limit xx,yy 就行了....

不过后来偶然查资料得知,mssql2012的ORDER BY 语句支持用 offset_fetch 来实现翻页(控制返回的行数) ,微软的msdn里有代码示例 http://msdn.microsoft.com/zh-cn/library/ms188385(v=sql.110).aspx#Offset

所以 SDUtility.GetPageSql 里的代码就简单的许多,直接拼接出 offset_fetch 语句即可

        public static string GetPageSql(int page = 0, int rows = 0)
        {
            page = page - 1;
            if (page < 0) page = 0;
            if (rows < 0) rows = 0;
            if (page==0 && rows==0)
            return "";

            return string.Format(" OFFSET {0} ROWS FETCH NEXT {1} ROWS ONLY",page*rows,rows);
        }

有了dapper,Contrib还有offset_fetch,再加上高大上sql语句,哦,对了,还有功不可没的Json.net,平时大部分的需求都可以满足了.....

复杂的表关系查询直接一句sql搞定,再也不像EF那样绞尽脑汁的去设计实体了....而且用dynimic和Json.net返回数据,也可以省下很多model,想想还真是有点小鸡冻,吼吼~~~

再就是还有一个可能在实际使用中可能遇到的问题...就是更新和查询的时候,我们有可能只需要model的其中几个属性(字段),我是这样解决的....

在model里面加了个方法,根据不同的操作需要,返回需要的字段,比如,在更新用户信息的时候,,有些字段是不能动的特别用来加密密码的salt字段,一旦生成了就不能动了...

所以我的model会这样写,由于刚接触C#,完全面向对象的思想还没有深入,我也不知道这种写法会不会违反什么编程原则,总之,先凑合能用就行....如果你有更好的方法,也欢迎交流学习......

GetUpdatePart用户返回编辑用户时需要更新的字段(登录次数,最后登录时间,还有salt字段要避开)
PrepareToUpdate 是在更新数据之前调用一下,传回原来数据库中的数据做对比,看看密码是否更改了,如果密码改了,就根据salt重新加密一下
PrepareToInsert 是在插入新数据之前调用,先生成一个salt,然后再对密码进行加密,同时把登录次数,最后登录时间赋值一下,防止出现null
    public class StaffUser
    {
        [Key] //For Contrib
        [Id(CheckAutoId = true)]//for Ex
        public int ID { get; set; }
        public string UserCode { get; set; }
        public string UserName { get; set; }
        public string Nick { get; set; }
        public string Password { get; set; }
        public byte IsEnable {get; set; }
        public int LoginCount { get; set; }
        public DateTime LastLogin { get; set; }
        public string Description { get; set; }
        public string salt { get; set; }

        //只更新部分字段
        public string[] GetUpdatePart()
        {
            return new string[] { "UserCode", "UserName", "Nick", "Password", "IsEnable", "Description" };
        }
        //更新前准备
        public string PrepareToUpdate(StaffUser Old)
        {
            //检查密码是否被修改
            if (Old.Password!=this.Password)
            {
                //重新生成密码
                this.Password = SDUtility.MD5(SDUtility.MD5(this.Password) + Old.salt);
            }
            return "";
        }

        //添加前准备
        public string PrepareToInsert()
        {
            //生成盐和密码
            this.salt = SDUtility.GetSalt();
            this.Password = SDUtility.MD5(SDUtility.MD5(this.Password) + this.salt);
            LoginCount = 0;
            LastLogin = DateTime.Now;
            return "";
        }

    }

这样,我在插入新数据的时候就可以用 Contrib 了

        private string DoUserInsert(StaffUser row)
        {
            try
            {
                //为插入做准备
                row.PrepareToInsert();
                DB.DbConnecttion.Insert<StaffUser>(row);
                return "";
            }
            catch (Exception ex)
            {
                return row.ID + row.UserName + "插入失败! " + ex.Message;
            }
        }

为了用Contrib在update的时候实现只更新部分字段,所以继续对 SqlMapperExtensions 进行改造,增加了一个 UpdatePart 方法,就是把原来的Update方法里更新所有字段改成只更新参数传入的字段

   public static bool UpdatePart<T>(this IDbConnection connection, T entityToUpdate,string[] UpdateProps , IDbTransaction transaction = null, int? commandTimeout = null) where T : class
        {
            var proxy = entityToUpdate as IProxy;
            if (proxy != null)
            {
                if (!proxy.IsDirty) return false;
            }

            var type = typeof(T);

            var keyProperties = KeyPropertiesCache(type);
            if (!keyProperties.Any())
                throw new ArgumentException("Entity must have at least one [Key] property");

            var name = GetTableName(type);

            var sb = new StringBuilder();
            sb.AppendFormat("update {0} set ", name);

            var allProperties = TypePropertiesCache(type);
            var computedProperties = ComputedPropertiesCache(type);
            //var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties));

            //只更新指定字段
            for (var i = 0; i < UpdateProps.Length; i++)
            {
                var property = UpdateProps[i];
                sb.AppendFormat("{0} = @{1}", property, property);
                if (i < UpdateProps.Length - 1)
                    sb.AppendFormat(", ");
            }
            sb.Append(" where ");
            for (var i = 0; i < keyProperties.Count(); i++)
            {
                var property = keyProperties.ElementAt(i);
                sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
                if (i < keyProperties.Count() - 1)
                    sb.AppendFormat(" and ");
            }
            var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);
            return updated > 0;
        }

然后更新数据的时候这样使用...

private string DoUserUpdate(StaffUser row)
        {
            try
            {
                //取出原来数据
                var old = DB.DbConnecttion.Get<StaffUser>(row.ID);
                //为更新做准备
                row.PrepareToUpdate(old);
                //只更新部分字段
                DB.DbConnecttion.UpdatePart<StaffUser>(row,row.GetUpdatePart());
                return "";
            }
            catch (Exception ex)
            {
                return row.ID + row.UserName + "更新失败! " + ex.Message;
            }

        }
时间: 2024-10-10 00:47:09

初学C#和MVC的一些心得,弯路,总结,还有教训(3)--Dapper的相关文章

初学C#和MVC的一些心得,弯路,总结,还有教训(2)--关于Entity Framework

---恢复内容开始--- 看了一堆视频教程后,感觉基本了解的差不多了,可以动手.....因为最好的学习方法就是实践嘛.... 所以打算从网站做起,在WebForm和MVC之间选了MVC,因为感觉高大上...也比较灵活 于是买了两本书<ASP.NET MVC 4高级编程(第4版)>和<ASP.NET MVC 4 Web编程>,在群里有群友问,为什么要买两本...其实这是习惯啦...对于初学者来说,最先要了解的就是,这个东西大概是个什么东西....书的作者不同,介绍的侧重点也不同,买两

初学C#和MVC的一些心得,弯路,总结,还有教训(1)--语言的选择

---恢复内容开始--- 因为惰性,自制力,求知欲等各方面原因....一直没有学新技术,总感觉VB6凑合能用就凑合用.... 于是大概从05年开始,几乎每次新版的vs一发布,我就下载回来,然后安装,然后,,,,就扔那了..... 直到装了Win8系统,VB6的IDE(SP6)怎么也装不上了,,,,,终于咬牙决定进军.net.. 因为有深厚的VB基础嘛,至少语法上会有相当的相似,所以想都没想就奔着VB.net去了... 可是学了一段时间后发现几个问题 1.感觉关键字冗余,代码不够优美 2.示例代码

初学C#和MVC的一些心得,弯路,总结,还有教训(4)--Cache 关于创建多个缓存实例

asp.net中的数据缓存可以用 HttpRuntime.Cache ,这个是大家都知道的,但如果缓存的数据比较多,又比较杂乱,想要把缓存分开管理(也就是创建多个缓存实例)应该怎么做呢... 于是常规做法来了 using System.Web.Caching; public static class MyCache { public static Cache cache = new Cache(); } 不用说也知道,肯定跑不起来,不然也不会有这篇文章了,使用的时候对新建的 MyCache.ca

ASP.NET MVC TempData使用心得

说明: 在ASP.NET MVC中資料傳遞主要有ViewData與TempData ViewData主要是Controller傳遞Data給View,存留期只有一個Action,要跨Action要使用TempData, 而TempData依TempDataProvider的不同,會有不同的存留期,預設的TempDataProvider是SessionStateTempDataProvider, 你沒有看錯,預設是用Session來存放TempData, Session不是使用者存放資料,而且存留

ASP.NET MVC 開發心得分享 (21):Routing 觀念與技巧

ASP.NET MVC 預設在 Global.asax 所定義的 RegisterRoutes 方法中可以輕易的定義你希望擁有的網址格式,嚴格上來講這並非 ASP.NET MVC 的專利,而是從 ASP.NET 3.5 SP1 就加入的新特性,所以就算是傳統的 ASP.NET Web Form 一樣可以利用 Routing 所帶來的好處,今天我就來講一些 Routing 的觀念與技巧. 快速上手 我先解釋在 ASP.NET MVC 專案中 Global.asax 所定義的 Routing 程式碼

iOS开发与设计模式 - MVC

iOS开发与设计模式 - MVC 最近在学习GoF的设计模式这本书,粗略的浏览了一遍,真是好书.好书就应该好好读,因此很有必要从实际的言语.项目理解设计模式. 我是做iOS开发的,自然就从这方面入手(脑). MVC iOS开发最基本的一个模式就是MVC, M指model,V指view,C指controller,有很多文章对它们是什么,它们的关系,它们如何交互进行了详细的说明,本文就不再展开说明了,仅放一张图供大家参考(来自斯坦福大学ios课程)  ViewController 是什么? 在iOS

ASP.NET MVC学习系列(一)-WebAPI初探

由于即将要接手的新项目计划用ASP.NET MVC3来开发,所以最近一段时间一直在看相关的书或文章.因为之前在大学里也曾学习过MVC2开发,也做过几个简单的MVC2的小型测试项目,不过在后来工作以后主要还是开发WebForm的项目,所以MVC的东西也就逐渐的淡忘了. 经过这一段时间的系统学习,真的觉得MVC3相比于之前的MVC2还有WebForm来说,确实有一种让人欲罢不能爽歪歪的感觉.特别是Razor语法.Linq表达式等的结合运用. 为了将学习过程中遇到的一些值得留意的问题和知识点进行一个很

javaScript中for、with、日期的用法

最近初学javaScript,记录下学习心得吧,javaScript是一种灵活性很高的语言,今天先记录下它的初级应用. with语句:有时候我们需要多次使用dom对象的函数,每次都要声明,例如document.write(),我们就可以用with,免去多次调用的麻烦. example: with(document){ write("Hello"); write("World"); write("<hr>"); } for语句:表示遍历

MVC5中Model层开发数据注解

ASP.NET MVC5中Model层开发,使用的数据注解有三个作用: 数据映射(把Model层的类用EntityFramework映射成对应的表) 数据验证(在服务器端和客户端验证数据的有效性) 数据显示(在View层显示相应的数据) 数据注解相关的命名空间如下: System.ComponentModel.DataAnnotations System.ComponentModel.DataAnnotations.Schema System.Web.Mvc System.Web.Securit