我们正在设计一种可以支持多个数据库的产品。我们目前正在做这样的事情,以便我们的代码支持MS SQL以及MySQL:
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;
}
}
}
}
您能否建议是否有更好的方法?该方法的优点和缺点是什么?
答案 0 :(得分:10)
无论你做什么,不要编写自己的映射代码。它之前已经完成了,它可能比你手写的任何东西都要好上百万倍。
毫无疑问,您应该使用NHibernate。它是一个对象关系映射器,它使数据库访问透明:您定义了一组代表数据库中每个表的DAL类,并使用NHibernate提供程序对您的数据库执行查询。 NHibernate将动态生成查询数据库和填充DAL对象所需的SQL。
关于NHibernate的好处是它根据你在配置文件中指定的内容生成SQL。开箱即用,它支持SQL Server,Oracle,MySQL,Firebird,PostGres和a few other databases。
答案 1 :(得分:5)
我会使用NHibernate。
答案 2 :(得分:2)
如果您必须自己编写代码并且不使用提供统一访问的产品,请记住SqlDataAdapter和OracleDataAdapter等对象继承自公共DbDataAdapter(至少在运行时的更高版本中)。如果向下转换为DbDataAdapter,则可以编写可以在两个数据库中对两个数据库执行相同操作的代码。你的一些代码看起来有点像这样:
DbDataAdapter adapter = GetOracleDataAdapter() as DbDataAdapter;
一旦你下来了,无论是SqlDataAdapter还是OracleDataAdapter都没关系。你可以用同样的方式调用它。
但是,请记住,对两个数据库进行编码意味着使用仅存在于两者中的功能,同时必须解决两者的缺点。这不是一个好主意。
答案 3 :(得分:2)
如果您需要从数据库条目到对象的映射,我建议您使用其他已经建议的解决方案:NHibernate。 如果这似乎对您的应用程序来说太过分了,并且您希望采用Ado.net方法并且不需要O / RM-soultion,那么您应该看一下Spring.net的人做了什么并了解{{3 }}
答案 4 :(得分:2)
根据您目前的需要,我同意NHibernate ......
只想指出你的班级层次结构......
您最好使用界面
喜欢(只需检查doc或Internet上的确切语法)
Interface IDBParser
Function1
Function2
class MSSQLParser : IDBParser
Function1
Function2
class MySQLParser : IDBParser
Function1
Function2
然后在您的代码中,您可以使用界面
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条件。添加其他数据库也会容易得多。另一个优点是它可以通过发送模拟对象帮助您进行单元测试。
答案 5 :(得分:1)
有对象关系映射层支持多种数据库技术,如Entity Spaces。
答案 6 :(得分:1)
在这种情况下总是有用的是创建一个分层架构,其中所有与数据库相关的东西都在数据访问层中。然后你可以有不同的DAO层实现,一个用于Oracle,SQL Server等......
您应该使用接口将业务层与DAO层分开,这样您的业务层就可以使用它们来访问DAO层。因此,您可以完美地交换DAO层的底层实现,以在Oracle DB或您喜欢的任何系统上运行。
另一个好建议是看看斯科特已经建议的对象关系映射器。我将看一下NHibernate或Entity框架。
答案 7 :(得分:0)
解决此问题的一种方法是将应用程序设计为完全使用断开连接的DataSet,并编写一个数据访问组件,处理从您将支持的不同数据库品牌中获取数据,以及对DataSet进行的持久更改通过您的应用程序返回原始数据库。
优点:.Net中的DataSet编写得很好,易于使用且功能强大,并且非常出色地提供了处理基于表格的数据的方法和工具。
缺点:如果您的应用程序需要在客户端使用非常大的数据集,则此方法可能会出现问题。
答案 8 :(得分:0)
目前,微软的实体框架有一些缺点,其中一些可能是交易破坏者,具体取决于应用程序的预期架构。
从我所看到和读到的关于V2的内容,它将与.Net 4一起发布,我认为它当然值得一看。
答案 9 :(得分:0)
很多人都提出了一个O / R映射框架,比如NHibernate。这是一种非常合理的方法,除非您出于某种原因不想使用O / R映射器。像NHibernate这样的东西可能会让你获得95%+,但你可能需要编写一些自定义SQL。如果是这样的话,不要惊慌失措;你仍然可以为其他人做一个特别的解决方案。
在这种情况下,将需要自定义SQL的位取出并将它们分离到特定于平台的插件模块中。根据您想要支持的各个数据库平台,编写Oracle,MySQL,SQL Server(等)插件。
ADO.Net使得包装sprocs相当容易,因此您可以将平台相关层向下移动到某些存储过程中,向中间层提供或多或少的consitent API。仍然存在一些平台依赖性(例如SQL Server变量名称上的'@'前缀),因此您需要创建一个通用的sproc包装器机制(这并不是那么难)。
运气好的话,您需要以这种方式突破的具体操作数量相当少,因此维护插件的工作量将会受到限制。