C# 为.NET产品支持多个数据库的最佳方式是什么?
我们正在设计一种可以支持多个数据库的产品。我们目前正在做类似的事情,以便我们的代码支持MS SQL和MySQL:C# 为.NET产品支持多个数据库的最佳方式是什么?,c#,design-patterns,multiple-databases,architecture,C#,Design Patterns,Multiple Databases,Architecture,我们正在设计一种可以支持多个数据库的产品。我们目前正在做类似的事情,以便我们的代码支持MS SQL和MySQL: namespace Handlers { public class BaseHandler { protected string connectionString; protected string providerName; protected BaseHandler() {
namespace Handlers
{
public class BaseHandler
{
protected string connectionString;
protected string providerName;
protected BaseHandler()
{
connectionString = ApplicationConstants.DatabaseVariables.GetConnectionString();
providerName = ApplicationConstants.DatabaseVariables.GetProviderName();
}
}
}
namespace Constants
{
internal class ApplicationConstants
{
public class DatabaseVariables
{
public static readonly string SqlServerProvider = "System.Data.SqlClient";
public static readonly string MySqlProvider = "MySql.Data.MySqlClient";
public static string GetConnectionString()
{
return ConfigurationManager.ConnectionStrings["CONNECTION_STRING"].ConnectionString;
}
public static string GetProviderName()
{
return ConfigurationManager.ConnectionStrings["CONNECTION_STRING"].ProviderName;
}
}
}
}
namespace Handlers
{
internal class InfoHandler : BaseHandler
{
public InfoHandler() : base()
{
}
public void Insert(InfoModel infoModel)
{
CommonUtilities commonUtilities = new CommonUtilities();
string cmdInsert = InfoQueryHelper.InsertQuery(providerName);
DbCommand cmd = null;
try
{
DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
DbConnection con = LicDbConnectionScope.Current.GetOpenConnection(provider, connectionString);
cmd = commonUtilities.GetCommand(provider, con, cmdInsert);
commonUtilities.PrepareCommand(cmd, infoModel.AccessKey, "paramAccessKey", DbType.String, false, provider, providerName);
commonUtilities.PrepareCommand(cmd, infoModel.AccessValue, "paramAccessValue", DbType.String, false, provider, providerName);
cmd.ExecuteNonQuery();
}
catch (SqlException dbException)
{
//-2146232060 for MS SQL Server
//-2147467259 for MY SQL Server
/*Check if Sql server instance is running or not*/
if (dbException.ErrorCode == -2146232060 || dbException.ErrorCode == -2147467259)
{
throw new BusinessException("ER0008");
}
else
{
throw new BusinessException("GENERIC_EXCEPTION_ERROR");
}
}
catch (Exception generalException)
{
throw generalException;
}
finally
{
cmd.Dispose();
}
}
}
}
namespace QueryHelpers
{
internal class InfoQueryHelper
{
public static string InsertQuery(string providerName)
{
if (providerName == ApplicationConstants.DatabaseVariables.SqlServerProvider)
{
return @"INSERT INTO table1
(ACCESS_KEY
,ACCESS_VALUE)
VALUES
(@paramAccessKey
,@paramAccessValue) ";
}
else if (providerName == ApplicationConstants.DatabaseVariables.MySqlProvider)
{
return @"INSERT INTO table1
(ACCESS_KEY
,ACCESS_VALUE)
VALUES
(?paramAccessKey
,?paramAccessValue) ";
}
else
{
return string.Empty;
}
}
}
}
请问有没有更好的方法?这种方法的优缺点是什么?有一些对象关系映射层支持多种数据库技术,如。我会使用 这里有个好主意无论您做什么,都不要编写自己的映射代码。它以前已经做过了,而且可能比任何你能手写的东西都要好一百万倍 毫无疑问,你应该使用。它是一个使数据库访问透明的对象关系映射器:定义一组表示数据库中每个表的DAL类,并使用NHibernate提供程序对数据库执行查询。NHibernate将动态生成查询数据库和填充DAL对象所需的SQL
NHibernate的优点在于它根据您在配置文件中指定的内容生成SQL。开箱即用,它支持SQL Server、Oracle、MySQL、Firebird、PostGres和。解决此问题的一种方法是将应用程序设计为完全使用断开连接的数据集,并编写一个数据访问组件,处理从您将支持的不同数据库品牌获取数据的操作,以及将应用程序对数据集所做的更改持久化回原始数据库 优点:Net中的数据集编写得很好,易于使用,功能强大,在提供处理基于表的数据的方法和工具方面做得很好
缺点:如果您的应用程序需要在客户端处理非常大的数据集,那么这种方法可能会有问题。在这种情况下,最好创建一个分层的体系结构,其中所有与数据库相关的内容都位于数据访问层。然后您可以有不同的DAO层实现,一个用于Oracle、SQL Server等 您应该使用接口将业务层与DAO层分开,这样您的业务层就可以使用它们来访问DAO层。因此,您可以完美地交换DAO层的底层实现,以便在Oracle DB或您喜欢的任何系统上运行
另一个很好的建议是看看Scott已经建议过的对象关系映射器。我想看看NHibernate或Entity framework。现在,Microsoft的Entity framework有一些缺点,其中一些缺点可能会破坏交易,具体取决于应用程序的预期体系结构
从我所看到和阅读的有关V2的内容来看,我认为它肯定值得一看。许多人建议使用O/R映射框架,如NHibernate。这是一种非常合理的方法,除非您出于某种原因不想使用O/R映射器。类似NHibernate的东西可能会让您获得95%以上的收益,但您可能需要编写一些自定义SQL。如果是这样,不要惊慌;您仍然可以为其余部分提供一个特别的解决方案 在本例中,将确实需要自定义SQL的位分离到特定于平台的插件模块中。根据需要为您想要支持的各个数据库平台编写Oracle、MySQL、SQL Server(等)插件 Net使封装存储过程变得相当容易,因此您可以将依赖于平台的层向下移动到一些存储过程中,向中间层提供或多或少一致的API。仍然存在一些平台依赖项(例如SQLServer变量名上的“@”前缀),因此您需要创建一个通用的存储过程包装机制(这并不难)
如果幸运的话,您需要以这种方式进行的特定操作数量将非常少,因此维护插件的工作量将受到限制。如果您必须自己编写代码,而不是使用提供统一访问的产品,请记住,像SqlDataAdapter和OracleDataAdapter这样的对象继承自公共DbDataAdapter(至少在运行时的更高版本中)。如果您转换为DbDataAdapter,那么您可以在对两个数据库都执行相同操作的位置编写代码来处理这两个数据库。您的一些代码看起来有点像这样:
DbDataAdapter adapter = GetOracleDataAdapter() as DbDataAdapter;
一旦您对它进行了转换,它是SqlDataAdapter还是OracleDataAdapter就无关紧要了。你也可以这样称呼它
但是,请记住,为两个数据库编码意味着使用仅存在于这两个数据库中的功能,同时必须解决这两个数据库的缺点。这真的不是一个好主意。如果您需要从数据库条目到对象的映射,我建议您使用其他已建议的解决方案:NHibernate。
如果这对您的应用程序来说似乎有些过分,并且您希望使用Ado.net方法,而不需要O/RM解决方案,那么您应该看看Spring.net的工作人员做了什么,并了解一下对于您当前的需要,我同意NHibernate 只是想指出一些关于类层次结构的东西 您将更好地使用界面 比如(只需查看文档或互联网上的确切语法) 然后在代码中可以使用接口
Main()
IDBParser dbParser;
if(...)
dbParser = new MSSQLParser();
else
dbParser = new MySQLParser();
SomeFunction( dbParser );
// the parser can be sent by parameter, global setting, central module, ...
SomeFunction( IDBParser dbParser)
dbParser.Function1();
这样管理起来就更容易了,代码也不会充满相同的if/else条件。添加其他数据库也会容易得多。另一个优点是,它可以通过发送模拟对象来帮助您进行单元测试。因为怀疑
Main()
IDBParser dbParser;
if(...)
dbParser = new MSSQLParser();
else
dbParser = new MySQLParser();
SomeFunction( dbParser );
// the parser can be sent by parameter, global setting, central module, ...
SomeFunction( IDBParser dbParser)
dbParser.Function1();