熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

簡化基於數據庫的DotNet應用程序開發

2013-11-13 09:43:27  來源: .NET編程 

  分析

  要做一個基於數據庫的應用程序我們有大量的重復勞動要去做建表寫增刪改查的SQL語句寫與數據庫表對應的實體類寫執行SQL的c#代碼寫添加修改列表詳細頁面等等這些活動都是圍繞著一個個都數據表來開展的NET領域有很多的OR Mapping的方案但好多方案用起來好用但原理很復雜而且性能也不好把握所以我們可以做一個輕型的ORM方案有了ORM框架根據數據表寫c#實體類這些勞動其實也可以寫一個代碼生成器來幫我們生成甚至代碼生成器還能幫我們生成一些界面的代碼我們大概需要解決如下問題
我們要有一個通用的數據庫操作幫助類類似微軟的DAAB但最好能支持多種數據庫
我們要有一個使用簡單的orm框架能方便的用c#代碼來進行數據庫存取操作而且要盡量保證性能比如使用參數化查詢
我們要有一個代碼生成器幫助我們解決一些重復性勞動比如生成實體類生成調用存儲過程的c#代碼等

  圍繞這個問題我們一一來展開

  一通用的數據庫吃操作幫助類

  ADONET 為我們訪問數據庫提供了一套與具體數據庫無關的模型其核心類是DbProviderFactory它遵循了Provider模式就是把對各種數據庫的操作抽象出一個Provider再由各種數據庫去寫與具體數據庫相關的Provider然後通過配置在運行時方便的切換數據庫而盡量少的不修改業務邏輯層的代碼業務邏輯層依賴的是抽象的Provider這也是典型的依賴倒置就是說業務邏輯說我需要哪些接口我依賴這些接口而讓別人去實現這些接口在運行的時候再去加載調用實現這些接口的具體類
  為了提高性能減少SQLSERVER執行計劃的重編譯我們盡量使用參數化的查詢而一個固定的語句或者存儲過程它的ADONET參數是固定的所以我們可以把這些參數緩存起來避免每次執行SQL語句都創新新的參數對象另外oledb的 provider的參數是不能命名的所以給參數賦值要按順序賦值

  為了使用方便我們為執行SQL語句提供如下的API
 

  public SystemDataDataSet SqlExecuteDateSet(string sql string[] paramters params object[] values)
public SystemDataDataTable SqlExecuteDateTable(string sql string[] paramters params object[] values)
public int SqlExecuteNonQuery(string sql string[] paramters params object[] values)
public SystemDataCommonDbDataReader SqlExecuteReader(string sql string[] paramters params object[] values)
public object SqlExecuteScalar(string sql string[] paramters params object[] values)

  當然為了支持存儲過程的執行以及數據庫事務還需要提供相關的重載的API大概的使用示例(面向SQLSERVER)如下
 

  DbHelper dbhelper = new DbHelper();
string sql = delete from Citys where CityId = @id;
using (DatabaseTrans trans = new DatabaseTrans(dbhelper))
{
    try
    {
        dbhelperSqlExecuteNonQuery(trans sql new string[] { @id } );
        dbhelperSqlExecuteNonQuery(trans sql new string[] { @id } );
        transCommit();
        OutPut(ok);
    }
    catch (Exception)
    {
        transRollBack();
        OutPut(no ok);
    }
}

  二通用的ORM框架

  先看如下的代碼

  //添加
xxxCase xxxCase = new xxxCase();
xxxCaseTitle = abc;
xxxCaseContent = 呵呵;
xxxCaseCaseFrom = CaseFrom客服投訴;
xxxCasePostUser = huhao;
xxxCaseCreateTime = DateTimeNow;
xxxCaseCaseType = CaseType生產環境查詢;
xxxCasePriority = CasePriority中;
xxxCaseReleationServices = aaabbb;
xxxCaseReleationClient = cccddd;
EntityBaseInsert(xxxCase);

  //修改
xxxCaseClearInnerData();
xxxCaseCaseId = ;
xxxCaseTitle = 嘿嘿;
EntityBaseUpdate(xxxCase);

  //刪除
xxxCaseClearInnerData();
xxxCaseCaseId = ;
EntityBaseDelete(xxxCase);

  //復雜條件查詢查詢大於昨天的客服投訴或者wawa關閉的問題
