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羣裏發問他都能及時回覆。園友們也能夠嘗試用用看。