重构为对不同的输入对象使用通用函数
本文关键字:对象 函数 输入 重构 | 更新日期: 2023-09-27 18:14:31
我想重构一些在控制台应用程序中运行的代码。该应用程序更新外部数据库,并且它最近更新以支持MySQL或SQL Server。所以现在有两个几乎相同的方法,其中有很多重复的代码,因为一个方法签名使用MySqlConnection和MySqlCommand(等),另一个使用SqlConnection和SqlCommand(等)。
代码基本上是相同的,除了在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
}
提前感谢!
也许你可以实现工厂设计模式(如果你不想用泛型,这是我的意见,你可以考虑一下。)这将帮助您防止代码重复。
实现你的Factory类
Public class Factory
{
public static IDbConnection createDbInstance(ExportManifest m_settings)
{
if (m_Settings.ServerType.ToLower() == "mysql")
{
return new MySqlConnection();
}
else
return new SqlConnection();
}
} `
,在你的实际方法中,你可以使用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);
}
private static void PrepareSqlCommand(IDbConnection sqlConnection, IDbCommand 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();
}
为了编写一次数据访问代码,但能够根据某些逻辑切换实现,您应该针对IDbConnection进行编码。
大意是:
using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString))
{
//execute database actions against IDBConnection
}
在我看来,使用存储库模式可以很好地建立这一点,并且它使您不需要了解数据库实现细节,但是模式的适当实现可能会使您的用例过于复杂。至于决定哪些连接获得new()的逻辑,如上所述的工厂方法就足够了,但如果这是一个微不足道的应用程序,您也可以轻松地传递enum标志。在大型软件中,您通常希望使用控制反转容器来控制要注入的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();
}
SqlConnection
和MySqlConnection
都继承自DbConnection
,实现了IDbConnection
。SqlCommand
和MySqlCommand
也是一样,它们实现了IDbCommand
。
然后,你可以使用这些接口来合并你的代码。
但是,由于某些原因,您将需要使用实际类型(作为返回值)。你可以这样修改你的方法:
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();
}