C# 在C中使用相同的方法访问不同的SQL Server类型,如TSql或MySql#

C# 在C中使用相同的方法访问不同的SQL Server类型,如TSql或MySql#,c#,mysql,tsql,inheritance,dry,C#,Mysql,Tsql,Inheritance,Dry,目前,我正在编写一个程序来访问多种“类型”的SQL服务器,例如C#中的TSQL或MySQL。我创建了一个基类DBConnector,它有一个抽象方法: public abstract class DBConnector { //some other code... protected abstract int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int

目前,我正在编写一个程序来访问多种“类型”的SQL服务器,例如C#中的TSQL或MySQL。我创建了一个基类
DBConnector
,它有一个抽象方法:

public abstract class DBConnector
{
    //some other code...

    protected abstract int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20);

    //some other code...
}
现在我有两个派生类
TSqlConnector
MySqlConnector
,它们实现了这个抽象方法:

public abstract class DBConnector
{
    //some other code...

    protected abstract int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20);

    //some other code...
}
对于MySql,它看起来是这样的:

protected override int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20)
{
    int result = -1;
    using (MySqlCommand cmd = new MySqlCommand(statement, (MySqlConnection)con))
    {
        cmd.CommandTimeout = timeout;
        cmd.Connection.Open();

        if (executeAsScalar)
        {
            object resultObject = cmd.ExecuteScalar();
            if (resultObject is int tmpResult)
                result = tmpResult;
        }
        else
        {
            result = cmd.ExecuteNonQuery();
        }
    }
    return result;
}
protected override int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20)
{
    int result = -1;
    using (SqlCommand cmd = new SqlCommand(statement, (SqlConnection)con))
    {
        cmd.CommandTimeout = timeout;
        cmd.Connection.Open();

        if (executeAsScalar)
        {
            object resultObject = cmd.ExecuteScalar();
            if (resultObject is int tmpResult)
                result = tmpResult;
        }
        else
        {
            result = cmd.ExecuteNonQuery();
        }
    }
    return result;
}
对于TSql,它看起来是这样的:

protected override int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20)
{
    int result = -1;
    using (MySqlCommand cmd = new MySqlCommand(statement, (MySqlConnection)con))
    {
        cmd.CommandTimeout = timeout;
        cmd.Connection.Open();

        if (executeAsScalar)
        {
            object resultObject = cmd.ExecuteScalar();
            if (resultObject is int tmpResult)
                result = tmpResult;
        }
        else
        {
            result = cmd.ExecuteNonQuery();
        }
    }
    return result;
}
protected override int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20)
{
    int result = -1;
    using (SqlCommand cmd = new SqlCommand(statement, (SqlConnection)con))
    {
        cmd.CommandTimeout = timeout;
        cmd.Connection.Open();

        if (executeAsScalar)
        {
            object resultObject = cmd.ExecuteScalar();
            if (resultObject is int tmpResult)
                result = tmpResult;
        }
        else
        {
            result = cmd.ExecuteNonQuery();
        }
    }
    return result;
}
顺便说一下,这些方法还包含一些错误处理和其他内容,但是我简化了我在这篇文章中的方法

在我的其他自定义方法中调用此方法,如下所示:

调用Insert方法中的代码(obj是一个模型类的实例,该模型类具有与数据库表相同的属性,包括ID):

我的调用更新方法中的代码:

affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
我的调用删除方法中的代码:

affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
GetConnection()
返回运行时为
SqlConnection
MySqlConnection
DbConnection
对象<代码>sql是我的sql字符串。布尔参数决定是在
ExecuteNonQueryOrScalar()
中调用
ExecuteNonQuery
还是调用
ExecuteScalar

现在回答我的问题:

protected override int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20)
{
    int result = -1;
    using (MySqlCommand cmd = new MySqlCommand(statement, (MySqlConnection)con))
    {
        cmd.CommandTimeout = timeout;
        cmd.Connection.Open();

        if (executeAsScalar)
        {
            object resultObject = cmd.ExecuteScalar();
            if (resultObject is int tmpResult)
                result = tmpResult;
        }
        else
        {
            result = cmd.ExecuteNonQuery();
        }
    }
    return result;
}
protected override int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20)
{
    int result = -1;
    using (SqlCommand cmd = new SqlCommand(statement, (SqlConnection)con))
    {
        cmd.CommandTimeout = timeout;
        cmd.Connection.Open();

        if (executeAsScalar)
        {
            object resultObject = cmd.ExecuteScalar();
            if (resultObject is int tmpResult)
                result = tmpResult;
        }
        else
        {
            result = cmd.ExecuteNonQuery();
        }
    }
    return result;
}
  • 如您所见,这两个实现的代码几乎相同。唯一的区别是连接类型和命令类型。我听说你应该遵循“不要重复你自己”的模式。但我在这里重复我自己。你们知道我能在这里做什么吗?还是我应该坚持我现在拥有的?我有一个想法,在我的基类
    DBConnector
    中将这两个方法移到一个方法中,并使用泛型参数,我用
    其中T:IDBEntity
    其中K:DBConnection
    限制了泛型参数,但这样做时会出现编译时错误。我真的找不到一个解决办法来防止这种情况

  • 你对如何以不同的方式实施这一点有什么建议吗?你会改变什么

  • 如果需要共享更多代码,请告诉我

    感谢您抽出时间阅读我的问题

    编辑:我的问题的解决方案:
    在阅读了回复后,我意识到如何改进代码。我将我的方法
    ExecuteNonQueryOrScalar
    移动到我的基类
    DBConnector
    中,并添加了一个IDbCommand参数,该参数可以是运行时的任何IDbCommand对象。此方法如下所示(简化版):

    下面是一个示例,我如何在同一
    DBConnector
    -类中的
    Update
    -方法(简化)中调用该
    ExecuteOnQueryOrScalar
    -方法:

    protected int Update<T, K>(T obj, List<DBCondition> filterValues = null) where T : IDBEntity, new() where K : IDbCommand, new()
    {
        int affectedLines = -1;
        if (obj != null)
        {
            using (IDbCommand cmd = new K())
            {
                cmd.Connection = GetConnection();
                cmd.CommandText = obj.GetSqlUpdateStatement(DBMapping.GetDBMapping<T>(), _sqlSpecificSymbols, filterValues);
                affectedLines = ExecuteNonQueryOrScalar(cmd, false);
            }
        }
        return affectedLines;
    }
    
    TSqlConnector:

    public override int Update<T>(T obj, List<DBCondition> filterValues = null)
    {
        return base.Update<T, MySqlCommand>(obj, filterValues); //Call DbConnector.Update<T, K>() from the example above
    }
    
    public override int Update<T>(T obj, List<DBCondition> filterValues = null)
    {
        return base.Update<T, SqlCommand>(obj, filterValues); //Call DbConnector.Update<T, K>() from the example above
    }
    
    public override int Update(T obj,List filterValues=null)
    {
    返回base.Update(obj,filterValues);//从上面的示例中调用DbConnector.Update()
    }
    

    如果你想看更多我的代码,请尽管问我!如果你们中的一些人想了解我所做的一切,我还可以将我未简化的原始代码上传到github等网站。

    你的想法很好。但是.net团队中的某个人通过这种方式创建该工作为您节省了一些时间,如和

    然后,您可以在需要时使用此接口的不同具体实现。例如,MySQL的
    IDBConnection con=new-MySqlConnection
    ,SQL Server的
    IDBConnection con=new-SqlConnection

    这些接口公开了常见的方法,如
    ExecuteNonQuery
    。因此,您在代码中使用的只是一组接口。您的代码总是传递
    IDBConnection
    s和
    IDBCommand
    s,您只需在构建时选择所需的实现。这是

    当然,在填充命令的实际文本时,必须使用正确的SQL方言


    这和你想做的一样吗?

    听起来你想重新发明EntityFramework。你需要这方面的帮助吗?据我所知,唯一需要改变的代码是
    SqlCommand cmd=new-SqlCommand(statement,(SqlConnection)con)
    ->命令的创建。@Neil我非常了解EF或Dapper等框架。但我只是想学习如何在没有任何外部框架的情况下以默认方式实现这一点,但使用SqlCommand、SqlConnection、DataReader等。这也帮助我更好地理解其他模式和设计可能性,例如DRY和多态性。非常感谢,这真的帮助了我!我编辑了我的帖子,并在其中添加了我的新代码。随便看看吧!