开发自己的Data Access Application Block[上篇]

经常在网上看到对ORM的讨论沸沸扬扬,我也来凑个热闹,谈谈我写的一个ORM。最近在做一项工作,把我们经常用到的一些业务逻辑抽象出来,写成一个个的Application Block,使之可以运用到不同的Application中,比如Data Access,Messaging,Auditing,Data binding等等。现在先做一个Data access application block。由于时间仓促,没有进行什么优化和较多的测试,大家不必深究我所提供的Code ,我只希望为大家的ORM提供另一种想法。

这个application block应达到的目的:

  • 封装所有的Data Access操作。
  • 适合主流的DBMS:SQL Server(2000和2005),Oracle(9i和10g),DB2。
  • 尽量简化Developer的操作和提供最大的灵活性,在Data Retrieval方面,只要指定SQL或者Stored Procedure和相应的参数;在Data Update方面,既可以直接调用SQL和Stored Procedure,还可以把包含多个相互关联Data Table的Dataset通过一次调用实现数据的更新。此外,可以自由地选择使用SQL还是Stored procedure;可以使用Commander builder生成Command或者使用基于Mapped stored procedure生成的Command进行数据更新。
  • 实现泛型编程,使使用该AppBlock的代码能够适合所有的数据库。
  • 实现Transaction。
  • 提供可配置性,包括不同数据库的配置,不同Data Mapping的配置等等。

下面是该AppBlock使用到的Entity:

  • Database:Abstract Class,封装了绝大部分和具体数据库无关的Data Access操作逻辑。通过两个Mapping:IDbParameterNameMapping和IStoredProcedureNameMapping,实现Dataset和Db的一个映射。比如Dataset中Data table name和Stored procedure name的Mapping,Data table中Field和Stored procedure中参数名的Mapping。这两个Mapping是可以配置的,你只需要实现提供的Interface编写适合你的Mapping provider就可以了。
  • SqlDatabase:封装基于SQL Server 的操作。ADO.NET 2.0在1.0的基础上作了很大的改善,主要的增加的大量的基类,为我们进行泛型编程,编写和具体Db无关的代码变得异常容易。所以我们把大多数Data Access的操作可以封装在Abstract Database类中,SqlDatabase中的内容实际上是很少的。
  • OracleDatabase:封装基于Oracle的操作。
  • IDbParameterNameMapping和IStoredProcedureNameMapping:我想大家都是这样的感受,实现ORM的本质就是实现内存中的数据(主要是Dataset)和数据库的一个映射。在Dataset和数据库中的Table相互Mapping方面,我觉得没有必要采用特殊的Mapping,直接和简单易行的就是Table和Dataset中的Data Table完全匹配(table name 和field name完全匹配)。所以重要的是实现Dataset和Stored procedure的Mapping:Table Name如何与进行Insert,Update,Delete的Stored procedure name匹配,不同Version(original & current)的Field如何与Stored procedure的Parameter name 匹配。而这样一个匹配应该是可配置的,因为每个Application在数据库设计时的命名都有各自的要求,所以我在这里采用的Provider的设计模式。用户可以实现这两个Interface编写适合自己的Mapping provider,通过我提供的Configuration block很容易地完成配置。同时,我写了一个默认的,简单的Mapping:SimpleDbParameterNameMapping和SimpleStoredProcedureNameMapping。

      有一点需要补充的是,要实现上面的Mapping,对Stored Procedure的命名有较高的要求,手工编写的方式已经不能适合我们的要求,所以我们需要一个生成Stored procedure的Generator,这个Generator也使用这两个可配置的Mapping接口。

  • DatabaseFactory: 一个静态的类,根据配置的信息创建你需要的具体的Database对象,实现泛型化编程。
  • DataAccessConfigurationCollection,DataAccessConfigurationElement,DataAccessConfigurationSection 三个Configuration的Class。

为了使大家清楚地看出这个Application block所有的操作,我把所有的操作封装在一个IDatabase的interface中,不过需要注意的是,我采用的是基于Abstract class的编程,而不是基于Interface的编程,相信大家对这两种方式讨论得已经碰倒的太多了,孰优孰劣我就不想对说了。这个IDatabase 接口,只是展示所有Operation之用,并没有在我的代码中用到。

