Chloe.ORM框架应用实践
Chloe.ORM 是国人开发的一款数据库访问组件,很是简单易用。目前支持四种主流数据库:SqlServer、MySQL、Oracle,以及Sqlite,作者为这四种数据库划分出了各自对应的组件程序集,以 MySQL 为例即 Chloe.MySql.dll,其他以此类推,可以同时引用这些程序集从而在一个项目中访问多种数据库,另外 Chloe 用的是 Emit 生成 IL 代码,这样避免了反射机制造成的性能损耗。
Chloe 的文档对基础操作列举得很全面,我就针对实践中的一些应用体会做些记录,也当是备忘后查。
一、基于工厂模式多数据库访问机制的构建
1、数据库访问连接串
<!-- 默认数据库类型(其值参考枚举 DatabaseType 的项)-->
<add key="DefaultDb" value="MySQL" />
<!-- MySQL 默认数据库连接字符串 -->
<add key="MySQLConnectionString" value="Data Source=192.168.100.20;port=3306;Initial Catalog=Order;user id=sa;password=123456sa;pooling=true;AllowZeroDatetime=true;ConvertZeroDatetime=true;Charset=utf8" />
<!-- Oracle 默认数据库连接字符串 -->
<add key="OracleConnectionString" value="Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=228.10.135.8)(PORT=1521)))(CONNECT_DATA=(SID=orcl2)));User Id=sa;Password=123456sa;Pooling=true;MAX Pool Size=20;Min Pool Size=2;Connection Lifetime=20;Connect Timeout=20;" />
定义在 appSettings 节点下。
DefaultDb 表示在构建数据库连接对象时,采用默认方式使用的数据库类型。
MySQLConnectionString 和 OracleConnectionString 表示针对指定数据库的默认连接字符串。
2、数据库类型枚举
/// <summary>
/// 数据库类型
/// </summary>
public enum DatabaseType
{
MySQL = 1,
Oracle = 2
}
如果需要,可以继续追加 SqlServer 和 Sqlite。
3、数据库连接工厂接口
using System.Data;
namespace Chloe.Infrastructure
{
public interface IDbConnectionFactory
{
IDbConnection CreateConnection();
}
}
注:该接口在 Chloe 的底层已为我们定义好了。
4、面向具体数据库工厂类的实现
/// <summary>
/// 针对 MySQL 数据库的连接工厂类
/// </summary>
public class MySqlConnectionFactory : IDbConnectionFactory
{
string _connString = string.Empty;
public MySqlConnectionFactory()
{
this._connString = "server=192.168.120.68; port=3306; User Id=sa; password=123456sa; database=OrderAutoCategory; charSet=utf8;";
}
public MySqlConnectionFactory(string connString)
{
this._connString = connString;
}
public IDbConnection CreateConnection()
{
MySqlConnection conn = new MySqlConnection(this._connString);
return conn;
}
}
/// <summary>
/// 针对 Oracle 数据库的连接工厂类
/// </summary>
public class OracleConnectionFactory : IDbConnectionFactory
{
string _connString = string.Empty;
public OracleConnectionFactory()
{
this._connString = @"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=228.10.135.8)(PORT=1521)))(CONNECT_DATA=(SID=orcl2)));User Id=sa;Password=123456sa;Pooling=true;MAX Pool Size=20;Min Pool Size=2;Connection Lifetime=20;Connect Timeout=20;";
}
public OracleConnectionFactory(string connString)
{
this._connString = connString;
}
public IDbConnection CreateConnection()
{
OracleConnection oracleConnection = new OracleConnection(this._connString);
OracleConnectionDecorator conn = new OracleConnectionDecorator(oracleConnection);
return conn;
}
}
出于修改 DbCommand 参数绑定方式的目的,作者定义了一个装饰类 OracleConnectionDecorator,在项目实践中我们直接从官网复制过来使用即可。
5、用以构建 IDbContext 实例的自定义工厂类
using System;
using System.Configuration;
using Chloe;
using Chloe.Infrastructure.Interception;
using Chloe.MySql;
using Chloe.Oracle;
namespace Pro.Factory
{
public class DbContextFactory
{
public static IDbContext CreateDbContext()
{
// 数据库类型
DatabaseType dbType = GetDatabaseType(ConfigurationManager.AppSettings["DefaultDb"]);
// 连接字符串
string connectionString = GetConnectionString(dbType);
return CreateDbContext(dbType, connectionString);
}
public static IDbContext CreateDbContext(DatabaseType dbType)
{
string connectionString = GetConnectionString(dbType);
return CreateDbContext(dbType, connectionString);
}
public static IDbContext CreateDbContext(string connectionString)
{
DatabaseType dbType = GetDatabaseType(ConfigurationManager.AppSettings["DefaultDb"]);
return CreateDbContext(dbType, connectionString);
}
public static IDbContext CreateDbContext(DatabaseType dbType, string connectionString)
{
IDbContext context = null;
switch (dbType)
{
case DatabaseType.MySQL:
context = new MySqlContext(new MySqlConnectionFactory(connectionString));
break;
case DatabaseType.Oracle:
context = new OracleContext(new OracleConnectionFactory(connectionString));
break;
default:
throw new Exception("在工厂 DbContextFactory 中试图创建 IDbContext 时,发现数据库类型不明确(考虑遗漏了类型)");
}
IDbCommandInterceptor interceptor = new DbCommandInterceptor();
// 全局拦截器
//DbInterception.Add(interceptor);
// 单个DbContext拦截器
if (context != null)
{
context.Session.AddInterceptor(interceptor);
}
return context;
}
/* 公共函数 */
public static string GetConnectionString(DatabaseType dbType)
{
string connectionString = "";
switch (dbType)
{
case DatabaseType.MySQL:
connectionString = ConfigurationManager.AppSettings["MySQLConnectionString"];
break;
case DatabaseType.Oracle:
connectionString = ConfigurationManager.AppSettings["OracleConnectionString"];
break;
default:
throw new Exception("在工厂 DbContextFactory 中试图创建 IDbContext 时,发现数据库类型不明确(考虑遗漏了类型)");
}
if (string.IsNullOrEmpty(connectionString))
{
throw new Exception(string.Format(@"基于 {0} 数据库的连接字符串为空,需进行配置", dbType.ToString()));
}
return connectionString;
}
public static DatabaseType GetDatabaseType(string dbTypeName)
{
if (string.IsNullOrEmpty(dbTypeName))
{
throw new Exception("需配置默认数据库类型 DefaultDb ");
}
DatabaseType dbType = (DatabaseType)Enum.Parse(typeof(DatabaseType), dbTypeName);
return dbType;
}
}
}
上述代码的核心方法为 CreateDbContext,共提供了 4 个重载。
第一个无参数重载方法表示一切按默认的配置项进行初始化,从其代码可以看到,“数据库类型”是由配置节点的 DefaultDb 决定,然后调用了 GetConnectionString 方法来确立“连接字符串”,它会针对不同种类数据库安排一个默认的连接。
第二个重载方法要求提供“数据库类型”,然后直接调用 GetConnectionString 来确立“连接字符串”即可。
第三个重载方法要求提供“连接字符串”,那么“数据库类型”是由配置节点的 DefaultDb 决定。
第四个重载方法要求提供“数据库类型”和“连接字符串”,在调用时要确保这两个参数的值是统一的,即如果“数据库类型”是 MySQL 的话,那么“连接字符串”也必须是基于 MySQL 数据库。
另外,在第四个重载方法中还实现了拦截器功能,目的在于截取 SQL 语句,以备后查。
二、实体类解析
[Table("OrderDistributeRouteConfigCode")]
public class RouteConfigCode
{
[NonAutoIncrement]
[Column(IsPrimaryKey = true, Name = "Guid")]
public string Guid { get; set; }
[NotMapped]
public string DistributeSiteName { get; set; }
}
列举一下四个最常用的特性:
- Table 为表名映射,对应数据库表名
- Column 为列名映射,对应数据库列名
- NonAutoIncrement 表示该列为“非自增长”,意味着开发者要自行赋值
- NotMapped 表示该列不与任何表字段进行映射,比如在统计时,当某属性是通过二次计算得来时则可以标识该特性。
三、增删改查
1、新增
public BaseResult Add(RouteConfigCodeEdit edit)
{
BaseResult result = BaseResult.Fail();
DateTime currentDatetime = DateTime.Now;
using (IDbContext dbContext = DbContextFactory.CreateDbContext())
{
try
{
dbContext.Session.BeginTransaction();
RouteConfigCode entity = new RouteConfigCode();
entity.OrderDistributeRouteConfigGuid = edit.OrderDistributeRouteConfigGuid;
entity.SiteCode = edit.SiteCode;
entity.SiteName = edit.SiteName;
entity.OrderType = edit.OrderType;
entity.IsMQ = edit.IsMQ;
entity.Remarks = edit.Remarks;
entity.IsEnable = edit.IsEnable;
entity.Guid = Guid.NewGuid().ToString();
entity.CreateTime = currentDatetime;
entity.LastUpdateTime = currentDatetime;
dbContext.Insert(entity);
dbContext.Session.CommitTransaction();
result.Status = true;
result.StatusMessage = "新增成功";
}
catch (Exception ex)
{
dbContext.Session.RollbackTransaction();
NLogHelper.Error(ex);
result.StatusMessage = ex.Message;
}
}
return result;
}
整个业务逻辑操作都囊括在 using 块中,这样确保由 DbContextFactory 工厂构建的 IDbContext 连接对象可以及时的被关闭和销毁。
紧接着,拟定 try/catch 来分管期望与意外这两种情形,如果所有业务操作都在期望之中则正常提交事务(Commit),并返回相关状态为 true;如果操作期间发生了不可预测的意外情形,则通过 catch 块来捕获异常,首当其冲是回滚事务(Rollback),然后记录文本日志(txt),并返回异常内容给调用方。
使用基于 Insert 方法可以做到参数化,要注意的是它会把实体中所有的属性组织到 SQL 语句中。
2、修改
public BaseResult Update(RouteConfigCodeEdit edit)
{
BaseResult result = BaseResult.Fail();
DateTime currentDatetime = DateTime.Now;
using (IDbContext dbContext = DbContextFactory.CreateDbContext())
{
try
{
dbContext.Session.BeginTransaction();
RouteConfigCode entity = dbContext.Query<RouteConfigCode>().Where(p => p.Guid == edit.Guid).FirstOrDefault();
if (entity != null)
{
dbContext.TrackEntity(entity);
entity.Guid = edit.Guid;
entity.OrderDistributeRouteConfigGuid = edit.OrderDistributeRouteConfigGuid;
entity.SiteCode = edit.SiteCode;
entity.SiteName = edit.SiteName;
entity.OrderType = edit.OrderType;
entity.IsMQ = edit.IsMQ;
entity.Remarks = edit.Remarks;
entity.IsEnable = edit.IsEnable;
entity.LastUpdateTime = currentDatetime;
int effectedRows = dbContext.Update(entity);
result.Status = true;
result.StatusMessage = "修改成功";
}
else
{
result.Status = false;
result.StatusMessage = "修改失败,记录不存在";
}
dbContext.Session.CommitTransaction();
}
catch (Exception ex)
{
dbContext.Session.RollbackTransaction();
NLogHelper.Error(ex);
result.StatusMessage = ex.Message;
}
}
return result;
}
修改操作的重点在于属性跟踪,为避免不必要的属性更新,我们应尽量只更新那些发生了变化的属性,或者说被修改过的属性,所以为属性赋值之前就需要调用一次 TrackEntity 方法,最后才是调用 Update 方法,该方法支持参数化处理。
3、删除
public BaseResult Delete(string ids)
{
DateTime currentDatetime = DateTime.Now;
BaseResult result = BaseResult.Error("操作失败,");
using (IDbContext dbContext = DbContextFactory.CreateDbContext())
{
try
{
dbContext.Session.BeginTransaction();
// 批量操作时累计受影响行数
int total = 0;
string[] idArray = ids.Split(",");
foreach (string id in idArray)
{
RouteConfigCode entity = new RouteConfigCode();
entity.Guid = id;
int effectedRows = dbContext.Delete(entity);
if (effectedRows > 0)
{
total += effectedRows;
}
}
dbContext.Session.CommitTransaction();
result.Status = true;
result.StatusMessage = string.Format("操作成功,总记录:{0},执行成功:{1}", idArray.Length, total);
}
catch (Exception ex)
{
dbContext.Session.RollbackTransaction();
NLogHelper.Error(ex);
result.StatusMessage += ex.Message;
}
}
return result;
}
实例化一个对象,并对主键列赋值,然后传递给 Delete 方法即可,该方法支持参数化处理。
4、分页查询
分页 Pager:
public class Pager
{
public int totalRows { set; get; }
public int pageSize { set; get; }
public int pageNo { set; get; }
public int totalPages { set; get; }
public string direction { set; get; }
public string sort { set; get; }
public object rows { set; get; }
public Pager()
{
totalRows = 0;
pageSize = 20;
pageNo = 1;
totalPages = 0;
}
}
业务查询实体:
public class RouteConfigCodeSearch
{
public Pager Pager { get; set; }
public string SiteCode { get; set; }
public string SiteName { get; set; }
}
业务查询实体除了包含 Pager 之外还包含了查询栏里的各项条件,比如按编号(SiteCode)、按名称(SiteName)。
分页查询:
public List<RouteConfigCode> GetListByPage(RouteConfigCodeSearch search)
{
List<RouteConfigCode> routeConfigCodeList = new List<RouteConfigCode>();
using (IDbContext dbContext = DbContextFactory.CreateDbContext())
{
var query = dbContext.Query<RouteConfigCode>()
.LeftJoin<RouteConfig>((code, routeConfig) => code.OrderDistributeRouteConfigGuid == routeConfig.Guid)
.Select((code, routeConfig) => new RouteConfigCode
{
DistributeSiteName = routeConfig.DistributeSiteName,
Guid = code.Guid,
OrderDistributeRouteConfigGuid = code.OrderDistributeRouteConfigGuid,
SiteCode = code.SiteCode,
SiteName = code.SiteName,
OrderType = code.OrderType,
Remarks = code.Remarks,
CreateTime = code.CreateTime,
LastUpdateTime = code.LastUpdateTime,
IsEnable = code.IsEnable,
IsMQ = code.IsMQ
});
#region 查询条件
if (!string.IsNullOrEmpty(search.SiteCode))
{
query = query.Where(p => p.SiteCode.Contains(search.SiteCode));
}
if (!string.IsNullOrEmpty(search.SiteName))
{
query = query.Where(p => p.SiteName.Contains(search.SiteName));
}
#endregion
routeConfigCodeList = query.OrderBy(p => p.CreateTime).TakePage(search.Pager.pageNo, search.Pager.pageSize).ToList();
search.Pager.totalRows = query.Count();
}
return routeConfigCodeList;
}
通过 TakePage 方法就可以很方便的实现分页功能了,同时把总记录数赋给 totalRows 属性以告知调用者。
5、单条查询
public BaseResult GetItemById(string id)
{
JsonResult<RouteConfigCode> result = new JsonResult<RouteConfigCode>();
using (IDbContext dbContext = DbContextFactory.CreateDbContext())
{
try
{
RouteConfigCode entity = dbContext.Query<RouteConfigCode>().Where(p => p.Guid == id).FirstOrDefault();
if (entity == null)
{
result.Status = false;
result.StatusMessage = "查询记录失败";
}
else
{
result.Data = entity;
}
}
catch (Exception ex)
{
NLogHelper.Error(ex);
result.Status = false;
result.StatusMessage = ex.Message;
}
}
return result;
}
通过 FirstOrDefault 可以确保只查询一条记录,如果找不到则返回 null。
在使用 Chloe.ORM 的过程中总体感觉非常顺畅,满足了简单、易用的图快心理,重点是作者很热心,在QQ群里发问他都能及时回复。园友们也可以尝试用用看。
Chloe.ORM框架应用实践的更多相关文章
- Node.js ORM 框架 sequelize 实践
最近在做团队的一个内部系统,这次使用的nodejs web框架是团队统一的hapi.js,而数据库依然是mysql,ORM 框架选用有着6000+ stars 的 sequelize.js,hapi- ...
- [开源].NET数据库访问框架Chloe.ORM
扯淡 13年毕业之际,进入第一家公司实习,接触了 EntityFramework,当时就觉得这东西太牛了,访问数据库都可以做得这么轻松.优雅!毕竟那时还年轻,没见过世面.工作之前为了拿个实习机会混个工 ...
- 类EF框架Chloe.ORM升级:只为更完美
扯淡 Chloe.ORM:一款轻量.高效的.NET C#数据库访问框架(ORM).查询接口借鉴 Linq(但不支持 Linq).借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接 ...
- .NET ORM框架 SqlSuagr4.0 功能详解与实践【开源】
SqlSugar 4.0 ORM框架的优势 为了未来能够更好的支持多库分布式的存储,并行计算等功能,将SqlSugar3.x全部重写,现有的架构可以轻松扩展多库. 源码下载: https://gith ...
- [开源]无sql之旅-Chloe.ORM之增删查改
扯淡 这是一款轻量.高效的.NET C#数据库访问框架(ORM).查询接口借鉴 Linq(但不支持 Linq).借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接查询.分组查询. ...
- 高品质开源工具Chloe.ORM:支持存储过程与Oracle
扯淡 这是一款高质量的.NET C#数据库访问框架(ORM).查询接口借鉴 Linq.借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接查询.分组查询.聚合查询.插入数据.批量删 ...
- 吉特仓库管理系统-ORM框架的使用
最近在园子里面连续看到几篇关于ORM的文章,其中有两个印象比较深刻<<SqliteSugar>>,另外一篇文章是<<我的开发框架之ORM框架>>, 第一 ...
- 如何做好一个ORM框架
很多人都不太认可以第三方ORM,因为考虑的点不够全面,没有用户群体大的ORM有保证,这点是不可否认确是事实. 但是往往用户群体大的ORM又有不足之处,就拿用户群体最多的两个ORM来说一下吧 1.EF ...
- 利用抽象、多态实现无反射的绿色环保ORM框架
最近一直在忙新公司的基础库建设,对系统架构.开发框架及快速开发平台的设计实施都积累了一定的实践经验. 一般的中小型的软件开发公司,如果按照技术储备来衡量软件项目的技术含量的评定依据是可行的.但如果光是 ...
随机推荐
- 自制mpls ldp实验
实验步骤 步骤1:完成EIGRP BGP 及宣告配置 步骤2:完成LDP 的配置 过程校验 步骤1:校验和理解LDP 邻居关系的发现和邻接关系的建立 R4#show mpls ldp discover ...
- 集美大学网络1413第十五次作业成绩(团队十) -- 项目复审与事后分析(Beta版本)
题目 团队作业10--项目复审与事后分析(Beta版本) 团队作业10成绩 --团队作业10-1 Beta事后诸葛亮 团队/分值 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色 ...
- 201521123072《Java程序设计》第1周学习总结
201521123072<Java程序设计>第1周学习总结 标签(空格分隔): JAVA学习 1,本周学习总结 了解了JDK,JRE,JVM,学会使用cmd中一些简单的命令 了解了Java ...
- 201521123106《java程序设计》第一周学习总结
1.本章学习总结 认识了java语言,了解了java的历史,学习了各种java相关文件的使用,能够进行基本的程序操作,学会了使用博客.码云. 2.书面作业 1.为什么java程序可以跨平台运行?执行j ...
- 201521123070 《JAVA程序设计》第14周学习总结
1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 Q1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现 ...
- eclipse 项目引入第三方jar包 3种方法
我们在用Eclipse开发程序的时候,经常要用到第三方jar包.引入jar包不是一个小问题,由于jar包位置不清楚,而浪费时间.下面配图说明3种Eclipse引入jar包的方式. 1.最常用的普通操作 ...
- 201621123088《Java程序设计》第1周学习总结
1.本周学习总结 以几个关键词描述本周的学习内容.并阐述关键概念之间的联系. 这周是我第一次学习Java,对于我这个上学期没有学好的人来说,Java无疑是一个新的噩梦,但是我相信我这学期一定能学好Ja ...
- 巧用 BootStrap --- 栅格系统(布局)轻松搞定网页响应式布局!
摘要:Bootstrap 为我们提供了一套响应式.移动设备优先的流式栅格系统,合理的使用栅格系统将会使得网站页面布局变得更加简单,在设置了媒体查询之后,响应式网站也无需再单独写了.接下来我以Boots ...
- vim格式化代码
在命令模式下,按键盘gg=G 命令含义: gg:到达文件头=:缩进G:直到文件尾
- 一篇搞定微信分享和line分享
前言 在h5的页面开发中,分享是不可或缺的一部分,对于一些传播性比较强的页面,活动页之类的,分享功能极为重要.例如,京东等电商年末时会有一系列的总结h5在微信中传播,就不得不提到微信的分享机制. 微信 ...