zoukankan      html  css  js  c++  java
  • 可扩展的单据编号生成器 + 简单的解释器

    可扩展的单据编号生成器 + 简单的解释器

    背景

    在企业应用中单据编号的自定义是一个很常见的需求,能不能抽象一个通用的框架呢?之前写个一篇自定义密码强度的博文,感觉他们两个思路应该很相似。就让我们试试吧。

    思路

    这里的难点在于实现"解释器",比如将"前缀_<日期:yyyy_MM_dd>"解释为“工号生成器”,而且“解释器”的“规则”允许动态增加。

     

    实现

    代码下载

    类图

    核心代码

    CodeRuleGenerator.cs

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 using System.Text.RegularExpressions;
     8 
     9 namespace EntityCodeRuleDemo
    10 {
    11     public sealed class CodeRuleGenerator : ICodeRuleGenerator
    12     {
    13         private readonly IEnumerable<ICodeRuleProvider> _providers = new List<ICodeRuleProvider>();
    14 
    15         internal CodeRuleGenerator(IEnumerable<ICodeRuleProvider> providers)
    16         {
    17             _providers = providers;
    18         }
    19 
    20         public string Generate(object entity)
    21         {
    22             var sb = new StringBuilder();
    23 
    24             foreach (var provider in _providers)
    25             {
    26                 sb.Append(provider.Generate(entity));
    27             }
    28 
    29             return sb.ToString();
    30         }
    31     }
    32 }
    复制代码

    CodeRuleInterpreter.cs

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 using System.Text.RegularExpressions;
     8 
     9 using EntityCodeRuleDemo.RuleProviders;
    10 
    11 namespace EntityCodeRuleDemo
    12 {
    13     public static class CodeRuleInterpreter
    14     {
    15         private static Dictionary<Regex, Func<string, ICodeRuleProvider>> _providerFactorys = new Dictionary<Regex, Func<string, ICodeRuleProvider>>();
    16 
    17         static CodeRuleInterpreter()
    18         {
    19             SetProviderFactory(new Regex("^[^<].*?[^>]?$"), LiteralRuleProvider.LiteralRuleProviderFactory);
    20             SetProviderFactory(new Regex("^<日期(:(?<格式>.*?))?>$"), DateRuleProvider.DateRuleProviderFactory);
    21             SetProviderFactory(new Regex("^<属性(:(?<名称>.*?))?>$"), PropertyRuleProvider.PropertyRuleProviderFactory);
    22         }
    23 
    24         public static void SetProviderFactory(Regex regex, Func<string, ICodeRuleProvider> providerFactory)
    25         {
    26             _providerFactorys[regex] = providerFactory;
    27         }
    28 
    29 
    30         public static ICodeRuleGenerator Interpret(string codeRule)
    31         {
    32             var providers = GetProviders(codeRule);
    33 
    34             return new CodeRuleGenerator(providers);
    35         }
    36 
    37         private static IEnumerable<ICodeRuleProvider> GetProviders(string codeRule)
    38         {
    39             var literals = codeRule.Replace("<", "$<").Replace(">", ">$").Split('$');
    40 
    41             return literals
    42                 .Where(x => !string.IsNullOrEmpty(x))
    43                 .Select(GetProvider)
    44                 .ToList();
    45         }
    46 
    47         private static ICodeRuleProvider GetProvider(string literal)
    48         {
    49             var providerFactory = _providerFactorys
    50                 .FirstOrDefault(x => x.Key.IsMatch(literal))
    51                 .Value;
    52 
    53             if (providerFactory == null)
    54             {
    55                 throw new FormatException("格式化错误");
    56             }
    57 
    58             return providerFactory(literal);
    59         }
    60     }
    61 }
    复制代码

    Program.cs

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace EntityCodeRuleDemo
     8 {
     9     class Program
    10     {
    11         static void Main(string[] args)
    12         {
    13             var employeeCode = CodeRuleInterpreter
    14                 .Interpret("前缀_<日期:yyyy_MM_dd>_<属性:NamePinYin>")
    15                 .Generate(new Employee { NamePinYin = "DUANGW" });
    16 
    17             Console.WriteLine(employeeCode);
    18         }
    19     }
    20 
    21     class Employee
    22     {
    23         public string NamePinYin { get; set; }
    24         public string EmployeeCode { get; set; }
    25     }
    26 }
    复制代码

    运行效果

    备注

    按照这种思路,基本上能满足企业应用的多数编码规则要求。在真实的项目中,这些规则是要持久化到数据库的,这样就可以做到运行时动态的修改规则了。

    可扩展的单据编号生成器 之 顺序号(防止重复)

    背景

    我在上篇文章“.NET:可扩展的单据编号生成器 + 简单的解释器”中介绍了一个简单的单据编号框架。有朋友留言问如何实现“顺序号,且不能重复”,本篇文章就针对这个问题用上篇介绍的框架进行实现。

    思路

    顺序号 = 上次顺序号 + 步长

    根据上面的公式,问题可以化解为:如何获取上次顺序号?获取上次顺序号有两种方式:

      1. 扫描单据表,找出最新的一条记录。
      2. 引入种子表,种子表记录了最新的顺序号。

    因为生成的顺序号不能重复,这里就有了并发的要求,为了最大限度的提高并发性,我选择2(引入种子表)。

    并发处理可以选择:悲观锁或乐观锁,这里为了简单,我选择悲观锁

    实现

    代码下载:http://yunpan.cn/Q5KMUTA3qGPct

    种子表设计

    复制代码
    1 CREATE TABLE [dbo].[CodeSeeds] (
    2     [Id]    UNIQUEIDENTIFIER NOT NULL,
    3     [Key]   NVARCHAR (500)   NOT NULL,
    4     [Value] INT              NOT NULL,
    5     PRIMARY KEY CLUSTERED ([Id] ASC)
    6 );
    复制代码

    SeedCodeRuleProvider.cs

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 using System.Transactions;
     8 using System.Text.RegularExpressions;
     9 using System.Data.Entity.Infrastructure;
    10 
    11 namespace EntityCodeRuleDemo
    12 {
    13     public class SeedCodeRuleProvider : ICodeRuleProvider
    14     {
    15         private readonly int _width;
    16 
    17         public SeedCodeRuleProvider(int width)
    18         {
    19             _width = width;
    20         }
    21 
    22         public string Generate(object entity)
    23         {
    24             return GetSeedValue(entity).ToString().PadLeft(_width, '0');
    25         }
    26 
    27         protected virtual string GetKey(object entity)
    28         {
    29             return entity.GetType().FullName;
    30         }
    31 
    32         private int GetSeedValue(object entity)
    33         {
    34             try
    35             {
    36                 var value = 0;
    37                 var key = this.GetKey(entity);
    38 
    39                 using (var ts = new TransactionScope(TransactionScopeOption.RequiresNew,
    40                     new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
    41                 {
    42                     using (var context = new TestContext())
    43                     {
    44                         var seed = context.CodeSeeds.Where(x => x.Key == key).FirstOrDefault();
    45                         if (seed == null)
    46                         {
    47                             seed = new CodeSeed { Id = Guid.NewGuid(), Key = key, Value = -1 };
    48                             context.CodeSeeds.Add(seed);
    49                         }
    50 
    51                         seed.Value++;
    52                         value = seed.Value;
    53                         context.SaveChanges();
    54                     }
    55 
    56                     ts.Complete();
    57                 }
    58 
    59                 return value;
    60             }
    61             catch (DbUpdateException)
    62             {
    63                 return this.GetSeedValue(entity);
    64             }
    65         }
    66 
    67         public static SeedCodeRuleProvider SeedCodeRuleProviderFactory(string literal)
    68         {
    69             var match = new Regex("^<种子(:(?<宽度>.*?))?>$").Match(literal);
    70 
    71             var width = match.Groups["宽度"].Value;
    72 
    73             return new SeedCodeRuleProvider(string.IsNullOrEmpty(width) ? 5 : int.Parse(width));
    74         }
    75     }
    76 }
    复制代码

    Program.cs

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 using System.Text.RegularExpressions;
     8 
     9 namespace EntityCodeRuleDemo
    10 {
    11     class Program
    12     {
    13         static void Main(string[] args)
    14         {
    15             CodeRuleInterpreter.RegistProviderFactory(new Regex("^<种子(:(?<宽度>.*?))?>$"), SeedCodeRuleProvider.SeedCodeRuleProviderFactory);
    16 
    17             var employeeCode = CodeRuleInterpreter
    18                 .Interpret("前缀_<日期:yyyy_MM_dd>_<属性:NamePinYin>_<种子:6>")
    19                 .Generate(new Employee { NamePinYin = "DUANGW" });
    20 
    21             Console.WriteLine(employeeCode);
    22         }
    23     }
    24 }
    复制代码

    运行结果

    备注

    有写业务要求会强制编号的连续性或编号的随机性,对于这两种需求,还需要单独开发Provider,有机会再写文章介绍了。

    现在可选的框架

        现在我们开发一个.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, 博客园这里上传不了。没有办法。只能选别处了。

  • 相关阅读:
    自己的思考
    spring MVC整合freemarker
    手把手教你搭建SpringMVC——最小化配置
    深入hibernate的三种状态
    maven 构建slf4j1.7.7之简单测试与源码解析
    maven 构建slf4j1.7.7之简单测试与源码解析
    (转)URI和URL的区别
    Spring缓存机制的理解
    (转)oracle 高水位线详解
    (转)PL/SQL Developer使用技巧、快捷键
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3074888.html
Copyright © 2011-2022 走看看