CRL快速開發框架開源完全轉到Github


 

CRL簡介

CRL是一款面向對象的輕量級ORM框架,本着快速開發,使用簡便的原則,設計為

  1. 無需關心數據庫結構,CRL自動維護創建,即寫即用(CRL內部有表結構檢查機制,保證表結構一致性)
  2. 無需第三方工具生成代理類,標准對象結構即可
  3. 基於Linq.Expression語法解析,完全對象化操作,所有變量參數化處理,無注入安全等問題
  4. 支持join,group等常用語法和函數擴展方法
  5. 多種結果類型返回,對象,自定義對象,泛類型,字典等
  6. 多種數據庫,多庫支持
  7. 可封裝繼承的結構,充分使用面向對象的特性

使用CRL快速開發框架有哪些區別?

將此定義為快速開發框架,快速體現在:

  1. 無需關心數據表結構,框架會自動維護,只需要按面向對象的方式定義業務類
  2. 通過面向對象的特性,所有業務類和業務都可以被繼承,重用
  3. 數據訪問層不存在了,框架內置處理了,支持多種數據庫,一些功能可能不支持,如自動編譯
  4. 不像傳統的三層結構,一些基本增刪改查方法不需要寫了,這些在業務基類中以泛類型實現過了

還有哪些區別?

  • 自動編譯系統(僅MSSQL),上面講到的的表結構自動同步也屬於其中一部份,另外,對於一些復雜的查詢,默認都會編譯為存儲過程,如分頁
    對於一個分頁查詢,如:
    var query = Code.ProductDataManage.Instance.GetLambdaQuery();
                query.Where(b => b.Id > 0);
                query.Join<Code.Member>((a, b) => a.UserId == b.Id).Select((a, b) => new { a.ProductName, b.Name });//篩選返回的字段
                query.Join<Code.Order>((a, b) => a.UserId == b.UserId).Select((a, b) => new { orderid = b.OrderId });//篩選返回的字段
                query.OrderBy(b=>b.BarCode);
                query.Page(15,1);

    盡管條件再復雜,只要是基於LambdaQuery語法表示,都會編譯成等效的存儲過程

  • 緩存綁定,CRL能在所有業務類創建緩存,調用非常簡單,只是和數據庫查詢方法不同,如:
    ProductDataManage.Instance.QueryItemFromCache(1);//從緩存查詢
    ProductDataManage.Instance.QueryItem(1);//從數據庫查詢
    並且.緩存是自動維護,過期后后台線程自動更新,在相同應用程序下,調用Update方法時,也會更新對應的緩存項
  • 內置大數據分庫分表解決方案 ,通過CRL的結構,輕松實現此方案
  • 基於為oData查詢形式的分布式緩存結構,實現CRL分布式對象緩存
  • CRL.Package業務封裝庫,對於抽象,可重復使用的業務或組件可通過CRL進行封裝重用
    如會員和商家,都繼承於CRL.Package.Person.Person
    共同方法,登錄,密碼修改,驗證等方法就統一重用了

 

最新版由於方法和結構有所更改,升級到3.1

3.1后升級內容

2015-11-20
    版本升級為3.1
    http://www.cnblogs.com/hubro/p/4981728.html
    去掉了業務類里LambdaQuery為參數的查詢方法,改為直接由LambdaQuery進行查詢返回
    返回結果為以下類型
    List<dynamic> ToDynamic()
    List<TResult> ToList<TResult>()
    List<T> ToList()
    Dictionary<TKey, TValue> ToDictionary<TKey, TValue>()
    篩選值時,能按關聯表選擇了,GROUP也一樣
2015-11-21
    優化分頁判斷
    修改匿名對象查詢,使能正確返回值
    關聯查詢改為查詢分支了,Join方法會創建一個LambdaQueryJoin對象進行鏈式調用
2015-11-25
    增加Nullable可空類型屬性支持
    public DateTime? Birthday
    {
        get;
        set;
    }
2015-11-25
    優化表達式動態編譯,使常量不再編譯成委托
2015-12-04
    優化了關聯,使關聯后還能關聯
2015-12-07
    修改字段別名判斷出錯
    增加測試類TestAll
