推出自己的一個並不是什麼大不了的事情。這裏有一個基本的結構,我將如何實現它的最低需求(你當然可以擴展它):
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();
}
}
此只做基本的ExecuteNonQuery
和ExecuteReader
一樣操作,簡單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.Common
下DbProviderFactory檢測到您有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.Get
,Db.Save
等功能GetDbInstance
在這裏它發現在功能鍵正確的dll被調用,並且helper類很好地管理資源,同時另外完成各種db操作的任務。這樣的類可以避免打開和關閉連接的麻煩,釋放資源,每次都必須包含數據庫dll名稱空間等。這就是所謂的DbAL。您也可以使用additional layer來幫助DbAL在各種強類型模型類之間進行通信。我只是喜歡通過界面和約束多態性的力量,這是非常非常非常重要的! :)
是的,你可以,但不是隻有驅動程序名稱,你將不得不將整個連接字符串放在web.config中,因爲連接字符串可能會因數據庫而異。然後你可以通過實現ISqlConnection,ISqlCommand接口爲db動作創建一個輔助類。 – nawfal
@nawfal ya感謝你給予啓動,因爲我是新手,需要Google來查找它......如果你給我一些鏈接,以便開始 – GowthamanSS
請看我更新的答案。 – nawfal