为任何数据库支持编写驱动程序类泛型

Gow*_*nSS 4 .net c# database dll

在过去的几天里,我正在使用各种数据库,如MySQL,oracle,Ibmdb2等,通过odbc提供商与dot net连接.

例如:

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=***;"
Run Code Online (Sandbox Code Playgroud)

现在我的问题是

是否可以为任何数据库提供程序编写泛型类

Driver={My own driver};server=**********;uid=**;database=**;port=***;pwd=***;"
Run Code Online (Sandbox Code Playgroud)

它只是通过更改web.config中的驱动程序名称并将该dll文件放在我发布的Web应用程序或网站项目的bin文件夹中来连接每个数据库.

naw*_*fal 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);

}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这只做基本的ExecuteNonQueryExecuteReader类似的操作,简单的Transactions.没有存储过程.该Add函数适用于插入和检索最后插入的id和喜欢.让我变得懒惰并且只使用一个核心执行函数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);
    }
}
Run Code Online (Sandbox Code Playgroud)

4)现在我将在GetDbInstance某处创建一个额外的静态函数,以便它推断出正确的数据库参数,如连接字符串,提供程序类型等.还有一个扩展方法来简化查询的参数化.我将它们都放在上面的静态Db类中,但这是你的选择(有些人在Db类中编写它但我更喜欢它在外面因为功能应该是你的应用程序).

5)注意对您喜欢的数据库进行中性查询.

要么

您可以利用DbProviderFactorySystem.Data.Common检测的类型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..
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你的GetDbInstance方法看起来像:

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

    return Db(driver, connectionString);
}
Run Code Online (Sandbox Code Playgroud)

Pro:您摆脱了if-else编程风格,并且Db将根据配置文件中的提供程序和连接字符串实例化正确版本的类.

Con:您需要在配置文件中指定正确的提供者/驱动程序.


来自C#代码的示例查询如下所示:

string query = "SELECT * FROM User WHERE id=@id AND savedStatus=@savedStatus";
var users = Db.Get(sql, cmd =>
{
    cmd.Parameterize("id", 1);
    cmd.Parameterize("savedStatus", true);
}, selector).ToArray();
Run Code Online (Sandbox Code Playgroud)

所有你所要做的就是通话Db.Get,Db.Save等功能GetDbInstance是这里的关键,其发现在右边的DLL函数被调用,以及辅助类管理的资源以及同时还做了各种数据库操作的任务.这样的类可以避免打开和关闭连接,释放资源,每次都必须包含数据库DLL命名空间等的麻烦.这就是所谓的DbAL.您还可以使用其他图层来帮助DbAL在各种强类型模型类之间进行通信.我只是喜欢通过接口和约束的多态性的力量,非常非常OOP!:)