WhereCondition condition = new WhereCondition(
    xxxCaseCaseFromColNameSqlOperatorEqual (short)CaseFrom客服投訴)
    And(
    new WhereCondition(xxxCaseCreateTimeColName SqlOperatorGreaterThan
        DateTimeNowAddDays()))
    Group()
    Or(
    new WhereCondition(xxxCaseCloseUserColName SqlOperatorEqual wawa));

  IList<xxxCase> list = EntityBaseSelect<xxxCase>(
    new string[] {Title PostUser} condition);

  foreach (xxxCase item in list)
{
    ConsoleWriteLine({}{}itemTitleitemPostUser);
}
ConsoleReadKey();

  上面的代碼是以面向對象(請忽略那些關於貧血模型的討論說上面的代碼不夠OO上面的代碼至少相對的面向對象而且看起來很直觀)的方式去執行一些業務這應該比到處寫SQL語句要強很多吧而且如果這些操作內部使用的仍然是參數化查詢而不是拼sql字符串的話

  性能也不會很差(請忽略具體語句是否能使用索引的討論那得具體分析)

  我們看一下EntityBaseInsert方法的實現邏輯很簡單明了其他的UpdateDeleteSelect也是類似的思路

  private static DbHelper _db = new DbHelper();
public static void Insert(EntityBase entity) {
    string sql = GetInsertSql(entity);
    string[] parameters = GetParameters(entityInnerData);
    object[] parameterValues = GetParameterValuess(entityInnerData);
    _dbSqlExecuteNonQuery(sql parameters parameterValues);
}
private static string GetInsertSql(EntityBase entity) {
    int len = entityInnerDataCount;
    StringBuilder sql = new StringBuilder();
    sqlAppendFormat(INSERT INTO [{}]\r\n entityTableName);
    sqlAppend((\r\n);
    for (int i = ; i < len; i++) {
        if (i != len )
            sqlAppendFormat([{}] entityInnerData[i]Key);
        else
            sqlAppendFormat([{}] entityInnerData[i]Key);
    }
    sqlAppend()\r\n);
    sqlAppend(VALUES(\r\n);
    for (int i = ; i < len; i++) {
        if (i != len )
            sqlAppendFormat(@{} entityInnerData[i]Key);
        else
            sqlAppendFormat(@{} entityInnerData[i]Key);
    }
    sqlAppend()\r\n);
    return sqlToString();
}
private static string[] GetParameters(IList<DbCommonClass<string object>> items) {
    int len = itemsCount;
    List<string> parameters = new List<string>();
    for (int i = ; i < len; i++) {
        parametersAdd(stringFormat(@{} items[i]Key));
    }
    return parametersToArray();
}
private static object[] GetParameterValuess(List<DbCommonClass<string object>> items) {
    int len = itemsCount;
    List<object> parameters = new List<object>();
    for (int i = ; i < len; i++) {
        parametersAdd(items[i]Value);
    }
    return parametersToArray();
}

  當然Select方法稍微復雜一些因為我們要考慮復雜的Where字句Top字句OrderBy字句等我們為Where字句建立了一個WhereCondition對象來方便的用c#代碼來描述SQL的where語句但是為了實現簡單我們不去實現表連接復雜的子語句等支持(我個人認為向NBear等框架做的過於強大了)

  三代碼生成器

  ADONET的各種數據庫實現都有獲取某個數據庫Schema的API其中最重要的是SqlConnectionGetSchema(SqlClientMetaDataCollectionNamesTables)和SqlCommandExecuteReader( CommandBehaviorKeyInfo | CommandBehaviorCloseConnection)方法有了這兩個方法我們可以枚舉一個數據庫的所有表及某個表的所有字段及每個字段的類型長度可否為空是否為主鍵是否為標識列等信息有了這些元數據我們再根據一個模板就可以生成特定格式的代碼了而且我們需要新增加一種代碼生成的格式的話只需添加一個模板就可以了這樣的代碼生成器還有擴展性而不是一個寫死的針對特定框架的代碼生成器
  為了脫離對特定數據庫的依賴我們建立一個代碼生成器的元數據模型如下
 

  public class CodeModel
{
 public string ClassName;
 public string TableName;
 public string Descript;
 public string Namespace;
 public string PkColName;
 public List<CodeProperty> Properties;
}
public class CodeProperty
{
 public string DbColName;
 public int? DbLength;
 public bool DbAllowNull
 public SqlDbType DbType;
 public string DbTypeStr;
 public bool DbIsIdentity;
 public bool DbIsPk;
 
 public string Descript;
 public string PropertyName;
 public SystemType CSharpType;
 public string CSharpTypeStr;
 
