zoukankan      html  css  js  c++  java
  • 实战分层架构

    现在可选的框架

        现在我们开发一个.net应用,面临的选择比较多。我们可以选择entity framework, enterprise library, nhibernate, 还有一个mybatis.net, 即java世界mybatis/ibatis的.net版。 IOC的框架可以选择Unity, Ninject,Spring.net(java的spring对应的.net版本)。Entity framework可以使用linq查询,有好几种开发模式,如code first, db first, 可以不用写sql。Entity framework适合sql server。虽有mysql提供了entity framework的provider,但是不是很好, 经常不得不单独写sql来操作mysql. Enterprise library是个不错的选择,有log, exception, policy injection的一些东西,操作数据库也比较好。nhibernate是java版hibernate的.net对应版本。对数据库包装得比较多,可以不用写sql. 但是有些时候对数据库操作不够优化,会有一些多余数据库操作。带来一些性能的影响。mybatis.net是个介于ado.net与nhibernate之间的框架,它负责数据库对象与内存对象的映射。程序员必须写sql语句来操作数据库。IOC的框架中Unity, Ninject, Spring.net, 都是不错的框架。有时这些框架能照顾到我们大部分的需求,也有一些情况,不能全部照顾到我们的需求。这时就得用一些老办法。比如直接就用ado.net

    适应多种数据库的db helper代码

        这里主要是想说一些基于ado.net来开发asp.net mvc应用的一些思路。为什么选用ado.net呢,ado.net性能可以达到最好,灵活。先看一段db helper的代码:

    using System;
    using System.Configuration;
    using System.Data;
    using System.Data.Common;
    using System.Collections.Generic;
    
    namespace DataAccessCommon
    {
        /// <summary>
        /// The MyDBHelper class is intended to encapsulate high performance, scalable best practices for 
        /// common uses of SqlClient, OracleClient, OleDb, and others
        /// </summary>
        public static class MyStaticDBHelper
        {
            public struct MyDBParameter
            {
                public string strParameterName;
                public DbType dbType;
                public object value;
                public ParameterDirection parameterDirection;
    
                public MyDBParameter(string parameterName, DbType type, object theValue, ParameterDirection direction = ParameterDirection.Input)
                {
                    strParameterName = parameterName;
                    dbType = type;
                    value = theValue;
                    parameterDirection = direction;
                }
            }
            public static string DatabaseType = "SqlServer";
            private static Dictionary<string, string> providers = new Dictionary<string, string>() {
            { "SqlServer", "System.Data.SqlClient" }
            , { "Oracle", "System.Data.OracleClient" }
            , { "OleDb", "System.Data.OleDb" } 
            };
            private static DbProviderFactory dataFactory = DbProviderFactories.GetFactory(providers[DatabaseType]);
            public static string CONNECTION_STRING = ConfigurationManager.AppSettings["ConnectionString"];
    
            #region private methods
            private static void AttachParameters(DbCommand command, DbParameter[] parameters)
            {
                if (parameters != null)
                {
                    command.Parameters.AddRange(parameters);
                }
            }
    
            private static DbCommand CreateCommand(object conn)
            {
                DbCommand command = null;
                //If it is just a connection(not a transaction)
                if (conn is DbConnection)
                {
                    command = ((DbConnection)conn).CreateCommand();
                    if (command.Connection.State != ConnectionState.Open)
                    {
                        command.Connection.Open();
                    }
                }
                else //It is a transaction, then join the transaction
                {
                    command = ((DbTransaction)conn).Connection.CreateCommand();
                    command.Transaction = (DbTransaction)conn;
                }
                return command;
            }
    
            private static DbCommand SetupCommand(object conn, CommandType commandType, string strSQLOrSPName, List<MyDBParameter> myDBParameters)
            {
                DbParameter[] parameters = myDBParameters != null ? CreateDBParameters(myDBParameters).ToArray() : null;
                DbCommand command = CreateCommand(conn);
                command.CommandText = strSQLOrSPName;
                command.CommandType = commandType;
                AttachParameters(command, parameters);
                return command;
            }
    
            private static DbParameter CreateDBParameter(string strParameterName, DbType dbType, object value, ParameterDirection direction)
            {
                DbParameter parameter = dataFactory.CreateParameter();
                parameter.ParameterName = strParameterName;
                parameter.DbType = dbType;
                parameter.Value = value;
                parameter.Direction = direction;
                return parameter;
            }
    
            private static List<DbParameter> CreateDBParameters(List<MyDBParameter> myDBParameters)
            {
                List<DbParameter> parameters = new List<DbParameter>();
                foreach (MyDBParameter myDBParameter in myDBParameters)
                {
                    parameters.Add(CreateDBParameter(myDBParameter.strParameterName, myDBParameter.dbType, myDBParameter.value, myDBParameter.parameterDirection));
                }
                return parameters;
            }
            #endregion
    
            public static DbConnection GetConnection()
            {
                DbConnection connection = dataFactory.CreateConnection();
                connection.ConnectionString = CONNECTION_STRING;
                return connection;
            }
    
            public static int ExecuteNonQuery(object conn, CommandType commandType, string strSQLOrSPName, List<MyDBParameter> myDBParameters = null)
            {
                DbCommand command = SetupCommand(conn, commandType, strSQLOrSPName, myDBParameters);
                return command.ExecuteNonQuery();
            }
    
            public static DataSet ExecuteDataset(object conn, CommandType commandType, string strSQLOrSPName, List<MyDBParameter> myDBParameters = null)
            {
                DbCommand command = SetupCommand(conn, commandType, strSQLOrSPName, myDBParameters);
                DbDataAdapter dataAdaptor = dataFactory.CreateDataAdapter();
                DataSet ds = new DataSet();
                dataAdaptor.SelectCommand = command;
                dataAdaptor.Fill(ds);
                return ds;
            }
    
            public static DbDataReader ExecuteReader(object conn, CommandType commandType, string strSQLOrSPName, List<MyDBParameter> myDBParameters = null)
            {
                DbCommand command = SetupCommand(conn, commandType, strSQLOrSPName, myDBParameters);
                return command.ExecuteReader();
            }
    
            public static object ExecuteScalar(object conn, CommandType commandType, string strSQLOrSPName, List<MyDBParameter> myDBParameters = null)
            {
                DbCommand command = SetupCommand(conn, commandType, strSQLOrSPName, myDBParameters);
                return command.ExecuteScalar();
            }
        }
    }

    此代码能支持访问Oracle, sql server, OleDB。用的都是DbConnection之类的。只要开始选择了正确的provider, DbProviderFactories就给创建相应的connection, command等类,就可以顺利地处理这个对应的数据库了。sql的参数是DbType。用来适应数据库类型。MyDBParameter结构封装了参数名,类型,参数值,传入传出方向。目前的版本只考虑了一个数据库连接。连接串只有一个。DbProviderFactory只有一个实例。没有考虑到动态切换连接的情况。如果是要多个连接,得要多个DbProviderFactory的实例。CreateCommand方法里判断了传入的的数据库连接是一个DbConnection还是一个DbTransaction,如果是一个DbTransaction的话,可以加入这个数据库事务。如果只是一个DbConnection则不加入已有的数据库事务,使用自动的数据库事务。

    数据实体类

    using System;
    using System.Collections.Generic;
    
    namespace DataEntity
    {
        public class UserMenuItem
        {
            #region Properties
            public int MenuItemID { get; set; }
            public string MenuItemName { get; set; }
            public int MenuID { get; set; }
            public int Ordinal { get; set; }
            public int Indent { get; set; }
            #endregion
        }
    }

    纯数据的类。这里使用了比较老的c#语法。也可以加上DataAnnotation的标签。可以实现验证数据,也可以加上Display标签,引用资源文件。这个数据实体类在MVC页面里绑定时可以显示想应的label。label的内容来自于资源文件,便于使用多语言的界面。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using Resource.Entity;
    
    namespace DataEntity
    {
        public class UserAccount
        {
            #region Properties
            public int ID { get; set; }
    
            [Required(ErrorMessageResourceType=typeof(Resource.Entity.UserAccount), ErrorMessageResourceName="Common_Required_ErrorMessage")]
            [StringLength(30, ErrorMessageResourceType=typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "NAME_StringLength_ErrorMessage")]
            [RegularExpression(@"[a-zA-Z].*", ErrorMessageResourceType=typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "NAME_RegularExpression_ErrorMessage")]
            [Display(ResourceType=typeof(Resource.Entity.UserAccount), Name="NAME_DisplayName")]
            public string Name { get; set; }
    
            [Required(ErrorMessageResourceType=typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "Common_Required_ErrorMessage")]
            [RegularExpression(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
                , ErrorMessageResourceType=typeof(Resource.Entity.UserAccount), ErrorMessageResourceName="EMAIL_RegularExpression_ErrorMessage")]
            [Display(ResourceType=typeof(Resource.Entity.UserAccount), Name="EMAIL_DisplayName")]
            public string Email { get; set; }
    
            [Display(ResourceType = typeof(Resource.Entity.UserAccount), Name = "PASSWORD_DisplayName")]
            [Required(ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "Common_Required_ErrorMessage")]
            [StringLength(32, ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "PASSWORD_StringLength", MinimumLength = 8)]
            public string Password { get; set; }
    
            [Display(ResourceType = typeof(Resource.Entity.UserAccount), Name = "Balance")]
            public decimal Balance { get; set; }
    
            [Required(ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "Common_Required_ErrorMessage")]
            [Display(ResourceType = typeof(Resource.Entity.UserAccount), Name = "CONFIRMPASSWORD_DisplayName")]
            [Compare("Password", ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "CONFIRMPASSWORD_CompareErrorMessage")]
            public string ConfirmPassword { get; set; }
    
            [Required(ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "Common_Required_ErrorMessage")]
            [Display(ResourceType = typeof(Resource.Entity.UserAccount), Name = "OLDNAME_DisplayName")]
            public string OldName { get; set; }
    
            [Required(ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "Common_Required_ErrorMessage")]
            [Display(ResourceType = typeof(Resource.Entity.UserAccount), Name = "OLDEMAIL_DisplayName")]
            public string OldEmail { get; set; }
    
            [Required(ErrorMessageResourceType = typeof(Resource.Entity.UserAccount), ErrorMessageResourceName = "Common_Required_ErrorMessage")]
            [Display(ResourceType = typeof(Resource.Entity.UserAccount), Name = "OLDPassword_DisplayName")]
            public string OldPassword { get; set; }
            #endregion
        }
    }

    下面是数据访问的代码:

    using System;
    using System.Collections.Generic;
    using System.Data;
    using DataEntity;
    using DataAccessCommon;
    
    namespace DataAccess
    {
        public class DALUserMenuItem
        {
            #region data access methods
    
            public int DeleteUserMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "DELETE FROM [UserMenuItem]   WHERE   [MenuItemID] = @MenuItemID";
    
                int result = 0;
                result = MyStaticDBHelper.ExecuteNonQuery(conn, System.Data.CommandType.Text, strSQL, paras);
                return result;
    
            }
    
            public int UpdateUserMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemName", DbType.String, usermenuitem.MENUITEMNAME),
                    new MyStaticDBHelper.MyDBParameter("@MenuID", DbType.Int32, usermenuitem.MENUID),
                    new MyStaticDBHelper.MyDBParameter("@Ordinal", DbType.Int32, usermenuitem.ORDINAL),
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "UPDATE [UserMenuItem] SET   [MenuItemName] = @MenuItemName,  [MenuID] = @MenuID,  [Ordinal] = @Ordinal  WHERE   [MenuItemID] = @MenuItemID";
    
                int result = 0;
                result = MyStaticDBHelper.ExecuteNonQuery(conn, System.Data.CommandType.Text, strSQL, paras);
                return result;
    
            }
    
            public int AddUserMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemName", DbType.String, usermenuitem.MENUITEMNAME),
                    new MyStaticDBHelper.MyDBParameter("@MenuID", DbType.Int32, usermenuitem.MENUID),
                    new MyStaticDBHelper.MyDBParameter("@Ordinal", DbType.Int32, usermenuitem.ORDINAL),
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "INSERT INTO [UserMenuItem] (  [MenuItemName] ,  [MenuID] ,  [Ordinal]  ) VALUES( @MenuItemName, @MenuID, @Ordinal );  SELECT SCOPE_IDENTITY() as [MenuItemID]";
    
                int result = 0;
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL, paras);
                if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
                    usermenuitem.MENUITEMID = Convert.ToInt32(ds.Tables[0].Rows[0][0]);
                    result = 1;
                }
                return result;
    
            }
    
            public List<UserMenuItem> GetAllUserMenuItem(Object conn)
            {
    
                string strSQL = "SELECT * FROM [UserMenuItem] ";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL);
    
                return DataMapper.MapDataTableToObjectList<UserMenuItem>(ds.Tables[0]);
    
            }
    
            public UserMenuItem FindAUserMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "SELECT * FROM [UserMenuItem]   WHERE   [MenuItemID] = @MenuItemID";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return DataMapper.MapDataTableToSingleRow<UserMenuItem>(ds.Tables[0]);
    
            }
    
            public System.Int32 SelectCountUserMenuItem(Object conn)
            {
    
                string strSQL = "SELECT COUNT(1) AS Count FROM [UserMenuItem] ";
    
                Object obj = null;
                obj = MyStaticDBHelper.ExecuteScalar(conn, System.Data.CommandType.Text, strSQL);
                return (System.Int32)obj;
    
            }
    
            public System.Int32 SelectCountWhereClauseUserMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "SELECT COUNT(1) AS Count FROM [UserMenuItem]   WHERE   [MenuItemID] = @MenuItemID";
    
                Object obj = null;
                obj = MyStaticDBHelper.ExecuteScalar(conn, System.Data.CommandType.Text, strSQL, paras);
                return (System.Int32)obj;
    
            }
    
            public List<UserMenuItem> SelectTopUserMenuItem(Object conn)
            {
    
                string strSQL = "SELECT Top 50 *  FROM [UserMenuItem] ";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL);
    
                return DataMapper.MapDataTableToObjectList<UserMenuItem>(ds.Tables[0]);
    
            }
    
            public List<UserMenuItem> SelectOrderByPrimaryKeyUserMenuItem(Object conn)
            {
    
                string strSQL = "SELECT *  FROM [UserMenuItem] ORDER BY [MenuItemID] , [MenuItemName] , [MenuID] , [Ordinal]";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL);
    
                return DataMapper.MapDataTableToObjectList<UserMenuItem>(ds.Tables[0]);
    
            }
    
            public List<UserMenuItem> SelectGroupByPrimaryKeyUserMenuItem(Object conn)
            {
    
                string strSQL = "SELECT * FROM [UserMenuItem] GROUP BY [MenuItemID] , [MenuItemName] , [MenuID] , [Ordinal]";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL);
    
                return DataMapper.MapDataTableToObjectList<UserMenuItem>(ds.Tables[0]);
    
            }
    
            public List<UserMenuItem> SelectUserMenuItemsByMenuID(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuID", DbType.Int32, usermenuitem.MENUID)
                };
                
                string strSQL = "SELECT * FROM [UserMenuItem] WHERE [MenuID] = @MenuID ORDER BY [Ordinal]";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return DataMapper.MapDataTableToObjectList<UserMenuItem>(ds.Tables[0]);
            }
    
            public UserMenuItem SelectPreviousMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuID", DbType.Int32, usermenuitem.MENUID),
                    new MyStaticDBHelper.MyDBParameter("@Ordinal", DbType.Int32, usermenuitem.ORDINAL)
                };
    
                string strSQL = "SELECT TOP 1 * FROM [UserMenuItem] WHERE [MenuID] = @MenuID AND [Ordinal] < @Ordinal ORDER BY [Ordinal] DESC";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return DataMapper.MapDataTableToSingleRow<UserMenuItem>(ds.Tables[0]);
            }
    
            public UserMenuItem SelectNextMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuID", DbType.Int32, usermenuitem.MENUID),
                    new MyStaticDBHelper.MyDBParameter("@Ordinal", DbType.Int32, usermenuitem.ORDINAL)
                };
    
                string strSQL = "SELECT TOP 1 * FROM [UserMenuItem] WHERE [MenuID] = @MenuID AND [Ordinal] > @Ordinal ORDER BY [Ordinal] ASC";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return DataMapper.MapDataTableToSingleRow<UserMenuItem>(ds.Tables[0]);
            }
    
            public int MoveLeftMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "UPDATE [UserMenuItem] SET [Indent] = CASE WHEN [Indent] - 1 >= 0 THEN [Indent] - 1 ELSE 0 END WHERE [MenuItemID] = @MenuItemID";
    
                int iResult = 0;
                iResult = MyStaticDBHelper.ExecuteNonQuery(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return iResult;
            }
    
            public int MoveRightMenuItem(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuItemID", DbType.Int32, usermenuitem.MENUITEMID)
                };
    
                string strSQL = "UPDATE [UserMenuItem] SET [Indent] = CASE WHEN [Indent] + 1 <= 2 THEN [Indent] + 1 ELSE 2 END WHERE [MenuItemID] = @MenuItemID";
    
                int iResult = 0;
                iResult = MyStaticDBHelper.ExecuteNonQuery(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return iResult;
            }
    
            public UserMenuItem SelectMaxOrdinal(Object conn, UserMenuItem usermenuitem)
            {
                List<MyStaticDBHelper.MyDBParameter> paras = new List<MyStaticDBHelper.MyDBParameter> {
                    new MyStaticDBHelper.MyDBParameter("@MenuID", DbType.Int32, usermenuitem.MENUID)
                };
    
                string strSQL = "SELECT IsNull(Max(Ordinal),0) as Ordinal FROM [UserMenuItem] WHERE [MenuID] = @MenuID";
    
                DataSet ds = null;
                ds = MyStaticDBHelper.ExecuteDataset(conn, System.Data.CommandType.Text, strSQL, paras);
    
                return DataMapper.MapDataTableToSingleRow<UserMenuItem>(ds.Tables[0]);
            }
            #endregion
        }
    }

    这个数据访问的代码都是这种方式,开始准备参数。用的都是MyStaticDBHelper.MyDBParameter结构。给出参数名,参数类型,参数值和参数方向(输入还是输出)。然后就是一个sql语句,其中有参数。之后是根据查询的类型,update/delete/insert的就调用db helper的ExecuteNonQuery方法,如果是select一个表的话,调用db helper的ExecuteDataset方法。再之后,就要将返回的值给转换成对应的返回类型。如一个实体对象或者实体对象列表。这个类里的sql语句都是预先设计好的sql语句,每个sql语句都有参数,然后每个sql查询都有一个c#方法与之对应。DataMapper.MapDataTableToSingleRow是将DataTable中第一行转换成一个实体对象。DataMapper.MapDataTableToObjectList是将返回的DataTable转换成实体类的列表, 即List<实体类>,这里DataMapper类采用了Reflection的方式来进行转换实体类。虽然不是最快的。在某些情况下也可以接受。我们做过一个实例程序来对比,将DataTable转成实体类列表,有直接赋值,Emit, Reflection, delegate, Expression tree等不同的方法,经过性能测试,直接赋值是最快的。Emit稍微比直接赋值慢,但是已经很快了。直接赋值写代码比较繁琐。Emit的方法代码稍微有点复杂,但是运行效率不错。又比较灵活。是个相当好的方法,Emit和Expresssion Tree的方法有一些缺点,就是很难调试,万一出现问题会很难找到问题的根源。这里是比较不同方法将DataTable转成数据实体类的测试代码:  https://files.cnblogs.com/mikelij/testGenerateEntity.zip, 大家可以下载了去试试,应该说这几种方法都还不错。这里的代码选用了Reflection方法。因为Reflection也没有慢得很多。Reflection方法的兼容性好。不会出问题。Emit和Express tree方法经常会遇到不兼容类型的问题。而且很难排查问题。

     数据映射的类

    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections.Generic;
    using System.Reflection;
    
    namespace DataAccessCommon
    {
        public class DataMapper
        {
    
            public static List<TType> MapDataTableToObjectList<TType>(DataTable dt) where TType : new()
            {
                List<TType> result = new List<TType>();
                foreach (DataRow currentRow in dt.Rows)
                {
                    TType ttype = new TType();
                    for (int i = 0; i < dt.Columns.Count; i++)
                    {
                        for (int j = 0; j < ttype.GetType().GetProperties().Length; j++)
                        {
                            if (dt.Columns[i].ColumnName.ToUpper() == ttype.GetType().GetProperties()[j].Name.ToUpper())
                            {
                                ttype.GetType().GetProperties()[j].SetValue(ttype, currentRow[i], null);
                                break;
                            }
                        }
                    }
                    result.Add(ttype);
                    ttype = new TType();
                }
                return result;
            }
    
            public static TType MapDataTableToSingleRow<TType>(DataTable dt) where TType : new()
            {
                TType ttype = new TType();
                if (dt.Rows.Count > 0)
                {
                    DataRow currentRow = dt.Rows[0];
                    for (int i = 0; i < dt.Columns.Count; i++)
                    {
                        for (int j = 0; j < ttype.GetType().GetProperties().Length; j++)
                        {
                            if (dt.Columns[i].ColumnName.ToUpper() == ttype.GetType().GetProperties()[j].Name.ToUpper())
                            {
                                ttype.GetType().GetProperties()[j].SetValue(ttype, currentRow[i], null);
                                break;
                            }
                        }
                    }
                }
                return ttype;
            }
        }
    }

    商业类的代码

    商业类的代码是基于我们OOA/OOD设计出的。比如一个银行ATM的例子,其业务里有若干名词,比如银行户头,ATM机等名词,每个名词下又有若干属性,比如银行帐号,帐号所有者名字,开立日期等,ATM机有ATM机号,地理位置,所属银行编号,等等。围绕着这些名词,有相关的一些动作。比如取钱,存钱,插卡入ATM机,记录ATM流水。等等等等。这里已经将名词的数据属性放到了数据实体类里。这些数据实体类里就只有那些名词的数据属性,没有那些动作,即一个纯数据的类。这里要提到的商业类包含了商业方法的类,这些商业方法就对应着那些动作。比如取钱,就有一个Withdraw方法对应。存钱就就一个Deposite方法对应。这两个方法都放在一个叫BankAccount的的商业类里面。这里用的银行的例子,说明这里所用到的设计方法。

    使用Unity之类的IOC容器进行policy injection

    下面就要说说IOC了,就是说我们设计一个商业类,里面有几个商业方法。如果让调用者直接依赖于这个商业类,那么将来有一天要改变这些商业方法时,可能就不得不同时改调用者和商业类。为了避免这种情况,我们可以从商业类提取出一个接口。这个接口只有这些动作的名字,没有具体具体实现。然后由负责具体实现的商业类来实现这些接口。说了这些东西与IOC有什么关系呢?这样做正是为了实现IOC打下基础。要知道象Unity这样的IOC容器,都是负责创建对象。它负责从接口映射到具体的商业类。当调用者需要创建一个接口的实例,接口本身是不能实例化的,容器会为调用者创建一个实现了该接口商业类的实例。

    一个商业接口的例子:

    using System;
    namespace BusinessLogic
    {
        [MyDBHandler]
        public interface IBLLUserMenu
        {
            int AddUserMenu(DataEntity.UserMenu usermenu);
            int DeleteUserMenu(DataEntity.UserMenu usermenu);
            DataEntity.UserMenu FindAUserMenu(DataEntity.UserMenu usermenu);
            System.Collections.Generic.List<DataEntity.UserMenu> GetAllUserMenu();
            int SelectCountUserMenu();
            int SelectCountWhereClauseUserMenu(DataEntity.UserMenu usermenu);
            System.Collections.Generic.List<DataEntity.UserMenu> SelectGroupByPrimaryKeyUserMenu();
            System.Collections.Generic.List<DataEntity.UserMenu> SelectMenusByApplicationID(DataEntity.UserMenu usermenu);
            System.Collections.Generic.List<DataEntity.UserMenu> SelectOrderByPrimaryKeyUserMenu();
            System.Collections.Generic.List<DataEntity.UserMenu> SelectTopUserMenu();
            int UpdateUserMenu(DataEntity.UserMenu usermenu);
            object CONNECTION { get; set; }
            DataEntity.UserMenu USERMENU { get; set; }
            System.Collections.Generic.List<DataEntity.UserMenu> USERMENU_LIST { get; set; }
        }
    }

    此代码中的MyDBHandler是一个字定义的attribute, 用于Unity来进行拦截判断。有这个attribute就拦截,没有就不拦截。
    而相应的商业类就是这样的:

    using System;
    using System.Collections.Generic;
    using System.Data;
    using DataEntity;
    using DataAccess;
    using DataAccessCommon;
    using CommonUtil;
    
    namespace BusinessLogic
    {
        internal class BLLUserMenu : BusinessLogic.IBLLUserMenu
        {
            private readonly DataAccess.DALUserMenu dal = new DataAccess.DALUserMenu();
            private object conn = null;
            private UserMenu usermenu;
            private List<UserMenu> usermenus;
    
            public object CONNECTION
            {
                get
                {
                    return conn;
                }
                set
                {
                    conn = value;
                }
            }
            
            public UserMenu USERMENU
            {
                get
                {
                    return usermenu;
                }
                set
                {
                    usermenu = value;
                }
            }
            
            public List<UserMenu> USERMENU_LIST
            {
                get
                {
                    return usermenus;
                }
                set
                {
                    usermenus = value;
                }
            }
            
            #region business logic method
    
            public int DeleteUserMenu(UserMenu usermenu)
            {
                return dal.DeleteUserMenu(conn,usermenu);
            }
    
            public int UpdateUserMenu(UserMenu usermenu)
            {
                return dal.UpdateUserMenu(conn,usermenu);
            }
    
            public int AddUserMenu(UserMenu usermenu)
            {
                return dal.AddUserMenu(conn,usermenu);
            }
    
            public List<UserMenu> GetAllUserMenu()
            {
                return dal.GetAllUserMenu(conn);
            }
    
            public UserMenu FindAUserMenu(UserMenu usermenu)
            {
                return dal.FindAUserMenu(conn,usermenu);
            }
    
            public System.Int32 SelectCountUserMenu()
            {
                return dal.SelectCountUserMenu(conn);
            }
    
            public System.Int32 SelectCountWhereClauseUserMenu(UserMenu usermenu)
            {
                return dal.SelectCountWhereClauseUserMenu(conn,usermenu);
            }
    
            public List<UserMenu> SelectTopUserMenu()
            {
                return dal.SelectTopUserMenu(conn);
            }
    
            public List<UserMenu> SelectOrderByPrimaryKeyUserMenu()
            {
                return dal.SelectOrderByPrimaryKeyUserMenu(conn);
            }
    
            public List<UserMenu> SelectGroupByPrimaryKeyUserMenu()
            {
                return dal.SelectGroupByPrimaryKeyUserMenu(conn);
            }
    
            public List<UserMenu> SelectMenusByApplicationID(UserMenu usermenu)
            {
                return dal.SelectMenusByApplicationID(conn, usermenu);
            }
            #endregion
        }
    }

    目前这个商业类的方法都比较简单,如果有比较复杂的,可能一个商业方法里需要调用数据访问的方法好多个,在做一些逻辑判断。那么这些商业方法就可以变得复杂多了。如这样的一个商业方法:

            public bool MoveUpItem(UserMenuItem usermenuitem)
            {
                usermenuitem = dal.FindAUserMenuItem(conn, usermenuitem);
                UserMenuItem previousMenuItem = dal.SelectPreviousMenuItem(conn, usermenuitem);
                int iTempOrdinal = usermenuitem.Ordinal;
                usermenuitem.Ordinal = previousMenuItem.Ordinal;
                previousMenuItem.Ordinal = iTempOrdinal;
                dal.UpdateUserMenuItem(conn, usermenuitem);
                dal.UpdateUserMenuItem(conn, previousMenuItem);
                return true;
            }

    Unity配置信息:

      <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
        <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
        <namespace name="BusinessLogic" />
        <container name="myContainer">
          <extension type="Interception" />
          <register type="BusinessLogic.IBLLApplication, BusinessLogic" mapTo="BusinessLogic.BLLApplication, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLDomain, BusinessLogic" mapTo="BusinessLogic.BLLDomain, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLFormElement, BusinessLogic" mapTo="BusinessLogic.BLLFormElement, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserAccount, BusinessLogic" mapTo="BusinessLogic.BLLUserAccount, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserColumns, BusinessLogic" mapTo="BusinessLogic.BLLUserColumns, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserForm, BusinessLogic" mapTo="BusinessLogic.BLLUserForm, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserMenu, BusinessLogic" mapTo="BusinessLogic.BLLUserMenu, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserMenuItem, BusinessLogic" mapTo="BusinessLogic.BLLUserMenuItem, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserSession, BusinessLogic" mapTo="BusinessLogic.BLLUserSession, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <register type="BusinessLogic.IBLLUserTables, BusinessLogic" mapTo="BusinessLogic.BLLUserTables, BusinessLogic">
            <interceptor name="myinterceptor" type="InterfaceInterceptor" isDefaultForType="true" />
            <policyInjection />
          </register>
          <interception>
            <policy name="mypolicy">
              <callHandler name="myHandler1" type="BusinessLogic.MyDBHandler, BusinessLogic"></callHandler>
              <matchingRule name="myrule" type="CustomAttributeMatchingRule">
                <constructor>
                  <param name="attributeType" type="System.Type, mscorlib">
                    <value value="BusinessLogic.MyDBHandlerAttribute, BusinessLogic" typeConverter="BusinessLogic.GetTypeConverter, BusinessLogic" />
                  </param>
                  <param name="inherited" type="bool">
                    <value value="true" />
                  </param>
                </constructor>
              </matchingRule>
            </policy>
          </interception>
        </container>
      </unity>

    注意到这些register的节点没有?这些节点实现了接口到具体商业类的映射。接口表示的是一个抽象。它只有方法的声明,没有具体实现。在调用者需要一个具体的实现了这个接口的商业类时,容器帮助我们创建这个商业类的实例,而接口到商业类的映射就是在Unity配置文件里做的。
    Unity除了帮助我们实现接口到商业类的映射,还可以帮助我们实现aop. 比如log, db transaction, exception handling.

    using System;
    using System.Data;
    using System.Data.Common;
    using System.Collections.Generic;
    using Microsoft.Practices.Unity.InterceptionExtension;
    using DataAccessCommon;
    using CommonUtil;
    
    namespace BusinessLogic
    {
        public class MyDBHandler : ICallHandler
        {
            private int iOrder = 0;
    
            public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
            {
                var retvalue = getNext()(input, getNext); // call the intercepting method
                if (retvalue.Exception != null)
                {
                    SysLog.GetInstance().LogError(retvalue.Exception);
                }
                return retvalue;
            }
    
            public int Order
            {
                get
                {
                    return iOrder;
                }
                set
                {
                    iOrder = value;
                }
            }
        }
    }

    这个MyDBHandler已经在之前的Unity配置中指定了。即这句:

    <callHandler name="myHandler1" type="BusinessLogic.MyDBHandler, BusinessLogic"></callHandler>

    这句是去调用被拦截的方法:

    retvalue = getNext()(input, getNext);

    被拦截方法(即我们的商业方法)返回以后,程序就检查retvalue.Exception有没有出错,有就调用logging的类来写log。将出错信息完整地打印出来。
    自定义的attribute类:

    using System;
    using System.Collections.Generic;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.InterceptionExtension;
    namespace BusinessLogic
    {
        public class MyDBHandlerAttribute : HandlerAttribute
        {
            public override ICallHandler CreateHandler(IUnityContainer container)
            {
                return new MyDBHandler();
            }
        }
    }

    至于db transaction, 如果数据库事务比较简单,可以用TransactionScope,前面的MyDBHandler的invoke方法就替换成这样。

                    using (TransactionScope ts = new TransactionScope())
                    {
                        var retvalue = getNext().Invoke(input, getNext);
                        if (retvalue.Exception != null)
                        {
                            SysLog.GetInstance().LogError(retvalue.Exception);
                        }
                        else
                        {
                            ts.Complete();
                        }
                        return retvalue
                    }

    Unity配置里用到的一个工具类代码:

    using System;
    using System.Collections.Generic;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.InterceptionExtension;
    namespace BusinessLogic
    {
        public class GetTypeConverter : System.ComponentModel.TypeConverter
        {
            public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context,
                System.Globalization.CultureInfo culture,
                object value)
            {
                return Type.GetType(value.ToString());
            }
        }
    }

    这个类用来做类型转换的。用来帮助Unity来找自定义的MyDBHandlerAttribute的。

    商业类的调用者

    为了调用商业类,我们有一个类来帮助创建相应的商业类的实例:

    using System;
    using System.Collections.Generic;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.Configuration;
    
    namespace BusinessLogic
    {
        public static class BusinessClassCreator
        {
            public static IUnityContainer container = new UnityContainer().LoadConfiguration("myContainer");
    
            public static T GetInstance<T>()
            {
                return (T)container.Resolve(typeof(T), null);
            }
    
            public static object GetInstance(Type type)
            {
                return container.Resolve(type, null);
            }
        }
    }

    在调用的地方代码这么写:

            [HttpGet]
            public ActionResult SaveMenuScheme()
            {
                UserMenu userMenu = new UserMenu();
                userMenu.MenuID = GetMenuID(this);
                userMenu = BusinessClassCreator.GetInstance<IBLLUserMenu>().FindAUserMenu(userMenu);
                short bMenuScheme = 0;
                bMenuScheme = (short)DesignTableController.GetID(this);
                userMenu.Scheme = bMenuScheme;
                BusinessClassCreator.GetInstance<IBLLUserMenu>().UpdateUserMenu(userMenu);
                return DisplayMenuList();
            }

    这是一个在asp.net mvc中调用上述商业类的样例代码,首先通过BusinessClassCreator.GetInstance<IBLLUserMenu>()得到接口IBLLUserMenu对应的商业类对象,然后再调用IBLLUserMenu接口上的方法。比如此例中调用了FindUserMenu方法和UpdateUserMenu方法。每个方法的返回类型已经由接口定义好。我们只要按照这个接口的定义来使用这个接口就可以了。接口在这里的好处就是它定义了商业类的规范,所有实现此接口的商业类都符合此接口的规范。而且具体实现和接口定义是分离的。这样我们就可以单独改变实现接口的商业类。商业类的调用者既可以是一个asp.net mvc的程序,也可以是一个asp.net web form的,还可以是一个winform程序。

     demo代码下载: http://dl.vmall.com/c08haaatpu, 博客园这里上传不了。没有办法。只能选别处了。

  • 相关阅读:
    POJ 3140 Contestants Division (树dp)
    POJ 3107 Godfather (树重心)
    POJ 1655 Balancing Act (树的重心)
    HDU 3534 Tree (经典树形dp)
    HDU 1561 The more, The Better (树形dp)
    HDU 1011 Starship Troopers (树dp)
    Light oj 1085
    Light oj 1013
    Light oj 1134
    FZU 2224 An exciting GCD problem(GCD种类预处理+树状数组维护)同hdu5869
  • 原文地址:https://www.cnblogs.com/mikelij/p/3059297.html
Copyright © 2011-2022 走看看