namespace Artech.ApplicationBlock.DataAccess
{
    public interface IDatabase
    {
        #region Fill a System.Data.DataSet with retrieved data.
        void FillDataSet(DataSet dataInfo, string commandText, IDictionary<string, object> parameters);

        void FillDataSet(DataSet dataInfo, string tableName, string commandText, IDictionary<string, object> parameters);

        void FillDataSet(DataSet dataInfo, string tableName, CommandType commandType, string commandText, IDictionary<string, object> parameters);

        #endregion

        #region Save the changed data which is stored in a dataset into database.
        void UpdateData(DataSet dataInfo);

        void UpdateData(DataTable table);

        void UpdateData(DataTable table, string insertCommandText, string updateCommandText, string deleteCommandText,
           Dictionary<string, object> insertParameters, Dictionary<string, object> updateParameters, Dictionary<string, object> deleteParameters);

                void UpdateData(DataTable table, CommandType commandType, string insertCommandText, string updateCommandText, string deleteCommandText,
            Dictionary<string, object> insertParameters, Dictionary<string, object> updateParameters, Dictionary<string, object> deleteParameters);

        void UpdateData(DataTable table, DbCommand insertCommand, DbCommand updateCommand, DbCommand deleteCommand);

        #endregion

        #region Execute a command and return the affect row count.
        int ExecuteNonQuery(CommandType commandType, string commandText, Dictionary<string, object> inputParameters, Dictionary<string, object> outputParameters);

        int ExecuteNonQuery(CommandType commandType, string commandText, Dictionary<string, object> inputParameters);

        int ExecuteNonQuery( string commandText, Dictionary<string, object> inputParameters, Dictionary<string, object> outputParameters);

        int ExecuteNonQuery( string commandText, Dictionary<string, object> inputParameters);
        #endregion

        #region Execute a command and return the data in the form of data reader.
        DbDataReader ExecuteReader(CommandType commandType, string commandText, Dictionary<string, object> inputParameters, Dictionary<string, object> outputParameters);

        DbDataReader ExecuteReader(CommandType commandType, string commandText, Dictionary<string, object> inputParameters);

        DbDataReader ExecuteReader(string commandText, Dictionary<string, object> inputParameters, Dictionary<string, object> outputParameters);

        DbDataReader ExecuteReader(string commandText, Dictionary<string, object> inputParameters);
        #endregion

        #region Execute a command and return a scalar value.

        object ExecuteScalar(CommandType commandType, string commandText, Dictionary<string, object> inputParameters, Dictionary<string, object> outputParameters);

        object ExecuteScalar(CommandType commandType, string commandText, Dictionary<string, object> inputParameters);

        object ExecuteScalar(string commandText, Dictionary<string, object> inputParameters, Dictionary<string, object> outputParameters);

        object ExecuteScalar(string commandText, Dictionary<string, object> inputParameters);
        #endregion

        #region Transaction based operation
        void BeginTransaction();
        void Commit();
        void RollBack();
        #endregion
    }
}

这个列表和大部分ORM没有什么太大的区别,大家已经司空见惯,实现起来也不会有什么太大的困难。对于大部分操作,我不会做详细的介绍。接下来我们来简要地看看这样一个AppBlock是如何实现的。 1. Data Mapping

我们首先来看看Data Mapping:实现Dataset中Table name和Stored Procedure Name的Mapping,以及Dataset 中的Field 和Stored procedure的参Parameter name的Mapping。

IDbParameterNameMapping

using System;
using System.Collections.Generic;
using System.Text;

namespace Artech.ApplicationBlock.DataMapping
{
    /// <summary>
    /// IStoredProcedureNameMapping defines the mapping between the data table name and the name of stored procedures to perform insertion, modification and deletion operation.
    /// </summary>
    public interface IStoredProcedureNameMapping
    {
        /// <summary>
        /// Get the name of stored procedure to perform seletion operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform seletion operation</returns>
        string GetSelectStoredProcedureName(string tableName);