2015-12-11
    統一函數方法查詢語法,增加返回單個字段返回方法GetScalar
2015-12-15
    去掉NOTIN,NOTLIKE擴展方法,擴展方法源類型改為強類型
    優化緩存按主鍵查詢方法,直接用鍵值查詢

 

到目前為止,常用查詢語法和函數方法已經解析完成,支持功能表現在:

  • Join 多表N 次關聯查詢
  • Sum,Count,Max,Min函數統計
  • 表Group,Distinct
  • 查詢字段篩選,支持別名
  • 可為空屬性類型Nullable
  • 支持虛擬字段
  • 基本邏輯判斷語法和擴展函數方法
  • 多表字段排序
  • 多種結果類型返回
  • 任意查詢分頁

3.1主要優化內容

  1. 優化查詢語法,統一了結果返回方法
  2. 增強了表結構檢查,后台線程會強制檢查對像與表結構一致性

 

更新1:語法查詢優化

語法查詢優化表現在以下幾個方面

  • 字段間二元運算 
    query.Select(b => new {aa = b.Id * b.Number })//選擇字段時二元運算 
    query.Where(b => b.Id < b.Id * b.Number)//條件選擇時二元運算
  • 表達式不區分左右
    query.Where(b => 10 > b.Id);和query.Where(b => b.Id < 10 ); 等效
  • 一元運算支持 
    query.Where(b => !b.IsTop)//等效為 isTop!=1 目前只處理了BOOL類型 
    query.Where(b => !b.ProductName.Contains("122"))//BOOL類型的擴展方法同樣支持
  • 關聯查詢支持以上所有特性
    query.Join<Code.Member>((a, b) => a.UserId == b.Id && a.Id == c).Select((a, b) => new { a.BarCode, Year2 = b.Year * b.Id }); 
    關聯查詢支持關聯表排序 query.OrderBy<Code.Member>(b => b.Name, true)
  • 多列排序,只需調用OrderBy方法多次
    query.OrderBy(b => b.Id, true);//按ID倒序
    query.OrderBy(b => b.Name, false)//按Name正序
    結果等效為 order by Id desc,Name asc

 

查詢方法修改

  • 使用LambdaQuery完整查詢調用Page方法就可以分頁了,通過ToList和ToDynamic方法返回指定類型結果
  • 當設置了關聯,Group語法,也會按關聯,Group語法解析
  • 當調用了Select方法篩選字段,則需要根據實際情況返回結果類型
  • 返回結果可以有以下幾種類型
    • List<dynamic> ToDynamic() 按篩選值返回動態類型
    • List<TResult> ToList<TResult>() 按篩選值返回指定類型
    • List<T> ToList() 直接返回當前類型
    • Dictionary<TKey, TValue> ToDictionary<TKey, TValue>() 按篩選值返回字典(不支持分頁)

示例

代碼1(實現Group查詢)

//Group查詢
            var query = Code.ProductDataManage.Instance.GetLambdaQuery();
            query.Select(b => new { b.BarCode, total = b.BarCode.COUNT() }); query.Where(b => b.Id > 0); query.GroupBy(b => new { b.BarCode }); query.OrderBy(b => b.BarCode.COUNT(), true);//Group需要設置排序 query.Page(15,1);//如果要分頁,設定分頁參數就行了 var list = query.ToDynamic(); int total = query.RowCount;

輸出

select  t1.BarCode,COUNT(t1.BarCode) as total  from [ProductData] t1  with(nolock)    where (t1.Id>@parame0) group by t1.BarCode  order by  COUNT(t1.BarCode) desc
[parame0]:[0]

 

代碼2(實現關聯查詢)

//關聯查詢
            var query = Code.ProductDataManage.Instance.GetLambdaQuery();
            query.Where(b => b.Id > 0); query.Join<Code.Member>((a, b) => a.UserId == b.Id).Select((a, b) => new { a.BarCode, b.Name }); query.Page(15,1);//如果要分頁,設定分頁參數就行了 var list = query.ToDynamic(); int total = query.RowCount;

輸出

select  t1.BarCode,t2.Name  from [ProductData] t1  with(nolock)   Inner join Member t2   with(nolock) on (t1.UserId=t2.Id)  where (t1.Id>@parame0) 
[parame0]:[0]

 

