2012-10-30 50 views
4

過去幾天,我一直在使用各種數據庫,如MySQL,oracle,Ibmdb2等,它們通過odbc提供程序與點網絡連接。爲任何數據庫支持編寫通用類驅動程序

例如:

1)MySQL: 

Driver={MySQL ODBC 5.1 Driver};server=**********;uid=**;database=**;port=***;pwd=***;" 

2)oracle: 

Driver={Microsoft ODBC for Oracle};server=**********;uid=**;database=**;port=***;pwd=***;" 

3)Db2: 

Driver={IBM DB2 ODBC DRIVER};server=**********;uid=**;database=**;port=***;pwd=***;" 

現在我的問題是

是有可能寫通用類,使用任何數據庫提供 作爲

Driver={My own driver};server=**********;uid=**;database=**;port=***;pwd=***;" 

剛剛通過改變驅動每個數據庫連接名稱在web.config中,並將該dll文件放在我發佈的Web應用程序或網站項目的bin文件夾中。

+2

是的,你可以,但不是隻有驅動程序名稱,你將不得不將整個連接字符串放在web.config中,因爲連接字符串可能會因數據庫而異。然後你可以通過實現ISqlConnection,ISqlCommand接口爲db動作創建一個輔助類。 – nawfal

+1

@nawfal ya感謝你給予啓動,因爲我是新手,需要Google來查找它......如果你給我一些鏈接,以便開始 – GowthamanSS

+0

請看我更新的答案。 – nawfal

回答

8

推出自己的一個並不是什麼大不了的事情。這裏有一個基本的結構,我將如何實現它的最低需求(你當然可以擴展它):

1)首先創建一個接口,指定基本功能。

interface IDb 
{ 
    IEnumerable<T> Get<T>(string query, Action<IDbCommand> parameterizer, 
          Func<IDataRecord, T> selector); 

    int Add(string query, Action<IDbCommand> parameterizer); 

    int Save(string query, Action<IDbCommand> parameterizer); 

    int SaveSafely(string query, Action<IDbCommand> parameterizer); 

} 

2)創建通用助手類,它不僅要實現該接口,但也應該是類型IDbConnection指定。該類應該更好(不一定)可實例化(非靜態),以便您可以傳遞所需的連接字符串來實例化它。

這裏是一個完全懶惰實現:

using System; 
using System.Data; 
using System.Collections.Generic; 
using System.Linq; 

public class Db<T> : IDb where T : IDbConnection, new() 
{ 
    string connectionString; 

    public Db(string connectionString) 
    { 
     this.connectionString = connectionString; 
    } 

    IEnumerable<S> Do<R, S>(string query, Action<IDbCommand> parameterizer, 
          Func<IDbCommand, IEnumerable<R>> actor, Func<R, S> selector) 
    { 
     using (var conn = new T()) 
     { 
      using (var cmd = conn.CreateCommand()) 
      { 
       if (parameterizer != null) 
        parameterizer(cmd); 
       cmd.CommandText = query; 
       cmd.Connection.ConnectionString = connectionString; 

       cmd.Connection.Open(); 

       foreach (var item in actor(cmd)) 
        yield return selector(item); 
      } 
     } 
    } 

    public IEnumerable<S> Get<S>(string query, Action<IDbCommand> parameterizer, Func<IDataRecord, S> selector) 
    { 
     return Do(query, parameterizer, ExecuteReader, selector); 
    } 

    static IEnumerable<IDataRecord> ExecuteReader(IDbCommand cmd) 
    { 
     using (var r = cmd.ExecuteReader(CommandBehavior.CloseConnection)) 
      while (r.Read()) 
       yield return r; 
    } 

    public int Add(string query, Action<IDbCommand> parameterizer) 
    { 
     return Do(query, parameterizer, ExecuteReader, r => Convert.ToInt32(r[0])).First(); 
    } 

    public int Save(string query, Action<IDbCommand> parameterizer) 
    { 
     return Do(query, parameterizer, ExecuteNonQuery, noAffected => noAffected).First(); 
    } 

    static IEnumerable<int> ExecuteNonQuery(IDbCommand cmd) 
    { 
     yield return cmd.ExecuteNonQuery(); 
    } 

    public int SaveSafely(string query, Action<IDbCommand> parameterizer) 
    { 
     // 'using' clause ensures rollback is called, so no need to explicitly rollback 
     return Do(query, parameterizer, cmd => 
     { 
      using (cmd.Transaction = cmd.Connection.BeginTransaction()) 
      { 
       var noAffected = ExecuteNonQuery(cmd); 
       cmd.Transaction.Commit(); 
       return noAffected; 
      } 
     }, noAffected => noAffected).First(); 
    } 
} 

