在 C# 中以相同的方法访问不同的 SQL 服务器类型,例如 TSql 或 MySql
Access different SQL Server Types such as TSql or MySql in the same method in C#
目前我正在编写一个程序来访问 'types' 个 SQL 服务器,例如 C# 中的 TSQL 或 MySQL。我创建了一个具有抽象方法的基础 class DBConnector
:
public abstract class DBConnector
{
//some other code...
protected abstract int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20);
//some other code...
}
现在我有两个派生的 classes TSqlConnector
和 MySqlConnector
实现了这个抽象方法:
对于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;
}
对于 TSql,它看起来像这样:
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;
}
顺便说一句,这些方法还包含一些错误处理和其他一些东西,但我为此简化了我的方法post。
此方法在我的其他自定义方法中被调用,例如:
我调用 Insert-Method 中的代码(obj
是模型 class 的一个实例,它具有与数据库 table 相同的属性,包括 ID):
obj.ID = ExecuteNonQueryOrScalar(GetConnection(), sql, true); //true for scalar-execution
我调用更新方法中的代码:
affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
我调用 Delete-Method 中的代码:
affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
GetConnection()
returns 一个 DbConnection
-对象,它在运行时是 SqlConnection
或 MySqlConnection
。 sql
是我的 sql-字符串。布尔参数决定是否在 ExecuteNonQueryOrScalar()
中调用 ExecuteNonQuery
或 ExecuteScalar
,正如您在我上面的代码中看到的那样。
现在回答我的问题:
可以看到,两种实现的代码几乎是一样的。唯一的区别是连接和命令的类型。我听说你应该遵循“不要重复自己”的模式。但我在这里重复一遍。你们知道我能在这里做什么吗?还是我应该坚持我目前拥有的东西?我有想法将这两种方法移动到我的基础 class DBConnector
中的一个方法并使用通用参数,我用 where T: IDBEntity
和 where K: DBConnection
限制,但是当我这样做时会出现编译时错误。我真的找不到防止这种情况的解决方案。
您对如何以不同的方式实现它有什么建议吗?你会改变什么?
如果我需要分享更多我的代码,请告诉我。
感谢您花时间阅读我的问题。
编辑:我的问题的解决方案:
阅读回复后,我意识到如何改进我的代码。我将我的方法 ExecuteNonQueryOrScalar
移到了我的基础 class DBConnector
中并添加了一个 IDbCommand-Parameter,它可以是运行时的任何 IDbCommand-Object。此方法如下所示(简化版):
protected int ExecuteNonQueryOrScalar(IDbCommand cmd, bool executeAsScalar, int timeout = 20)
{
int result = -1;
try
{
cmd.CommandTimeout = timeout;
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
if (executeAsScalar)
{
string resultObject = cmd.ExecuteScalar().ToString();
if (Int32.TryParse(resultObject, out int tmpResult)) //if (resultObject is int tmpResult)
result = tmpResult;
}
else
{
result = cmd.ExecuteNonQuery();
}
}
//Some error handling...
finally
{
if (cmd.Connection.State != ConnectionState.Closed)
cmd.Connection.Close();
}
return result;
}
这是一个例子,我如何在我的 Update
-方法(简化)中调用 ExecuteNonQueryOrScalar
-方法,在相同的 DBConnector
-Class:
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;
}
最后这里有两个例子,我如何在 MySqlConnector
或 TSqlConnector
中调用这个更新方法:
MySql连接器:
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
}
TSqlConnector:
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
}
如果您想查看我的更多代码,请随时问我!我还可以将我未简化的原始代码上传到 github 等网站,如果你们中的一些人想要完成我所做的一切。
你的想法很好。但是 .net 团队中有人通过创建 interfaces that work this way, like IDBConnection and IDBCommand.
为您节省了一些时间
然后这个接口有不同的具体实现,你可以在需要的时候使用。例如 IDBConnection con = new MySqlConnection
代表 MySQL,或 IDBConnection con = new SqlConnection
代表 SQL 服务器。
这些接口公开了常用方法,例如 ExecuteNonQuery
。因此,您在代码中使用的只是一堆接口。您的代码总是绕过 IDBConnection
s 和 IDBCommand
s,您只需在构造时选择您需要的实现即可。这是dependency inversion.
当然,在填充命令的实际文本时,您必须使用正确的 SQL 方言。
这是否符合您的要求?
目前我正在编写一个程序来访问 'types' 个 SQL 服务器,例如 C# 中的 TSQL 或 MySQL。我创建了一个具有抽象方法的基础 class DBConnector
:
public abstract class DBConnector
{
//some other code...
protected abstract int ExecuteNonQueryOrScalar(DbConnection con, string statement, bool executeAsScalar, int timeout = 20);
//some other code...
}
现在我有两个派生的 classes TSqlConnector
和 MySqlConnector
实现了这个抽象方法:
对于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;
}
对于 TSql,它看起来像这样:
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;
}
顺便说一句,这些方法还包含一些错误处理和其他一些东西,但我为此简化了我的方法post。
此方法在我的其他自定义方法中被调用,例如:
我调用 Insert-Method 中的代码(obj
是模型 class 的一个实例,它具有与数据库 table 相同的属性,包括 ID):
obj.ID = ExecuteNonQueryOrScalar(GetConnection(), sql, true); //true for scalar-execution
我调用更新方法中的代码:
affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
我调用 Delete-Method 中的代码:
affectedLines = ExecuteNonQueryOrScalar(GetConnection(), sql, false); //false for nonQuery-execution
GetConnection()
returns 一个 DbConnection
-对象,它在运行时是 SqlConnection
或 MySqlConnection
。 sql
是我的 sql-字符串。布尔参数决定是否在 ExecuteNonQueryOrScalar()
中调用 ExecuteNonQuery
或 ExecuteScalar
,正如您在我上面的代码中看到的那样。
现在回答我的问题:
可以看到,两种实现的代码几乎是一样的。唯一的区别是连接和命令的类型。我听说你应该遵循“不要重复自己”的模式。但我在这里重复一遍。你们知道我能在这里做什么吗?还是我应该坚持我目前拥有的东西?我有想法将这两种方法移动到我的基础 class
DBConnector
中的一个方法并使用通用参数,我用where T: IDBEntity
和where K: DBConnection
限制,但是当我这样做时会出现编译时错误。我真的找不到防止这种情况的解决方案。您对如何以不同的方式实现它有什么建议吗?你会改变什么?
如果我需要分享更多我的代码,请告诉我。
感谢您花时间阅读我的问题。
编辑:我的问题的解决方案:
阅读回复后,我意识到如何改进我的代码。我将我的方法 ExecuteNonQueryOrScalar
移到了我的基础 class DBConnector
中并添加了一个 IDbCommand-Parameter,它可以是运行时的任何 IDbCommand-Object。此方法如下所示(简化版):
protected int ExecuteNonQueryOrScalar(IDbCommand cmd, bool executeAsScalar, int timeout = 20)
{
int result = -1;
try
{
cmd.CommandTimeout = timeout;
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
if (executeAsScalar)
{
string resultObject = cmd.ExecuteScalar().ToString();
if (Int32.TryParse(resultObject, out int tmpResult)) //if (resultObject is int tmpResult)
result = tmpResult;
}
else
{
result = cmd.ExecuteNonQuery();
}
}
//Some error handling...
finally
{
if (cmd.Connection.State != ConnectionState.Closed)
cmd.Connection.Close();
}
return result;
}
这是一个例子,我如何在我的 Update
-方法(简化)中调用 ExecuteNonQueryOrScalar
-方法,在相同的 DBConnector
-Class:
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;
}
最后这里有两个例子,我如何在 MySqlConnector
或 TSqlConnector
中调用这个更新方法:
MySql连接器:
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
}
TSqlConnector:
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
}
如果您想查看我的更多代码,请随时问我!我还可以将我未简化的原始代码上传到 github 等网站,如果你们中的一些人想要完成我所做的一切。
你的想法很好。但是 .net 团队中有人通过创建 interfaces that work this way, like IDBConnection and IDBCommand.
为您节省了一些时间然后这个接口有不同的具体实现,你可以在需要的时候使用。例如 IDBConnection con = new MySqlConnection
代表 MySQL,或 IDBConnection con = new SqlConnection
代表 SQL 服务器。
这些接口公开了常用方法,例如 ExecuteNonQuery
。因此,您在代码中使用的只是一堆接口。您的代码总是绕过 IDBConnection
s 和 IDBCommand
s,您只需在构造时选择您需要的实现即可。这是dependency inversion.
当然,在填充命令的实际文本时,您必须使用正确的 SQL 方言。
这是否符合您的要求?