代碼3(實現關聯再Group)

//關聯再Group查詢
            var query = Code.ProductDataManage.Instance.GetLambdaQuery();
            query.Where(b => b.Id > 0); query.Join<Code.Member>((a, b) => a.UserId == b.Id).GroupBy((a, b) => new { a.BarCode, b.Name }).Select((a, b) => new { a.BarCode, b.Name }); query.OrderBy(b=>b.BarCode); query.Page(15,1);//如果要分頁,設定分頁參數就行了 var list = query.ToDynamic(); int total = query.RowCount;

輸出

select  t1.BarCode,t2.Name  from [ProductData] t1  with(nolock)   Inner join Member t2   with(nolock) on (t1.UserId=t2.Id)  where (t1.Id>@parame0) group by t1.BarCode,t2.Name  order by  t1.BarCode desc
[parame0]:[0]

 代碼4(實現多次關聯)

//多表關聯查詢
            var query = Code.ProductDataManage.Instance.GetLambdaQuery();
            query.Where(b => b.Id > 0); query.Join<Code.Member>((a, b) => a.UserId == b.Id).Select((a, b) => new { a.ProductName, b.Name });//篩選返回的字段 query.Join<Code.Order>((a, b) => a.UserId == b.UserId).Select((a, b) => new { orderid = b.OrderId });//篩選返回的字段 query.OrderBy(b=>b.BarCode); query.Page(15,1);//如果要分頁,設定分頁參數就行了 var list = query.ToDynamic(); int total = query.RowCount;

輸出

select  t1.ProductName,t2.Name,t3.OrderId as orderid  from [ProductData] t1  with(nolock)   Inner join Member t2   with(nolock) on (t1.UserId=t2.Id)  Inner join OrderProduct t3   with(nolock) on (t1.UserId=t3.UserId)  where (t1.Id>@parame0)  order by  t1.BarCode desc
[parame0]:[0]

 

  • 對於一個查詢,只需調用query.Page()方法就能輕易實現分頁
  • 如果需要把動態類型結果轉換成定義好的對象,只需調用query.ToList<ClassName>()方法
  • 業務類中不再提供使用完整LambdaQuery作為參數的方法返回數據,而直接使用LambdaQuery的子方法返回數據,如上所示

關於分頁

  • 設定分頁參數后,CRL會生成對應數據庫分頁語法,如MSSQL采用ROWNUM分頁
  • 分頁默認是編譯為存儲過程,如果數據庫不支持自動編譯,由以語句的形式查詢
  • 默認分頁和Group分頁為兩種處理方式

 

更新2:表結構強制檢查

關於數據表創建緩存依賴

  1. CRL會自動創建對象對應的表結構,並初始
  2. CRL判斷該不該創建對象對應的數據表,是由表緩存文件來判斷,緩存文件路徑/config/TableCache.config
  3. 系統運行時檢查目錄內有沒有緩存,有則加載,沒有則創建,在CRL內部檢測表創建方法內,根據此緩存來判斷,不存在表緩存,則創建表並保存緩存值
  4. 新增的對象,此時緩存內是沒有的,則會創建數據表
  5. 在緩存結構和數據表結構一致的情況下,新增對象屬性時,會自動創建數據表列
  6. 緩存文件和數據庫實際表結構在一些情況下可能不一致,則需要手動干預
  • 當數據庫有表,但字段不全,也沒有緩存文件,CRL創建緩存文件時會按對象完整的結構緩存,此時緩存的表結構和數據庫結構就不一致
  • 當緩存內有表,數據庫沒有表,CRL沒法判斷,不會自動創建表
  • 當緩存內表有所有表字段結構,數據庫表字段被手動刪掉,此時結構不一致
  • 當緩存文件被其它數據源生成的緩存文件覆蓋了,可能產生結構不一致

由於以上問題,可能會導至找不到字段或表的錯誤,當前版本作了完善,於是在后台單獨開啟了一個線程,用以檢查表結構,在對象被首次訪問后,就會添加到結構檢查隊列,由后台異步進行處理

在程序運行后,每個被訪問過的對象會被檢查一次結構,達到結構同步的目的

 

