前面已經簡單介紹過了該框架(不一定是框架),本文開始重點記錄其使用過程。可能記錄的內容不是太詳盡,框架也可能非常爛,但是裡面的程式碼句句是實戰專案所得。本文非教唆之類的文章,也非批判之類的文章,更不是炫技之類的文章,只是工作的記錄和總結,希望能夠給大家一些啟迪,忘諸位勿噴!
一. 組建專案需要的幾個部分
.NET中最為經典的三層結構,眾所周知,無人不曉. 在Git.Framework框架中我們也遵循最基本的這種結構,ORM部分我們劃分為如下: 資料實體層,資料訪問介面層,資料訪問層,[層序主入口載入相應的配置]。 在上一篇我們講到了最基本的配置。這幾個層次結構都要遵循一定的規則.
專案結構截圖:
以上是用這個框架開發的一套倉庫管理系統: 其中Git.Storage.Entity是對應的是實體資料層, Git.Storage.IDataAccess 是資料訪問介面層,Git.Storage.DataAccess資料訪問層。當然我們也可以將這些內容合併到一個類庫中,但是一般不建議這麼做。 而Git.Storage.Web 則是專案的檢視層,也就是程式的主入口。
二. 實體類的對映
既然做物件關係對映,那麼我們就要將Entity和資料庫的相關資訊關聯起來。先看看一個實體類的程式碼
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using Git.Framework.ORM; namespace Git.Storage.Entity.InStorage { [TableAttribute(DbName = "JooShowGit", Name = "InStorage", PrimaryKeyName = "ID", IsInternal = false)] public partial class InStorageEntity:BaseEntity { public InStorageEntity() { } [DataMapping(ColumnName = "ID", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=true,AutoIncrement=true,IsMap=true)] public Int32 ID { get; set; } public InStorageEntity IncludeID (bool flag) { if (flag && !this.ColumnList.Contains("ID")) { this.ColumnList.Add("ID"); } return this; } [DataMapping(ColumnName = "OrderNum", DbType = DbType.String,Length=50,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string OrderNum { get; set; } public InStorageEntity IncludeOrderNum (bool flag) { if (flag && !this.ColumnList.Contains("OrderNum")) { this.ColumnList.Add("OrderNum"); } return this; } [DataMapping(ColumnName = "InType", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 InType { get; set; } public InStorageEntity IncludeInType (bool flag) { if (flag && !this.ColumnList.Contains("InType")) { this.ColumnList.Add("InType"); } return this; } [DataMapping(ColumnName = "ProductType", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 ProductType { get; set; } public InStorageEntity IncludeProductType (bool flag) { if (flag && !this.ColumnList.Contains("ProductType")) { this.ColumnList.Add("ProductType"); } return this; } [DataMapping(ColumnName = "SupNum", DbType = DbType.String,Length=50,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string SupNum { get; set; } public InStorageEntity IncludeSupNum (bool flag) { if (flag && !this.ColumnList.Contains("SupNum")) { this.ColumnList.Add("SupNum"); } return this; } [DataMapping(ColumnName = "SupName", DbType = DbType.String,Length=100,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string SupName { get; set; } public InStorageEntity IncludeSupName (bool flag) { if (flag && !this.ColumnList.Contains("SupName")) { this.ColumnList.Add("SupName"); } return this; } [DataMapping(ColumnName = "ContactName", DbType = DbType.String,Length=200,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string ContactName { get; set; } public InStorageEntity IncludeContactName (bool flag) { if (flag && !this.ColumnList.Contains("ContactName")) { this.ColumnList.Add("ContactName"); } return this; } [DataMapping(ColumnName = "Phone", DbType = DbType.String,Length=50,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string Phone { get; set; } public InStorageEntity IncludePhone (bool flag) { if (flag && !this.ColumnList.Contains("Phone")) { this.ColumnList.Add("Phone"); } return this; } [DataMapping(ColumnName = "Address", DbType = DbType.String,Length=200,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string Address { get; set; } public InStorageEntity IncludeAddress (bool flag) { if (flag && !this.ColumnList.Contains("Address")) { this.ColumnList.Add("Address"); } return this; } [DataMapping(ColumnName = "ContractOrder", DbType = DbType.String,Length=50,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string ContractOrder { get; set; } public InStorageEntity IncludeContractOrder (bool flag) { if (flag && !this.ColumnList.Contains("ContractOrder")) { this.ColumnList.Add("ContractOrder"); } return this; } [DataMapping(ColumnName = "ContractType", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 ContractType { get; set; } public InStorageEntity IncludeContractType (bool flag) { if (flag && !this.ColumnList.Contains("ContractType")) { this.ColumnList.Add("ContractType"); } return this; } [DataMapping(ColumnName = "Status", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 Status { get; set; } public InStorageEntity IncludeStatus (bool flag) { if (flag && !this.ColumnList.Contains("Status")) { this.ColumnList.Add("Status"); } return this; } [DataMapping(ColumnName = "IsDelete", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 IsDelete { get; set; } public InStorageEntity IncludeIsDelete (bool flag) { if (flag && !this.ColumnList.Contains("IsDelete")) { this.ColumnList.Add("IsDelete"); } return this; } [DataMapping(ColumnName = "Num", DbType = DbType.Int32,Length=4,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 Num { get; set; } public InStorageEntity IncludeNum (bool flag) { if (flag && !this.ColumnList.Contains("Num")) { this.ColumnList.Add("Num"); } return this; } [DataMapping(ColumnName = "Amount", DbType = DbType.Decimal,Length=9,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public decimal Amount { get; set; } public InStorageEntity IncludeAmount (bool flag) { if (flag && !this.ColumnList.Contains("Amount")) { this.ColumnList.Add("Amount"); } return this; } [DataMapping(ColumnName = "NetWeight", DbType = DbType.Double,Length=8,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public double NetWeight { get; set; } public InStorageEntity IncludeNetWeight (bool flag) { if (flag && !this.ColumnList.Contains("NetWeight")) { this.ColumnList.Add("NetWeight"); } return this; } [DataMapping(ColumnName = "GrossWeight", DbType = DbType.Double,Length=8,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public double GrossWeight { get; set; } public InStorageEntity IncludeGrossWeight (bool flag) { if (flag && !this.ColumnList.Contains("GrossWeight")) { this.ColumnList.Add("GrossWeight"); } return this; } [DataMapping(ColumnName = "OrderTime", DbType = DbType.DateTime,Length=8,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public DateTime OrderTime { get; set; } public InStorageEntity IncludeOrderTime (bool flag) { if (flag && !this.ColumnList.Contains("OrderTime")) { this.ColumnList.Add("OrderTime"); } return this; } [DataMapping(ColumnName = "CreateTime", DbType = DbType.DateTime,Length=8,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public DateTime CreateTime { get; set; } public InStorageEntity IncludeCreateTime (bool flag) { if (flag && !this.ColumnList.Contains("CreateTime")) { this.ColumnList.Add("CreateTime"); } return this; } [DataMapping(ColumnName = "CreateUser", DbType = DbType.String,Length=100,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string CreateUser { get; set; } public InStorageEntity IncludeCreateUser (bool flag) { if (flag && !this.ColumnList.Contains("CreateUser")) { this.ColumnList.Add("CreateUser"); } return this; } [DataMapping(ColumnName = "AuditUser", DbType = DbType.String,Length=100,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string AuditUser { get; set; } public InStorageEntity IncludeAuditUser (bool flag) { if (flag && !this.ColumnList.Contains("AuditUser")) { this.ColumnList.Add("AuditUser"); } return this; } [DataMapping(ColumnName = "AuditeTime", DbType = DbType.DateTime,Length=8,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public DateTime AuditeTime { get; set; } public InStorageEntity IncludeAuditeTime (bool flag) { if (flag && !this.ColumnList.Contains("AuditeTime")) { this.ColumnList.Add("AuditeTime"); } return this; } [DataMapping(ColumnName = "PrintUser", DbType = DbType.String,Length=100,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string PrintUser { get; set; } public InStorageEntity IncludePrintUser (bool flag) { if (flag && !this.ColumnList.Contains("PrintUser")) { this.ColumnList.Add("PrintUser"); } return this; } [DataMapping(ColumnName = "PrintTime", DbType = DbType.DateTime,Length=8,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public DateTime PrintTime { get; set; } public InStorageEntity IncludePrintTime (bool flag) { if (flag && !this.ColumnList.Contains("PrintTime")) { this.ColumnList.Add("PrintTime"); } return this; } [DataMapping(ColumnName = "StoreKeeper", DbType = DbType.String,Length=100,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string StoreKeeper { get; set; } public InStorageEntity IncludeStoreKeeper (bool flag) { if (flag && !this.ColumnList.Contains("StoreKeeper")) { this.ColumnList.Add("StoreKeeper"); } return this; } [DataMapping(ColumnName = "Reason", DbType = DbType.String,Length=800,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string Reason { get; set; } public InStorageEntity IncludeReason (bool flag) { if (flag && !this.ColumnList.Contains("Reason")) { this.ColumnList.Add("Reason"); } return this; } [DataMapping(ColumnName = "OperateType", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public Int32 OperateType { get; set; } public InStorageEntity IncludeOperateType (bool flag) { if (flag && !this.ColumnList.Contains("OperateType")) { this.ColumnList.Add("OperateType"); } return this; } [DataMapping(ColumnName = "EquipmentNum", DbType = DbType.String,Length=50,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string EquipmentNum { get; set; } public InStorageEntity IncludeEquipmentNum (bool flag) { if (flag && !this.ColumnList.Contains("EquipmentNum")) { this.ColumnList.Add("EquipmentNum"); } return this; } [DataMapping(ColumnName = "EquipmentCode", DbType = DbType.String,Length=50,CanNull=false,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string EquipmentCode { get; set; } public InStorageEntity IncludeEquipmentCode (bool flag) { if (flag && !this.ColumnList.Contains("EquipmentCode")) { this.ColumnList.Add("EquipmentCode"); } return this; } [DataMapping(ColumnName = "Remark", DbType = DbType.String,Length=800,CanNull=true,DefaultValue=null,PrimaryKey=false,AutoIncrement=false,IsMap=true)] public string Remark { get; set; } public InStorageEntity IncludeRemark (bool flag) { if (flag && !this.ColumnList.Contains("Remark")) { this.ColumnList.Add("Remark"); } return this; } } public partial class InStorageEntity { [DataMapping(ColumnName = "CreateUserName", DbType = DbType.String)] public string CreateUserName { get; set; } public string InTypeLable { get; set; } public string StatusLable { get; set; } } }
[TableAttribute(DbName = "JooShowGit", Name = "InStorage", PrimaryKeyName = "ID", IsInternal = false)]
這個用於實體的描述特性,用於關聯資料庫中的表,這裡簡單將各個屬性描述一下:
[DBName] 用於適配資料庫連線配置檔案中的name屬性,可以結合下面的xml配置對比看
<?xml version="1.0" encoding="utf-8" ?> <databaseList> <database name="JooShowGit"> <connectionString>Server=127.0.0.1;database=JooShowGit;user id=sa;Password=000000</connectionString> </database> </databaseList>
[Name] 用於關聯到這個連線的資料庫中對應的哪一張表,比如這個配置就是對應資料庫中的InStorage表
[PrimaryKeyName] 指定這張表的主鍵欄位名, 這裡我們一般用主鍵自增列,目前尚不支援聯合組建
[IsInternal] 這個暫無特別意義,作為保留屬性
[DataMapping(ColumnName = "ID", DbType = DbType.Int32,Length=4,CanNull=false,DefaultValue=null,PrimaryKey=true,AutoIncrement=true,IsMap=true)]
[ColumnName] 對應資料庫中的欄位名
[DbType] 對應的資料型別
[Length]對應的資料儲存為長度
[CanNull] 這個欄位是否允許為空
[DefaultValue]預設值
[PrimaryKey]是否主鍵
[AutoIncrement]是否自增
[IsMap] 是否對映欄位 【注意這個標識屬性,如果這個欄位有對應的資料庫對映欄位則為true,否則為false。 因為在某些情況下實體類還有擴充套件欄位,但是這些欄位在資料庫並沒有】
public partial class InStorageEntity:BaseEntity
實體類需要整合BaseEntity類,BaseEntity中定義了很多方法,用於便捷操作輔助操作。
三. 資料訪問介面
文章開頭已經說過了,使用該框架需要遵循一定的規則模式,資料訪問介面也是如此,先看看下面一段程式碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using Git.Framework.ORM; using Git.Storage.Entity.InStorage; namespace Git.Storage.IDataAccess.InStorage { public partial interface IInStorage : IDbHelper<InStorageEntity> { } }
這段程式碼定義了一個介面IInStorage,但是這個介面繼承了泛型介面IDbHelper<T>。 而IDbHelper<T>是框架中定義的一個介面,這個介面封裝了對資料庫操作的一些方法。
public interface IDbHelper<T> : IDisposable where T : Git.Framework.ORM.BaseEntity { int Add(List<T> list); int Add(T entity); int Add(List<T> list, bool isOpenTrans); int Add(T entity, bool isOpenTrans); int Delete(IEnumerable<int> ids); int Delete(int id); int Delete(object value); int Delete(T entity); int Delete(IEnumerable<int> ids, bool isOpenTrans); int Delete(int id, bool isOpenTrans); int Delete(object value, bool isOpenTrans); int Delete(T entity, bool isOpenTrans); int DeleteBatch(IEnumerable<T> list, bool isOpenTrans); int GetCount(); int GetCount(bool isOpenTrans); int GetCount(T entity); int GetCount(T entity, bool isOpenTrans); List<T> GetList(); List<T> GetList(bool isOpenTrans); List<T> GetList(T entity); List<V> GetList<V>(T entity) where V : class, new(); List<T> GetList(T entity, bool isOpenTrans); List<V> GetList<V>(T entity, bool isOpenTrans) where V : class, new(); List<V> GetList<V>(T entity, int pageSize, int pageIndex, out int rowCount) where V : class, new(); List<T> GetList(T entity, int pageSize, int pageIndex, out int rowCount); List<V> GetList<V>(T entity, int pageSize, int pageIndex, out int rowCount, bool isOpenTrans) where V : class, new(); List<T> GetList(T entity, int pageSize, int pageIndex, out int rowCount, bool isOpenTrans); T GetSingle(int id); T GetSingle(object value); T GetSingle(T entity); V GetSingle<V>(T entity) where V : class, new(); T GetSingle(int id, bool isOpenTrans); T GetSingle(T entity, bool isOpenTrans); V GetSingle<V>(T entity, bool isOpenTrans) where V : class, new(); DataTable GetTable(); DataTable GetTable(bool isOpenTrans); DataTable GetTable(T entity); DataTable GetTable(T entity, bool isOpenTrans); DataTable Group(T entity); DataTable Group(T entity, bool isOpenTrans); IEnumerable<Linq.IGrouping<TKey, T>> Group<TKey>(T entity, Func<T, TKey> keySelector); V Max<V>(T entity); V Max<V>(T entity, bool isOpenTrans); V Min<V>(T entity); V Min<V>(T entity, bool isOpenTrans); V Sum<V>(T entity); V Sum<V>(T entity, bool isOpenTrans); List<T> Top(T entity, int pageSize); List<V> Top<V>(T entity, int pageSize) where V : class, new(); List<V> Top<V>(T entity, int pageSize, bool isOpenTrans) where V : class, new(); List<T> Top(T entity, int pageSize, bool isOpenTrans); List<V> Top<V>(T entity, int skipSize, int pageSize) where V : class, new(); List<T> Top(T entity, int skipSize, int pageSize); List<T> Top(T entity, int skipSize, int pageSize, bool isOpenTrans); List<V> Top<V>(T entity, int skipSize, int pageSize, bool isOpenTrans) where V : class, new(); int Update(List<T> list); int Update(T entity); int Update(List<T> list, bool isOpenTrans); int Update(T entity, bool isOpenTrans); }
檢視上面的類,裡面定義了非常多的方法,包含了增刪改查所用的常用方法,使用該介面中的方法可以滿足大部分的增刪改查操作,當然特別複雜的SQL操作是不能完成的,我們還提供了其他的方式來操作,後面詳細說明。
因為IInStorage 介面繼承了IDbHelper<T>介面,那IInStorage也用於了IDbHelper中的所有屬性和方法,但是這裡是一個泛型介面,通過制定操作類我們可以約束該介面操作哪個類,而不會沒有方向性的去操作其他無關類。從介面IDbHelper<T>中我們可以看到T 是使用了泛型約束的,T必須繼承BaseEntity 類。剛好和上面的實體對映類吻合。
四. 資料訪問
資料訪問層要實現資料訪問介面,先看看一下程式碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using Git.Framework.ORM; using Git.Framework.MsSql; using Git.Storage.Entity.InStorage; using Git.Storage.IDataAccess.InStorage; namespace Git.Storage.DataAccess.InStorage { public partial class InStorageDataAccess : DbHelper<InStorageEntity>, IInStorage { public InStorageDataAccess() { } } }
以上程式碼也沒有什麼特別的地方,只是這個類繼承了一個實體類,並且實現了一個介面
public partial class InStorageDataAccess : DbHelper<InStorageEntity>, IInStorage
DbHelper<InStorageEntity> 這個是框架中自帶的一個泛型類,該泛型類實現了泛型介面IDbHelper<T> , 上面這個類還實現了介面IInStorage介面,所以這個類也必須實現
IInStorage中的擴充套件的方法。
五. 如何使用
怎麼使用以上框架運算元據庫,其實非常簡單了,使用介面new 一個子類物件即可,沒有其他特殊的方式,也沒有使用IOC注入等。看看下面的程式碼
public IInStorage InStorage { get { return new InStorageDataAccess(); } } -------------------------------------------------------------------------------- public override string Create(InStorageEntity entity, List<InStorDetailEntity> list) { using (TransactionScope ts = new TransactionScope()) { int line = 0; entity.OrderNum = entity.OrderNum.IsEmpty() ? (new TNumProivder()).GetSwiftNum(typeof(InStorageEntity), 5) : entity.OrderNum; entity.IncludeAll(); if (!list.IsNullOrEmpty()) { list.ForEach(a => { a.IncludeAll(); a.OrderNum = entity.OrderNum; }); entity.Num = list.Sum(q => q.Num); entity.Amount = list.Sum(a => a.Amount); line = this.InStorage.Add(entity); line += this.InStorDetail.Add(list); } ts.Complete(); return line > 0 ? EnumHelper.GetEnumDesc<EReturnStatus>(EReturnStatus.Success) : string.Empty; } }
上面的程式碼用於建立一個單據的,單據在系統中一個是一個抽象類,這裡不過多的累述。
entity.IncludeAll(); 注意到這個語句,在插入資料的時候要呼叫這個方法,這個方法用於自定哪些欄位插入到資料庫,後面詳述具體過程。
this.InStorage.Add(entity); 最終我們只需要將呼叫這個方法Add 就可以將實體InStorageEntity的例項資料插入到對應的資料庫表InStorage中。
六. 總結
要使用此框架可以整理為一下結果步驟:
(1)新建工程並且引入框架中提供的類庫
(2)在主程式中配置相應的配置檔案,用於連線資料庫[配置文章必須遵循規則以及存放路徑]
(3)編寫或者生產實體對映類,資料訪問介面,資料訪問類三部分,資料訪問介面有時候也可以省略
(4)建立介面例項呼叫介面中提供的方法
總的來說該操作相對於ADO.NET還是比較方便的,但是和目前一些較為流行的框架比如Linq to SQL,Entity Framework 等比較還是比較有差距,所以還需要不斷的努力完善。
目前公司一直在使用這個框架,效果還不錯。如果你要說這些東西什麼什麼框架都可以實現,為什麼要自己搞一套,我只能說這是積累!