Chloe.ORM 是国人开发的一款数据库访问组件,非常简单易用。目前支持四种主流数据库:SqlServer、MySQL、Oracle,以及Sqlite,做者为这四种数据库划分出了各自对应的组件程序集,以 MySQL 为例即 Chloe.MySql.dll,其余以此类推,能够同时引用这些程序集从而在一个项目中访问多种数据库,另外 Chloe 用的是 Emit 生成 IL 代码,这样避免了反射机制形成的性能损耗。数据库
Chloe 的文档对基础操做列举得很全面,我就针对实践中的一些应用体会作些记录,也当是备忘后查。oracle
<!-- 默认数据库类型(其值参考枚举 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 节点下。app
DefaultDb 表示在构建数据库链接对象时,采用默认方式使用的数据库类型。
MySQLConnectionString 和 OracleConnectionString 表示针对指定数据库的默认链接字符串。函数
/// <summary> /// 数据库类型 /// </summary> public enum DatabaseType { MySQL = 1, Oracle = 2 }
若是须要,能够继续追加 SqlServer 和 Sqlite。性能
using System.Data; namespace Chloe.Infrastructure { public interface IDbConnectionFactory { IDbConnection CreateConnection(); } }
注:该接口在 Chloe 的底层已为咱们定义好了。ui
/// <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,在项目实践中咱们直接从官网复制过来使用便可。this
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 个重载。spa
第一个无参数重载方法表示一切按默认的配置项进行初始化,从其代码能够看到,“数据库类型”是由配置节点的 DefaultDb 决定,而后调用了 GetConnectionString 方法来确立“链接字符串”,它会针对不一样种类数据库安排一个默认的链接。日志
第二个重载方法要求提供“数据库类型”,而后直接调用 GetConnectionString 来确立“链接字符串”便可。code
第三个重载方法要求提供“链接字符串”,那么“数据库类型”是由配置节点的 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; } }
列举一下四个最经常使用的特性:
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 语句中。
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 方法,该方法支持参数化处理。
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 方法便可,该方法支持参数化处理。
分页 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 属性以告知调用者。
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群里发问他都能及时回复。园友们也能够尝试用用看。