 public bool UiAllowEmpty;
 public bool UiIsShowOn;
 public long? UiMaxCheck;
 public long? UiMinCheck;
 public string UiRegxCheck;
}

  得到元數據後剩下的就是讀取模板然後替換字符串了

  比如實體類的模板如下
 

  using System;
using SystemCollectionsGeneric;
using WawaSoftCommon;

  namespace $modelnamespace$ {
    public class $modelclassname$ : EntityBase {
$foreachprop$
        public const string $propproperty$ColName = $propdbcolname$;
$endforeach$   
        private static readonly List<string> _Cols = new List<string>();

  static $modelclassname$()
        {           
$foreachprop$
            _ColsAdd($propproperty$ColName);
$endforeach$           

  }

  public $modelclassname$() {
            _tableName = $modeltablename$;
            _PkName = $modelpkcolname$;           
        }

  $foreachprop$
        private $propcsharptype$ $propproperty$;
$endforeach$
 
$foreachprop$
        public $propcsharptype$ $propproperty$ {
            get { return $propproperty$; }
            set {
                $propproperty$ = value;
                AddInnerData($propproperty$ value);
            }
        }
$endforeach$
        protected override IList<string> Cols
        {
            get { return _Cols; }
        }

  public override void ConvertToEntity(IEnumerable<DbCommonClass<string object>> items) {
            foreach (DbCommonClass<string object> item in items) {
                switch (itemKey) {
$foreachprop$
                    case $propproperty$ColName:
                        $propproperty$ = ($propcsharptype$)itemValue;
                        break;
$endforeach$
                }
            }
        }
    }
}

  生成的實體類如下
 

  using System;
using SystemCollectionsGeneric;
using WawaSoftCommon;

  namespace Entities {
    public class User : EntityBase {
        public const string UserIdColName = UserId;
        public const string UsernameColName = Username;
        public const string NameColName = Name;
        public const string PasswordColName = Password;
        public const string CreateTimeColName = CreateTime;
        public const string IsAdminColName = IsAdmin;
        private static readonly List<string> _Cols = new List<string>();

  static User() {
            _ColsAdd(UserIdColName);
            _ColsAdd(UsernameColName);
            _ColsAdd(NameColName);
            _ColsAdd(PasswordColName);
            _ColsAdd(CreateTimeColName);
            _ColsAdd(IsAdminColName);

  }

  public User() {
            _tableName = User;
            _PkName = UserId;
        }

  private Nullable<Int> userid;
        private String username;
        private String name;
        private String password;
        private Nullable<DateTime> createtime;
        private Nullable<Boolean> isadmin;

  public Nullable<Int> UserId {
            get { return userid; }
            set {
                userid = value;
                AddInnerData(userid value);
            }
        }
        public String Username {
            get { return username; }
            set {
                username = value;
                AddInnerData(username value);
            }
        }
        public String Name {
            get { return name; }
            set {
                name = value;
                AddInnerData(name value);
            }
        }
        public String Password {
            get { return password; }
            set {
                password = value;
                AddInnerData(password value);
            }
        }
        public Nullable<DateTime> CreateTime {
            get { return createtime; }
            set {
                createtime = value;
                AddInnerData(createtime value);
            }
        }
        public Nullable<Boolean> IsAdmin {
            get { return isadmin; }
            set {
                isadmin = value;
                AddInnerData(isadmin value);
            }
        }
        protected override IList<string> Cols {
            get { return _Cols; }
        }

  public override void ConvertToEntity(IEnumerable<DbCommonClass<string object>> items) {
            foreach (DbCommonClass<string object> item in items) {
                switch (itemKey) {
                    case UserIdColName:
                        userid = (Nullable<Int>)itemValue;
                        break;
                    case UsernameColName:
                        username = (String)itemValue;
                        break;
                    case NameColName:
                        name = (String)itemValue;
                        break;
                    case PasswordColName:
                        password = (String)itemValue;
                        break;
                    case CreateTimeColName:
                        if (itemValue != DBNullValue)
                            createtime = (Nullable<DateTime>)itemValue;
                        break;
                    case IsAdminColName:
                        if (itemValue != DBNullValue)
                            isadmin = (Nullable<Boolean>)itemValue;
                        break;
                }
            }
        }
    }
}

  小結

  解決了以上幾個問題再開發數據庫應用應該會提高不少效率


From:http://tw.wingwit.com/Article/program/net/201311/11415.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.