        /// <summary>
        /// Get the name of stored procedure to perform insert operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform insertion operation</returns>
        string GetInsertStoredProcedureName(string tableName);

        /// <summary>
        /// Get the name of stored procedure to perform modification operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform modification operation</returns>
        string GetModifyStoredProcedureName(string tableName);

        /// <summary>
        /// Get the name of stored procedure to perform deletion operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform deletion operation</returns>
        string GetDeleteStoredProcedureName(string tableName);
    }
}

IDbParameterNameMapping

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace Artech.ApplicationBlock.DataMapping
{
    /// <summary>
    /// IDbParameterNameMapping define the defult mapping between the source column name and the parameter name of the corresponding stored procedure.
    /// </summary>
    public interface IDbParameterNameMapping
    {
        /// <summary>
        /// Get the source column name based on the parameter name of the related stored procedure.
        /// </summary>
        /// <param name="patameterName">The parameter name of the corresponding stored procedure.</param>
        /// <returns>The source column name corresponding to the parameter name.</returns>
        string GetSourceCoulmnName(string patameterName);

        /// <summary>
        /// Get the source parameter name based on the source column name.
        /// </summary>
        /// <param name="columnName">The source column name corresponding to the parameter name.</param>
        /// <param name="rowVersion">The data row version of the source solumn conressponding to the parameter.</param>
        /// <returns>The parameter name of the corresponding stored procedure.</returns>
        string GetParameterName(string columnName, DataRowVersion rowVersion);
    }
}

这两个Mapping主要用在通过Dataset跟新数据库的场景,利用IDbParameterNameMapping,我们通过Dataset中各个Table name获得对它进行Insert,Update,Delete操作的Stored procedure的name。利用IDbParameterNameMapping,我们可以为Stored procedure的Parameter指定对应的Source field.  

注:GetParameterName方法实际上是不需要的,我把使用在另一个AppBlock中。

接下来我们来写两个实现了上面连个Interface的默认的mapping:SimpleStoredProcedureNameMapping和SimpleDbParameterNameMapping。他实际上实现了这样的Mapping:比如Table name为T_ABC_DEF(我经常用的命名方式:以T开头代表Table,名称大写并一下划线连接),那么对应的Stored procedure name分别为:sp_abc_def_s(Select), sp_abc_def_i(Insert), sp_abc_def_u(Update), sp_abc_def_d(delete)。如果Field name为ABC_123,那么对于Original version的Parameter name为o_abc_123(o代表Original),Current version的Parameter name为p_abc_123(p代表一般意义的Parameter)。

using System;
using System.Collections.Generic;
using System.Text;

namespace Artech.ApplicationBlock.DataMapping
{
    /// <summary>
    /// IStoredProcedureNameMapping defines the mapping between the data table name and the name of stored procedures to perform insertion, modification and deletion operation.
    /// </summary>
    public class SimpleStoredProcedureNameMapping:IStoredProcedureNameMapping
    {
        #region IStoredProcedureNameMapping Members
        /// <summary>
        /// Get the name of stored procedure to perform selection operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform selection operation</returns>
        public string GetSelectStoredProcedureName(string tableName)
        {
            //T_ABC_DEF=>sp_abc_def_s
            return string.Format("sp_{0}_s", tableName.Substring(2, tableName.Length - 2).ToLower());
        }

        /// <summary>
        /// Get the name of stored procedure to perform insert operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform insertion operation</returns>
        public string GetInsertStoredProcedureName(string tableName)
        {
            //T_ABC_DEF=>sp_abc_def_i
            return string.Format("sp_{0}_i", tableName.Substring(2, tableName.Length - 2).ToLower());
        }

        /// <summary>
        /// Get the name of stored procedure to perform modification operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform modification operation</returns>
        public string GetModifyStoredProcedureName(string tableName)
        {
            //T_ABC_DEF=>sp_abc_def_u
            return string.Format("sp_{0}_u", tableName.Substring(2, tableName.Length - 2).ToLower());
        }
        /// <summary>
        /// Get the name of stored procedure to perform deletion operation.
        /// </summary>
        /// <param name="tableName">The name of the database table.</param>
        /// <returns>The name of stored procedure to perform deletion operation</returns>
        public string GetDeleteStoredProcedureName(string tableName)
        {
            //T_ABC_DEF=>sp_abc_def_d
            return string.Format("sp_{0}_d", tableName.Substring(2, tableName.Length - 2).ToLower());
        }

