C# 重构以使用具有不同输入对象的通用函数

C# 重构以使用具有不同输入对象的通用函数,c#,generics,dynamictype,C#,Generics,Dynamictype,我想重构一些在控制台应用程序中运行的代码。该应用程序更新了一个外部数据库,并且最近更新为支持MySQL或SQL Server。现在有两个几乎相同的方法,其中包含大量重复代码,因为一个方法签名使用MySqlConnection和MySqlCommand(etc),另一个使用SqlConnection和SqlCommand(etc) 除了ADO对象中的明显差异外,代码本质上是相同的 我想做的事情如下。我在这里看到了一些关于SO(例如)的帖子,以及其他网站,它们展示了如何使用动态类型进行设置,这非常好

我想重构一些在控制台应用程序中运行的代码。该应用程序更新了一个外部数据库,并且最近更新为支持MySQL或SQL Server。现在有两个几乎相同的方法,其中包含大量重复代码,因为一个方法签名使用MySqlConnection和MySqlCommand(etc),另一个使用SqlConnection和SqlCommand(etc)

除了ADO对象中的明显差异外,代码本质上是相同的

我想做的事情如下。我在这里看到了一些关于SO(例如)的帖子,以及其他网站,它们展示了如何使用动态类型进行设置,这非常好,只是没有一个示例能够比在泛型方法中编写foo.GetType()来证明动态类型是正确的更有效

那么,如何对该动态类型调用方法呢?当然,当我尝试设置时,尝试调用sqlConnection参数上的Open()方法不会编译

以下是我试图实现的目标:

private static void TransferXmlData(ExportManifest m_settings, XmlNodeList xmlNodeList)
{
    if (m_Settings.ServerType.ToLower() == "mysql")
    {
        using (MySqlConnection mySqlConnection = new MySqlConnection(m_Settings.TargetData.ConnectionString))
        {
            MySqlCommand mySqlCommand = 
                new MySqlCommand(Program.GetCommandTextTemplate(m_settings), mySqlConnection);
            PrepareSqlCommand(mySqlConnection, mySqlCommand, m_settings)
        }
    }
    else
    {
        using (SqlConnection sqlConnection = 
            new SqlConnection(m_Settings.TargetData.ConnectionString))
        {
            SqlCommand sqlCommand = 
                new SqlCommand(Program.GetCommandTextTemplate(m_settings), sqlConnection);
            PrepareSqlCommand(sqlConnection, sqlCommand, m_settings)
        }
    }
}

private static void PrepareSqlCommand<T>(T sqlConnection, T sqlCommand, ExportManifest m_settings)
{
    // Potentially a lot of code here that looks just like the 
    // code in the else block, Except that it uses the 
    // MySqlConnection objects instead of SqlConnection
    // Do some stuff
    sqlConnection.Open();  // obviously doesn't work
}
private static void TransferXmlData(ExportManifest m_设置,XmlNodeList XmlNodeList)
{
if(m_Settings.ServerType.ToLower()=“mysql”)
{
使用(MySqlConnection MySqlConnection=newmysqlconnection(m_Settings.TargetData.ConnectionString))
{
MySqlCommand MySqlCommand=
新的MySqlCommand(Program.GetCommandTextTemplate(m_设置),mySqlConnection);
PrepareSqlCommand(mySqlConnection、mySqlCommand、m_设置)
}
}
其他的
{
使用(SqlConnection SqlConnection=
新的SqlConnection(m_设置.TargetData.ConnectionString))
{
SqlCommand SqlCommand=
新的SqlCommand(Program.GetCommandTextTemplate(m_设置),sqlConnection);
PrepareSqlCommand(sqlConnection、sqlCommand、m_设置)
}
}
}
私有静态void PrepareSqlCommand(T sqlConnection、T sqlCommand、ExportManifest m_设置)
{
//这里可能有很多代码看起来就像
//else块中的代码,但它使用
//MySqlConnection对象而不是SqlConnection
//做点什么
sqlConnection.Open();//显然不起作用
}

提前谢谢

也许您可以实现工厂设计模式(如果您不想使用泛型,这是我的意见,您可以考虑一下。) 这将帮助您防止代码重复

实现您的工厂类