3.0增新的演示項目CRLShoppingDemo

之前有人提到看不懂文檔,希望有簡單的例子,如你所願,按基本在線銷售系統寫了一個Demo,以下為運行效果

此演示項目正如上面所說,采用了精簡的寫法實現了這些業務

 

繼承業務封裝,實現會員管理

 public class MemberManage : CRL.Package.Person.PersonBusiness<MemberManage, Member>

商家和會員就都有了登錄和驗證方法,並實現Form認證

public ActionResult Login(Model.Member member)
        {
            string error; var a = MemberManage.Instance.CheckPass(member.AccountNo, member.PassWord, out error); if (!a) { ModelState.AddModelError("", error); return View(); } var u = MemberManage.Instance.QueryItem(b => b.AccountNo == member.AccountNo); if (u.Locked) { ModelState.AddModelError("", "賬號已鎖定"); return View(); } MemberManage.Instance.Login(u, "Member", false); string returnUrl = Request["returnUrl"]; if (string.IsNullOrEmpty(returnUrl)) { returnUrl = "/"; } return Redirect(returnUrl); }

 頁面上需要取當前用戶,通過Form認證返回統一的Person對象

var user = MemberManage.Instance.CurrentUser;//CRL.Package.Person.Person

 

類似FindOne,FindList,分頁方法不用去傻傻的寫很多次了(一般三層結構會把這類方法寫N次),已經自動轉換好了

var item = ProductManage.Instance.QueryItemFromCache(b => b.Id == id);//從緩存查詢
var item = ProductManage.Instance.QueryItem(b => b.Id == id);//從數據庫查找

//分頁

var query = ProductManage.Instance.GetLambdaQuery();
query.Where(b => b.SupplierId == CurrentUser.Id);
int count;
var result = ProductManage.Instance.Page(query, out count);

 
        

 

內置賬戶交易系統,解決多種類型支付管理問題

可以給N種角色設置N種貨幣類型,統一進行管理

比如以下是充值現金到會員