        #endregion
    }
}

SimpleDbParameterNameMapping

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace Artech.ApplicationBlock.DataMapping
{
    /// <summary>
    /// SimpleDbParameterNameMapping defines a simple mapping method between the corresponding parameter name and source culumn name of the table.
    /// </summary>
    public class SimpleDbParameterNameMapping:IDbParameterNameMapping
    {
        #region IDbParameterNameMapping Members

        /// <summary>
        /// Get the source column name based on the parameter name of the related stored procedure.
        /// </summary>
        /// <param name="patameterName">The parameter name of the corresponding stored procedure.</param>
        /// <returns>The source column name corresponding to the parameter name.</returns>
        public string GetSourceCoulmnName(string patameterName)
        {
            //@p_abc_def|o_abc_def=>ABC_DEF
            return patameterName.Substring(3, patameterName.Length - 3).ToUpper();
        }

        /// <summary>
        /// Get the source parameter name based on the source column name.
        /// </summary>
        /// <param name="columnName">The source column name corresponding to the parameter name.</param>
        /// <param name="rowVersion">The data row version of the source solumn conressponding to the parameter.</param>
        /// <returns>The parameter name of the corresponding stored procedure.</returns>
        public string GetParameterName(string columnName, DataRowVersion rowVersion)
        {
            //ABC_DEF =>@p_abc_def|o_abc_def
            string prefix = "@p";
            if (rowVersion == DataRowVersion.Original)
            {
                prefix = "@o";
            }

            return string.Format("{0}_{1}", prefix, columnName.ToLowerInvariant());

        }

        #endregion
    }
}

2. Configuration

对于一个能够被不同Application使用的Application Block,可配置性是最基本的要求,为此,我为整个AppBlock编写了单独的Configuration。在很多情况下我们在一个Application中会使用到多个具有相同或不同类型的数据库,所以这个Configuration System也许我们配置若干数据库。因而我充分利于了ConfigurationElementCollection,ConfigurationElementCollection中的每个Element对应一个具体的数据库。相关的配置信息包括:

  • 一个唯一标该识数据库的DatabaseName(配置名称为name)
  • 一个带面Data access provider类型的DbProvider(配置名称为dbProvider)
  • 每个Db对应的ConnectionString(配置名称为connectionString)
  • 代表是否是Default Db的IsDefaultDatabase(配置名称为isDefault)
  • 两个Mapping :DbParameterNameMapping和StoredProcedureNameMapping(配置名称为dbParameterNameMapping和storedProcedureNameMapping,默认值为我丁一的默认Mapping type)
  • 默认的Command Type:DedaultCommandType(配置名称为dedaultCommandType,默认为使用stored procedure)
  • 使用CommandBuilder生成的Command还是使用Mapped stored procedure生成的Command进行Update:UsingCommandBuilderToUpdate(配置名称为usingCommandBuilderToUpdate,默认为使用Mapped stored procedure)。

ConfigurationElementCollection

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;

namespace Artech.ApplicationBlock.Configuration
{
    public class DataAccessConfigurationCollection: ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new DataAccessConfigurationElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            DataAccessConfigurationElement configurationElement = element as DataAccessConfigurationElement;
            if (configurationElement == null)
            {
                throw new Exception("It is fail to parse the data access configuration");
            }

            return configurationElement.DatabaseName;
        }
    }
}

DataAccessConfigurationElement

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Data;

namespace Artech.ApplicationBlock.Configuration
{
    public class DataAccessConfigurationElement:ConfigurationElement
    {
        /// <summary>
        /// The database namw which uniquely identifies the current database.
        /// </summary>
        [ConfigurationProperty("name", IsRequired = true)]
        public string DatabaseName
        {
            get
            {
                return this["name"] as string;
            }

            set
            {
                this["name"] = value;
            }
        }

        /// <summary>
        /// A string indicating the concrete database provider.
        /// </summary>
        [ConfigurationProperty("dbProvider", DefaultValue = "System.Data.SqlClient")]
        public string DbProvider
        {
            get
            {
                return this["dbProvider"] as string;
            }

            set
            {
                this["dbProvider"] = value;
            }
        }

        
        /// <summary>
        /// A string indicating the connection string to connect to the concrete database.
        /// </summary>
        [ConfigurationProperty("connectionString")]
        public string ConnectionString
        {
            get
            {
                return this["connectionString"] as string;
            }

            set
            {
                this["connectionString"] = value;
            }
        }

        /// <summary>
        /// A flag which determines if this is the default database.
        /// </summary>
        [ConfigurationProperty("isDefault", DefaultValue = false)]
        public bool IsDefaultDatabase
        {
            get
            {
                return (bool)this["isDefault"];
            }

            set
            {
                this["isDefault"] = value;
            }
        }
        /// <summary>
        /// A bool flag indicating if  the default operation are all executed using command builder or stored procedure.
        /// </summary>
        [ConfigurationProperty("usingCommandBuilderToUpdate", DefaultValue = false)]
        public bool UsingCommandBuilderToUpdate
        {
            get
            {
                return (bool)this["usingCommandBuilderToUpdate"];
            }

            set
            {
                this["usingCommandBuilderToUpdate"] = value;
            }
        }

        /// <summary>
        /// A CommandType enumeration indicating if the default command type.
        /// </summary>
        [ConfigurationProperty("dedaultCommandType", DefaultValue = CommandType.StoredProcedure)]
        public CommandType DedaultCommandType
        {
            get
            {
                return (CommandType)this["dedaultCommandType"];
            }
            set
            {
                this["dedaultCommandType"] = value;
            }
        }

        /// <summary>
        /// A string indicating the type of the class to perform mapping between parameter and the source column name.
        /// </summary>
        [ConfigurationProperty("dbParameterNameMapping", DefaultValue = "Artech.ApplicationBlock.DataMapping.SimpleDbParameterNameMapping,Artech.ApplicationBlock.DataMapping")]
        public string DbParameterNameMapping
        {
            get
            {
                return this["dbParameterNameMapping"] as string;
            }

            set
            {
                this["dbParameterNameMapping"] = value;
            }
        }

        /// <summary>
        /// A string indicating the type of the class to perform mapping between  the data table name and the name of stored procedures to perform insertion, modification and deletion operation.
        /// </summary>
        [ConfigurationProperty("storedProcedureNameMapping", DefaultValue = "Artech.ApplicationBlock.DataMapping.SimpleStoredProcedureNameMapping,Artech.ApplicationBlock.DataMapping")]
        public string StoredProcedureNameMapping
        {
            get
            {
                return this["storedProcedureNameMapping"] as string;
            }

            set
            {
                this["storedProcedureNameMapping"] = value;
            }
        }
    }
}

DataAccessConfigurationSection

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Data;

namespace Artech.ApplicationBlock.Configuration
{
    
    public class DataAccessConfigurationSection: ConfigurationSection
    {
        [ConfigurationProperty("", IsDefaultCollection = true)]
        public DataAccessConfigurationCollection Databases
        {
            get
            {
               return this[""] as DataAccessConfigurationCollection;
            }

            set
            {
                this[""] = value;
            }
        }

    }
}

我们可以把我们定义的Configure通过下面的方式运用到configuration文件中。

<?xml version="1.0"?>
<configuration>
    <configSections>
        <sectionGroup name="Artech.ApplicationBlock" type="Artech.ApplicationBlock.Configuration.ApplicationBlockConfigurationSectionGroup, Artech.ApplicationBlock.Configuration">
            <section name="Artech.ApplicationBlock.DataAccess" type="Artech.ApplicationBlock.Configuration.DataAccessConfigurationSection,Artech.ApplicationBlock.Configuration"/>
      <section name="Artech.ApplicationBlock.Messaging" type="Artech.ApplicationBlock.Configuration.MessagingConfigurationSection,Artech.ApplicationBlock.Configuration"/>
    </sectionGroup>
    </configSections>
    <Artech.ApplicationBlock>
        <Artech.ApplicationBlock.DataAccess>
      <add name="sqlDatabase"  connectionString="Data Source=JIANGJINNAN\SQLEXPRESS;Initial Catalog=iFramework;Integrated Security=True" ></add>      
    </Artech.ApplicationBlock.DataAccess>
    </Artech.ApplicationBlock>    