此只做基本的ExecuteNonQueryExecuteReader一樣操作,簡單Transaction秒。沒有存儲過程。 Add函數用於插入和檢索最後插入的id和likes。我非常生氣,懶得使用一個核心執行函數Do(這被稱爲各種數據庫操作),這就是爲什麼Do看起來很複雜,但它非常乾燥。理想情況下,它更好地分開。你也可以去掉Linq

3)最後提供靜態包裝Db與各地實例化Db類沒有泛型約束,這樣你就不必每次保持做一個數據庫查詢時間傳遞T參數。比如像這樣:

public static class Db 
{ 
    static IDb db = GetDbInstance(); 

    static IDb GetDbInstance() 
    { 
     // get these two from config file or somewhere 
     var connectionString = GetConnectionString(); 
     var driver = GetDbType(); // your logic to decide which db is being used 

     // some sort of estimation of your db 
     if (driver == SQLite) 
      return new Db<SQLiteConnection>(connectionString); 
     else if (driver == MySQL) 
      return new Db<MySqlConnection>(connectionString); 
     else if (driver == JET) 
      return new Db<OleDbConnection>(connectionString); 
     //etc 

     return null; 
    } 

    public static void Parameterize(this IDbCommand command, string name, 
            object value) 
    { 
     var parameter = command.CreateParameter(); 
     parameter.ParameterName = name; 
     parameter.Value = value; 
     command.Parameters.Add(parameter); 
    } 

    public static IEnumerable<T> Get<T>(string query, 
             Action<IDbCommand> parameterizer, 
             Func<IDataRecord, T> selector) 
    { 
     return db.Get(query, parameterizer, selector); 
    } 

    public static int Add(string query, Action<IDbCommand> parameterizer) 
    { 
     return db.Add(query, parameterizer); 
    } 

    public static int Save(string query, Action<IDbCommand> parameterizer) 
    { 
     return db.Save(query, parameterizer); 
    } 

    public static int SaveSafely(string query, Action<IDbCommand> parameterizer) 
    { 
     return db.SaveSafely(query, parameterizer); 
    } 
} 

4)現在,我想創建一個額外的靜態函數GetDbInstance的地方,使其推斷正確的數據庫參數,如連接字符串,供應商類型等也有一個擴展方法來緩解查詢參數。我把它們放在上面的靜態Db類中,但這是你的選擇(有些人把它寫在Db類本身中,但我更喜歡它,因爲功能應該是你的應用程序的)。

5)注意對你喜歡的數據庫有中立的查詢。

或者

您可以利用System.Data.CommonDbProviderFactory檢測到您有DbConnection /提供程序的類型。你可以只是一個非通用Db類,並做到:

public class Db 
{ 
    string connectionString; 
    DbProviderFactory factory; 

    public Db(string driver, string connectionString) 
    { 
     this.factory = DbProviderFactories.GetFactory(driver); 
     this.connectionString = connectionString; 
    } 

    //and your core function would look like 
    IEnumerable<S> Do<R, S>(string query, Action<IDbCommand> parameterizer, 
          Func<IDbCommand, IEnumerable<R>> actor, 
          Func<R, S> selector) 
    { 
     using (var conn = factory.CreateConnection()) 
     { 
      // and all the remaining code.. 
     } 
    } 
} 

GetDbInstance方法會是什麼樣子:

static IDb GetDbInstance() 
{ 
    string connectionString = GetConnectionString(); 
    string driver = GetDriver(); 

    return Db(driver, connectionString); 
} 

臨:你擺脫編程if-else風格和正確版本的Db類將根據配置文件中的提供程序和連接字符串進行實例化。答:您需要在配置文件中指定正確的提供者/驅動程序。


從你的C#代碼示例查詢看起來像:

string query = "SELECT * FROM User WHERE [email protected] AND [email protected]"; 
var users = Db.Get(sql, cmd => 
{ 
    cmd.Parameterize("id", 1); 
    cmd.Parameterize("savedStatus", true); 
}, selector).ToArray(); 

所有你所要做的就是調用Db.GetDb.Save等功能GetDbInstance在這裏它發現在功能鍵正確的dll被調用,並且helper類很好地管理資源,同時另外完成各種db操作的任務。這樣的類可以避免打開和關閉連接的麻煩,釋放資源,每次都必須包含數據庫dll名稱空間等。這就是所謂的DbAL。您也可以使用additional layer來幫助DbAL在各種強類型模型類之間進行通信。我只是喜歡通過界面和約束多態性的力量,這是非常非常非常重要的! :)

+0

ya謝謝你會檢查和更新你的 – GowthamanSS

+0

感謝您的更新 – GowthamanSS

+0

@GowthamanSS我很好奇,知道這是否幫助你。我只是從瘋狂的想象中寫出來:)我必須說我經常沒有找到跨數據庫工作的好幫手類。 – nawfal

相關問題