`

在实际方法中,可以使用IDbConnection和IDbCommand

private static void TransferXmlData(ExportManifest m_settings, XmlNodeList xmlNodeList)
{
    IDbConnection db = Factory.createDbInstance(m_settings);
                db.ConnectionString = m_Settings.TargetData.ConnectionString;
                IDbCommand comnd = db.CreateCommand();

                comnd.CommandText = Program.GetCommandTextTemplate(m_settings);
                comnd.CommandType = CommandType.Text;
               // db.Open(); if you want to open connection here
                PrepareSqlCommand(db, comnd, m_settings);
}


为了一次性编写数据访问代码,但能够基于某些逻辑切换出实现,您应该针对IDbConnection进行编码

大意是:

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString))

        {

            //execute database actions against IDBConnection               

        }

在我看来,这是使用repository模式建立起来的,它可以屏蔽数据库实现细节,但是模式的正确实现可能会使用例过于复杂。就决定哪些连接获得new()的逻辑而言,如上所述的工厂方法就足够了,但如果这是一个微不足道的应用程序,您也可以轻松地传递枚举标志。在大型软件中,您通常希望使用控制反转容器来控制要注入的IDbConnection的特定实例。在任何情况下,反射、泛型和动力学在这里都是错误的工具(除非有任何对象映射)。

正如@Sehnsucht所说,您可以这样做:

private static void TransferXmlData(ExportManifest m_settings, XmlNodeList xmlNodeList)
{
    if (m_Settings.ServerType.ToLower() == "mysql")
        Connect(connectionString => new MySqlConnection(connectionString),
            (text, connection) => new MySqlCommand(text, connection));
    else
        Connect(connectionString => new SqlConnection(connectionString),
            (text, connection) => new SqlCommand(text, connection));
}

private static void Connect(ExportManifest m_settings,
    Func<string, IDbConnection> createConnection,
        Func<string, IDbConnection, IDbCommand> createCommand)
{
    using (IDbConnection mySqlConnection =
        createConnection(m_Settings.TargetData.ConnectionString))
    {
        IDbCommand mySqlCommand =
            createCommand(Program.GetCommandTextTemplate(m_settings), mySqlConnection);
        PrepareSqlCommand(mySqlConnection, mySqlCommand, m_settings);
    }
}

private static void PrepareSqlCommand(IDbConnection sqlConnection,
    IDbCommand sqlCommand, ExportManifest m_settings)
{
    sqlConnection.Open();
}

你不能让你的方法期望一个
IDbConnection
和一个
IDbCommand
using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString))

        {

            //execute database actions against IDBConnection               

        }
private static void TransferXmlData(ExportManifest m_settings, XmlNodeList xmlNodeList)
{
    if (m_Settings.ServerType.ToLower() == "mysql")
        Connect(connectionString => new MySqlConnection(connectionString),
            (text, connection) => new MySqlCommand(text, connection));
    else
        Connect(connectionString => new SqlConnection(connectionString),
            (text, connection) => new SqlCommand(text, connection));
}

private static void Connect(ExportManifest m_settings,
    Func<string, IDbConnection> createConnection,
        Func<string, IDbConnection, IDbCommand> createCommand)
{
    using (IDbConnection mySqlConnection =
        createConnection(m_Settings.TargetData.ConnectionString))
    {
        IDbCommand mySqlCommand =
            createCommand(Program.GetCommandTextTemplate(m_settings), mySqlConnection);
        PrepareSqlCommand(mySqlConnection, mySqlCommand, m_settings);
    }
}

private static void PrepareSqlCommand(IDbConnection sqlConnection,
    IDbCommand sqlCommand, ExportManifest m_settings)
{
    sqlConnection.Open();
}
private static void Connect<TConnection, TCommand>(ExportManifest m_settings,
        Func<string, TConnection> createConnection,
        Func<string, TConnection, TCommand> createCommand)
    where TConnection : IDbConnection
    where TCommand : IDbCommand
{
    using (TConnection mySqlConnection =
        createConnection(m_Settings.TargetData.ConnectionString))
    {
        TCommand mySqlCommand =
            createCommand(Program.GetCommandTextTemplate(m_settings), mySqlConnection);
        PrepareSqlCommand(mySqlConnection, mySqlCommand, m_settings);
    }
}

private static void PrepareSqlCommand<TConnection, TCommand>(TConnection sqlConnection,
        TCommand sqlCommand, ExportManifest m_settings)
    where TConnection : IDbConnection
    where TCommand : IDbCommand
{
    sqlConnection.Open();
}