</configuration>

注:我把我开发的所有AppBlock相关的Configuration放在一个自定义的叫做ApplicationBlockConfigurationSectionGroup的ConfigurationSectionGroup中(<sectionGroup name="Artech.ApplicationBlock" type="Artech.ApplicationBlock.Configuration.ApplicationBlockConfigurationSectionGroup, Artech.ApplicationBlock.Configuration">),我推荐大家使用这种方式,一来可以使结构更加清晰,二来在你需要的时候,扩展该ConfigurationSectionGroup加入一些公用的配置。

3. Database Factory

为了实现泛型化的编程,使我的代码和具体的数据库无关,我采用了Factory的模式。我定义了两个重载的方法CreateDatabase,调用没有参数的方法创建一个默认的Database(isDefault=”true”), 调用有参数的方法通过传入配置的Database的name创建我们需要的Database。具体的逻辑就是通过ConfigurationManager提取相应的Configuration 信息,通过DbProvider创建对应类型的Database (比如”System.Data.SqlClient”对应SQL Server)。把provider的信息传入System.Data.Common.DbProviderFactories.GetFactory方法创建一个System.Data.Common。DbProviderFactory赋值给创建的Database的DatabaseProviderFactory属性(DbProviderFactory是一个很有价值的Type,为我们创建一个泛型的ADO.NET对象提供了方便)。同时把其他的配置信息赋值给创建的Database相应的属性。提取两个Mapping Type的信息,通过Reflection的机制创建相应的类型,并同时把其他的配置信息赋值给创建的Database相应的属性。

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Configuration;
using Artech.ApplicationBlock.Configuration;
using Artech.ApplicationBlock.Common;
using Artech.ApplicationBlock.DataMapping;
using Artech.ApplicationBlock.Common.Exceptions;

namespace Artech.ApplicationBlock.DataAccess
{
    public static class DatabaseFactory
    {
        static void ThrowAConfigurationException()
        {
            throw new CustomConfigurationException(CommonConstant.DataAccess.Messages.ConfigurationError);
        }

        static DatabaseFactory()
        {

        }

        /// <summary>
        /// Create the default database.
        /// </summary>
        /// <returns>Default database.</returns>
        public static Database CreateDatabase()
        {
            DataAccessConfigurationElement configurationElement = ApplicationBlockConfiguration.GetDataAccessConfigurationElement();
            Database database = null;

            if (configurationElement.DbProvider == CommonConstant.DataAccess.ProviderInvariantName_SqlServer)
            {
                database = new SqlDatabase();
            }
            else
            {
                
            }

            database.ConnectionString = configurationElement.ConnectionString;
            database.DatabaseProviderFactory = DbProviderFactories.GetFactory(configurationElement.DbProvider);
            database.DefaultCommandType = configurationElement.DedaultCommandType;

            Type dbParameterNameMappingType = Type.GetType(configurationElement.DbParameterNameMapping);
            database.DbParameterNameMapping = (IDbParameterNameMapping)dbParameterNameMappingType.GetConstructor(new Type[0]).Invoke(new object[0]);

            Type storedProcedureNameMappingType = Type.GetType(configurationElement.StoredProcedureNameMapping);
            database.StoredProcedureNameMapping = (IStoredProcedureNameMapping)storedProcedureNameMappingType.GetConstructor(new Type[0]).Invoke(new object[0]);
            database.UseCommandBuilder = configurationElement.UsingCommandBuilderToUpdate;

            return database;
        }
        /// <summary>
        /// Create a particular database by specifying the configurable database name.
        /// </summary>
        /// <param name="databaseName">the name of database specified in configuration.</param>
        /// <returns>The database corresponding to the database name.</returns>
        public static Database CreateDatabase(string databaseName)
        {
            DataAccessConfigurationElement configurationElement = ApplicationBlockConfiguration.GetDataAccessConfigurationElement(databaseName);
            Database database = null;
            if (configurationElement.DbProvider == CommonConstant.DataAccess.ProviderInvariantName_SqlServer)
            {
                database = new SqlDatabase();
            }
            else
            {

            }
            database.ConnectionString = configurationElement.ConnectionString;
            database.DatabaseProviderFactory = DbProviderFactories.GetFactory(configurationElement.DbProvider);
            database.DefaultCommandType = configurationElement.DedaultCommandType;

            Type dbParameterNameMappingType = Type.GetType(configurationElement.DbParameterNameMapping);
            database.DbParameterNameMapping = (IDbParameterNameMapping)dbParameterNameMappingType.GetConstructor(new Type[0]).Invoke(new object[0]);

            Type storedProcedureNameMappingType = Type.GetType(configurationElement.StoredProcedureNameMapping);
            database.StoredProcedureNameMapping = (IStoredProcedureNameMapping)storedProcedureNameMappingType.GetConstructor(new Type[0]).Invoke(new object[0]);
            database.UseCommandBuilder = configurationElement.UsingCommandBuilderToUpdate;

            return database;
        }
       
    }
}

注:上面的代码是不完整的,仅仅做到SQL Server的支持,由于基于Configuration的逻辑被我封装在另一个Configuration AppBlock中,所以代码中包含有对此的调用,相信不会影响大家的理解。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏along的开发之旅

Microsoft Visual Studio International Pack下载和试用

1.Simplified Chinese Pin-Yin Conversion Library(简体中文拼音转换类库)      

1123
来自专栏c#开发者

ado.net data services开发框架学习

ado.net data services开发框架学习 什么是ado.net data services Ado.net 数据服务可以很方便的将企业内部数据发...

2806
来自专栏大内老A

.NET的资源并不限于.resx文件,你可以采用任意存储形式[上篇]

为了构建一个轻量级的资源管理框架以满足简单的本地化(Localization)的需求,我试图直接对现有的Resource编程模型进行扩展。虽然最终没能满足我们的...

2407
来自专栏xingoo, 一个梦想做发明家的程序员

归并排序

采用分治的思想  以O(NlogN)最坏的情形运行时间运行 如果对merge的每个递归调用都采用局部声明一个临时数组,那么在任一时刻就可能有logN个临时数组处...

1877
来自专栏GreenLeaves

C#核编之System.Environment类

      在前面的例子中用来了Environment.GetCommandLineArgs()这个方法,这个方法就是获取用户的命令行输入,是Environme...

2407
来自专栏Create Sun

mvc+webapi 单元测试 3.使用Moq模拟AspnetMvc中的Request.Form

 1.前言     现在这个项目已经有阶段性的模块完成了,所以就想着对这些模块进行单元测试,以保证项目的代码的质量。首先虽然标题是mvc+webapi实质上我只...

6229
来自专栏强仔仔

SpringBoot JPA实现增删改查、分页、排序、事务操作等功能

今天给大家介绍一下SpringBoot中JPA的一些常用操作,例如:增删改查、分页、排序、事务操作等功能。 下面先来介绍一下JPA中一些常用的查询操作: ...

2706
来自专栏Java成神之路

Java微信公众平台开发_06_素材管理

2692
来自专栏马洪彪

C#解析PDF

C#解析PDF的方式有很多,比较好用的有ITestSharp和PdfBox。 PDF内容页如果是图片类型,例如扫描件,则需要进行OCR(光学字符识别)。 文本内...

5369
来自专栏木宛城主

工欲善其事,必先利其器:分享一套Code Smith 搭建N层架构模板

 开篇 平常开发时,由于冗余代码过多,程序员做重复的工作过多势必会影响开发效率。倘若对重复性代码简单的复制、粘贴,虽然也能节省时间,但也需仔细一步步替换,这无...

1948

扫码关注云+社区

领取腾讯云代金券