public bool Charge(Member member, decimal amount, string remark, TransactionType transactionType, out string error)
        {
            var account = Transaction.AccountManage.Instance.GetAccountId(member.Id, Model.AccountType.會員, transactionType); string orderId = DateTime.Now.ToString("yyMMddhhmmssff"); int tradeType = 10001; var trans = new List<CRL.Package.Account.Transaction>(); var ts = new CRL.Package.Account.Transaction() { AccountId = account, Amount = amount, OperateType = CRL.Package.Account.OperateType.收入, TradeType = tradeType, OutOrderId = orderId, Remark = remark }; trans.Add(ts); bool b = Transaction.TransactionManage.Instance.SubmitTransaction(out error,true, trans.ToArray());//提交流水 return b; }

確認訂單時扣款,同時贈送積分

var accountUser = Transaction.AccountManage.Instance.GetAccount(order.UserId, Model.AccountType.會員, Model.TransactionType.現金);
            var accountUser2 = Transaction.AccountManage.Instance.GetAccount(order.UserId, Model.AccountType.會員, Model.TransactionType.積分); if (accountUser.AvailableBalance < order.TotalAmount) { error = "賬戶余額不足"; return false; } int tradeType = 1001; var amount = order.TotalAmount; var orderId = order.OrderId; var remark = "訂單支付"; var trans = new List<CRL.Package.Account.Transaction>(); //生成會員交易流水 var ts = new CRL.Package.Account.Transaction() { AccountId = accountUser.Id, Amount = amount, OperateType = CRL.Package.Account.OperateType.支出, TradeType = tradeType, OutOrderId = orderId, Remark = remark }; trans.Add(ts); //贈送積分 var ts3 = new CRL.Package.Account.Transaction() { AccountId = accountUser2.Id, Amount = amount, OperateType = CRL.Package.Account.OperateType.收入, TradeType = tradeType, OutOrderId = orderId, Remark = "贈送積分" }; trans.Add(ts3); bool b = Transaction.TransactionManage.Instance.SubmitTransaction(out error, trans.ToArray());//提交流水 if(!b) { return false; }

更多詳細寫法請對照示例和開發文檔

增加了測試類WebTest.Code.TestAll.cs

此類演示了所有查詢方式,在調用通過的情況下,達到集成測試的結果

public class TestAll
    {
        public static void TestQuery()
        {
            var instance = Code.ProductDataManage.Instance;
            var query = ProductDataManage.Instance.GetLambdaQuery();
            query.Select(b => new { b.InterFaceUser, bb = b.Id * b.Number
                });
            var year = DateTime.Now.Year;
            query.Where(b => b.Year == year);//虛擬字段
            #region 擴展方法
            query.Where(b => 0 < b.Id);//不再區分左邊右邊了
            query.Where(b => b.Id < b.Number);//直接比較可以解析通過
            query.Where(b => b.ProductName.Contains("122"));//包含字符串
            query.Where(b => !b.ProductName.Contains("122"));//不包含字符串
            query.Where(b => b.ProductName.In("111", "222"));//string in
            query.Where(b => b.AddTime.Between(DateTime.Now, DateTime.Now));//在時間段內
            query.Where(b => b.AddTime.DateDiff(DatePart.dd, DateTime.Now) > 1);//時間比較
            query.Where(b => b.ProductName.Substring(0, 3) == "222");//截取字符串
            query.Where(b => b.Id.In(1, 2, 3));//in
            query.Where(b => !b.Id.In(1, 2, 3));//not in
            query.Where(b => b.UserId.Equals(Code.ProductChannel.其它));//按值等於,enum等於int
            query.Where(b => b.ProductName.StartsWith("abc"));//開頭值判斷
            query.Where(b => b.Id.Between(1, 10));//數字區間
            query.Where(b => b.ProductName.Like("123"));// %like%
            query.Where(b => b.ProductName.LikeLeft("123"));// %like
            query.Where(b => b.ProductName.LikeRight("123"));// like%
            query.Page(2, 1);
            var sql1 = query.PrintQuery();
            var list = query.ToDynamic();
            #endregion

            #region 關聯
            //索引值
            query = ProductDataManage.Instance.GetLambdaQuery();
            query.Join<Code.Member>((a, b) => a.UserId == b.Id && a.BarCode.Contains("1"))
                .SelectAppendValue(b => b.Mobile).OrderBy(b => b.Id, true);
            var list2 = query.ToList();
            foreach (var item in list2)
            {
                var mobile = item["Mobile"];
            }
            //按篩選值
            query = ProductDataManage.Instance.GetLambdaQuery();
            query.Join<Code.Member>((a, b) => a.UserId == b.Id && a.BarCode.Contains("1"))
                .Select((a, b) => new { a.BarCode, b.Name }).Join<Code.Order>((a, b) => a.Id == b.UserId);
            query.OrderBy(b => b.Id);
            var list3 = query.ToDynamic();
            foreach (var item in list3)
            {
                var barCode = item.BarCode;
            }
            //關聯再關聯
            query = ProductDataManage.Instance.GetLambdaQuery();
            query.Join<Code.Member>((a, b) => a.UserId == b.Id && a.BarCode.Contains("1"))
                .SelectAppendValue(b => b.Mobile).OrderBy(b => b.Id, true).Join<Code.Order>((a, b) => a.Id == b.UserId);
            //按IN查詢
            query = ProductDataManage.Instance.GetLambdaQuery();
            query.In<Code.Member>(b => b.UserId, b => b.Id, (a, b) => a.SupplierId == "10" && b.Name == "123");
            var sql2 = query.PrintQuery();
            #endregion

            #region GROUP
            query = Code.ProductDataManage.Instance.GetLambdaQuery();
            query.Where(b => b.Id > 0);
            //選擇GROUP字段
            query.Select(b => new
            {
                sum2 = b.SUM(x => x.Number * x.Id),//等效為 sum(Number*Id) as sum2
                total = b.BarCode.COUNT(),//等效為count(BarCode) as total
                sum11 = b.Number.SUM(),//等效為sum(Number) as sum1
                b.ProductName,
                num1 = b.SUM(x => x.Number * x.Id),
                num2 = b.MAX(x => x.Number * x.Id),
                num3 = b.MIN(x => x.Number * x.Id),
                num4 = b.AVG(x => x.Number * x.Id) 
            });
            //GROUP條件
            query.GroupBy(b => new { b.ProductName });
            //having
            query.GroupHaving(b => b.Number.SUM() >= 0);
            //設置排序
            query.OrderBy(b => b.BarCode.Count(), true);//等效為 order by count(BarCode) desc
            var list4 = query.ToDynamic();
            foreach(var item in list4)
            {
                var total = item.total;
            }
            #endregion

            #region DISTINCT
            query = Code.ProductDataManage.Instance.GetLambdaQuery();
            query.Where(b => b.Id > 0);
            query.DistinctBy(b => new { b.ProductName });
            query.DistinctCount();//表示count Distinct 結果名為Total
            var list5 = query.ToDynamic();
            foreach (var item in list5)
            {
                var total = item.Total;
                //var name = item.ProductName;
            }
            #endregion

            #region 函數
            //按條件id>0,合計Number列
            var sum = instance.Sum(b => b.Id > 0, b => b.Number * b.UserId);
            //按條件id>0,進行總計
            var count = instance.Count(b => b.Id > 0);
            var max = instance.Max(b => b.Id > 0, b => b.Id);
            var min = instance.Min(b => b.Id > 0, b => b.Id);
            //使用語句進行函數查詢
            query = ProductDataManage.Instance.GetLambdaQuery();
            query.Select(b => b.Number.SUM());
            decimal sum2 = query.ToScalar();
            #endregion
        }
        public static void TestUpdate()
        {
            var instance = Code.ProductDataManage.Instance;
            #region 更新
            //要更新屬性集合
            CRL.ParameCollection c = new CRL.ParameCollection();
            c["ProductName"] = "product1";
            Code.ProductDataManage.Instance.Update(b => b.Id == 4, c);
            //按對象差異更新
            var p = new Code.ProductData() { Id = 4 };
            //手動修改值時,指定修改屬性以在Update時識別,分以下幾種形式
            p.Change(b => b.BarCode);//表示值被更改了
            p.Change(b => b.BarCode, "123");//通過參數賦值
            p.Change(b => b.BarCode == "123");//通過表達式賦值
            Code.ProductDataManage.Instance.Update(b => b.Id == 4, p);//指定查詢更新

            p = Code.ProductDataManage.Instance.QueryItem(b => b.Id > 0);
            p.UserId += 1;
            Code.ProductDataManage.Instance.Update(p);//按主鍵更新,主鍵值是必須的
            #endregion

            #region 緩存更新
            var item = Code.ProductDataManage.Instance.QueryItemFromCache(1);
            var guid = Guid.NewGuid().ToString().Substring(0,8);
            item.Change(b => b.SupplierName, guid);
            Code.ProductDataManage.Instance.Update(item);
            item = Code.ProductDataManage.Instance.QueryItemFromCache(1);
            var item2 = Code.ProductDataManage.Instance.QueryItem(1);
            var a = item.SupplierName == item2.SupplierName && item.SupplierName == guid;
            if (!a)
            {
                throw new Exception("更新緩存失敗");
            }
            #endregion

            #region 事務
            string error;
            item = Code.ProductDataManage.Instance.QueryItem(1);

            var result = Code.ProductDataManage.Instance.PackageTrans((out string ex) =>
            {
                ex = "";
                var product = new ProductData();
                product.BarCode = "sdfsdf";
                product.Number = 10;
                ProductDataManage.Instance.Add(product);
                return false;
            }, out error);
            if (result)
            {
                throw new Exception("事務未回滾");
            }
            #endregion
        }
    }
View Code

 

項目開源

項目開源地址:https://github.com/hubro-xx/CRL3

以后所有升級更新都會基於此版本庫,若重大結構變更則建新的項目

需要交流可入群,群內也不再單獨上傳源碼包,最新源碼更新此庫即可

 

源碼結構:

CRL3.1=>

  CRLShoppingDemo------------->在線購物示例項目

  Core.Mvc------------------------->MVC簡單封裝

  CRL------------------------------->CRL主框架

  CRL.Package--------------------->CRL業務封裝

  RoleControl---------------------->基於CRL實現的通用權限系統

  WebTest------------------------->開發&測試文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM