程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 簡化基於數據庫的DotNet應用程序開發

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

編輯:關於.NET

分析

要做一個基於數據庫的應用程序,我們有大量的重復勞動要去做,建表,寫增 刪改查的SQL語句,寫與數據庫表對應的實體類,寫執行SQL的c#代碼,寫添加、 修改、列表、詳細頁面等等。這些活動都是圍繞著一個個都數據表來開展的, 在.NET領域有很多的OR Mapping的方案,但好多方案用起來好用,但原理很復雜 ,而且性能也不好把握,所以我們可以做一個輕型的ORM方案。有了ORM框架,根 據數據表寫c#實體類這些勞動,其實也可以寫一個代碼生成器來幫我們生成,甚 至代碼生成器還能幫我們生成一些界面的代碼。我們大概需要解決如下問題

1、我們要有一個通用的數據庫操作幫助類,類似微軟的DAAB,但最好能支持 多種數據庫;

2、我們要有一個使用簡單的orm框架,能方便的用c#代碼來進行數據庫存取操 作,而且要盡量保證性能,比如使用參數化查詢;

3、我們要有一個代碼生成器幫助我們解決一些重復性勞動,比如生成實體類 ,生成調用存儲過程的c#代碼等;

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

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

ADO.NET 2.0為我們訪問數據庫提供了一套與具體數據庫無關的模型,其核心 類是DbProviderFactory,它遵循了Provider模式,就是把對各種數據庫的操作抽 象出一個Provider,再由各種數據庫去寫與具體數據庫相關的Provider,然後通 過配置在運行時方便的切換數據庫,而盡量少的不修改業務邏輯層的代碼,業務 邏輯層依賴的是抽象的Provider。這也是典型的依賴倒置,就是說業務邏輯說我 需要哪些接口,我依賴這些接口,而讓別人去實現這些接口,在運行的時候再去 加載調用實現這些接口的具體類。

為了提高性能,減少SQLSERVER執行計劃的重編譯,我們盡量使用參數化的查 詢,而一個固定的語句或者存儲過程它的ADO.NET參數是固定的,所以我們可以把 這些參數緩存起來,避免每次執行SQL語句都創新新的參數對象。另外oledb的 ado.net provider的參數是不能命名的,所以給參數賦值要按順序賦值。

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

public System.Data.DataSet SqlExecuteDateSet(string sql, string[] 

paramters, params object[] values)

public System.Data.DataTable SqlExecuteDateTable(string sql, string[] 

paramters, params object[] values)

public int SqlExecuteNonQuery(string sql, string[] paramters, params 

object[] values)

public System.Data.Common.DbDataReader 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

    {

        dbhelper.SqlExecuteNonQuery(trans, sql, new string[] { 

"@id" }, 1);

        dbhelper.SqlExecuteNonQuery(trans, sql, new string[] { 

"@id" }, 2);

        trans.Commit();

        OutPut("ok");

    }

    catch (Exception)

    {

        trans.RollBack();

        OutPut("no ok");

    }

}

二、通用的ORM框架

先看如下的代碼

//1、添加

xxxCase xxxCase = new xxxCase();

xxxCase.Title = "abc";

xxxCase.Content = "呵呵";

xxxCase.CaseFrom = CaseFrom.客服投訴;

xxxCase.PostUser = "huhao";

xxxCase.CreateTime = DateTime.Now;

xxxCase.CaseType = CaseType.生產環境查詢;

xxxCase.Priority = CasePriority.中;

xxxCase.ReleationServices = "aaa,bbb";

xxxCase.ReleationClient = "ccc,ddd";

EntityBase.Insert(xxxCase);

//2、修改

xxxCase.ClearInnerData();

xxxCase.CaseId = 1;

xxxCase.Title = "嘿嘿";

EntityBase.Update(xxxCase);

//3、刪除

xxxCase.ClearInnerData();

xxxCase.CaseId = 1;

EntityBase.Delete(xxxCase);

//4、復雜條件查詢,查詢大於昨天的客服投訴或者wawa關閉的問題

WhereCondition condition = new WhereCondition(

    xxxCase.CaseFromColName,SqlOperator.Equal, (short)CaseFrom.客服

投訴)

    .And(

    new WhereCondition(xxxCase.CreateTimeColName, 

SqlOperator.GreaterThan ,

        DateTime.Now.AddDays(-1)))

    .Group()

    .Or(

    new WhereCondition(xxxCase.CloseUserColName, SqlOperator.Equal, 

"wawa"));

IList<xxxCase> list = EntityBase.Select<xxxCase>(

    new string[] {"Title", "PostUser"}, condition);

foreach (xxxCase item in list)

{

    Console.WriteLine("{0}-{1}",item.Title,item.PostUser);

}

Console.ReadKey();

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

我們看一下EntityBase.Insert方法的實現,邏輯很簡單明了,其他的 Update,Delete,Select也是類似的思路。

private static DbHelper _db = new DbHelper();
public static void Insert(EntityBase entity) {
string sql = GetInsertSql (entity);
string[] parameters = GetParameters (entity.InnerData);
object[] parameterValues = GetParameterValuess(entity.InnerData);
_db.SqlExecuteNonQuery (sql, parameters, parameterValues);
}
private static string GetInsertSql(EntityBase entity) {
int len = entity.InnerData.Count;
StringBuilder sql = new StringBuilder ();
sql.AppendFormat("INSERT INTO [{0}]\r\n", entity.TableName);
sql.Append("(\r\n");
for (int i = 0; i < len; i++) {
if (i != len - 1)
sql.AppendFormat("[{0}],", entity.InnerData[i].Key);
else
sql.AppendFormat("[{0}]", entity.InnerData[i].Key);
}
sql.Append(") \r\n");
sql.Append("VALUES(\r\n");
for (int i = 0; i < len; i++) {
if (i != len - 1)
sql.AppendFormat("@{0},", entity.InnerData[i].Key);
else
sql.AppendFormat("@{0}", entity.InnerData[i].Key);
}
sql.Append(") \r\n");
return sql.ToString();
}
private static string[] GetParameters(IList<DbCommonClass<string, object>> items) {
int len = items.Count;
List<string> parameters = new List<string>();
for (int i = 0; i < len; i++) {
parameters.Add(string.Format("@{0}", items[i].Key));
}
return parameters.ToArray();
}
private static object[] GetParameterValuess (List<DbCommonClass<string, object>> items) {
int len = items.Count;
List<object> parameters = new List<object>();
for (int i = 0; i < len; i++) {
parameters.Add(items[i].Value);
}
return parameters.ToArray();
}

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

三、代碼生成器

ADO.NET的各種數據庫實現都有獲取某個數據庫Schema的API,其中最重要的是 SqlConnection.GetSchema(SqlClientMetaDataCollectionNames.Tables)和 SqlCommand.ExecuteReader( CommandBehavior.KeyInfo | CommandBehavior.CloseConnection)方法,有了這兩個方法,我們可以枚舉一個 數據庫的所有表,及某個表的所有字段,及每個字段的類型,長度、可否為空, 是否為主鍵,是否為標識列等信息,有了這些元數據,我們再根據一個模板就可 以生成特定格式的代碼了。而且我們需要新增加一種代碼生成的格式的話,只需 添加一個模板就可以了,這樣的代碼生成器還有擴展性,而不是一個寫死的針對 特定框架的代碼生成器。

為了脫離對特定數據庫的依賴,我們建立一個代碼生成器的元數據模型,如下

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 System.Type CSharpType;

 public string CSharpTypeStr;

 

 public bool UiAllowEmpty;

 public bool UiIsShowOn;

 public long? UiMaxCheck;

 public long? UiMinCheck;

 public string UiRegxCheck;

}

得到元數據後,剩下的就是讀取模板,然後替換字符串了,比如實體類的模板 ,如下

using System;

using System.Collections.Generic;

using WawaSoft.Common;

namespace $model.namespace$ {

    public class $model.classname$ : EntityBase {

$foreach.prop$

        public const string $prop.property$ColName = 

"$prop.dbcolname$";

$endforeach$    

        private static readonly List<string> _Cols = new 

List<string>();

        static $model.classname$()

        {            

$foreach.prop$

            _Cols.Add($prop.property$ColName);

$endforeach$            

        }

        public $model.classname$() {

            _tableName = "$model.tablename$";

            _PkName = "$model.pkcolname$";         

   

        }

$foreach.prop$

        private $prop.csharptype$ $prop.property2$;

$endforeach$

 

$foreach.prop$

        public $prop.csharptype$ $prop.property$ {

            get { return $prop.property2$; }

            set {

                $prop.property2$ = value;

                AddInnerData("$prop.property2$", 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 (item.Key) {

$foreach.prop$

                    case $prop.property$ColName:

                        $prop.property2$ = 

($prop.csharptype$)item.Value;

                        break;

$endforeach$

                }

            }

        }

    }

}

生成的實體類,如下

using System;

using System.Collections.Generic;

using WawaSoft.Common;

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() {

            _Cols.Add(UserIdColName);

            _Cols.Add(UsernameColName);

            _Cols.Add(NameColName);

            _Cols.Add(PasswordColName);

            _Cols.Add(CreateTimeColName);

            _Cols.Add(IsAdminColName);

        }

        public User() {

            _tableName = "User";

            _PkName = "UserId";

        }

        private Nullable<Int32> userid;

        private String username;

        private String name;

        private String password;

        private Nullable<DateTime> createtime;

        private Nullable<Boolean> isadmin;

        public Nullable<Int32> 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 (item.Key) {

                    case UserIdColName:

                        userid = 

(Nullable<Int32>)item.Value;

                        break;

                    case UsernameColName:

                        username = (String)

item.Value;

                        break;

                    case NameColName:

                        name = (String)

item.Value;

                        break;

                    case PasswordColName:

                        password = (String)

item.Value;

                        break;

                    case CreateTimeColName:

                        if (item.Value != 

DBNull.Value)

                            createtime = 

(Nullable<DateTime>)item.Value;

                        break;

                    case IsAdminColName:

                        if (item.Value != 

DBNull.Value)

                            isadmin = 

(Nullable<Boolean>)item.Value;

                        break;

                }

            }

        }

    }

}

小結

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved