2011年11月22日

使用Action表驱动代替switch…case语句

大家都知道,代码中出现过多的if…else嵌套语句时很难读,所以常用switch…case语句进行代替,代码的可读性就好了很多。

举个简单的例子:

01 public void DoSomeThing(string str)
02     {
03       if (str == "A") {
04         DoSomethingForA(str);
05       }
06       else if (str == "B") {
07         DoSomethingForB(str);
08       }
09       else if (str == "C") {
10         DoSomethingForC(str);
11       }
12     }
1 我们一眼就看出,这段代码可以使用switch语句进行重构,重构后的代码如下:
01 public void DoSomeThing(string str)
02     {
03       switch (str) {
04         case "A":
05           DoSomethingForA(str);
06           break;
07         case "B":
08           DoSomethingForB(str);
09           break;
10         case "C":
11           DoSomethingForC(str);
12           break;
13         default:
14           break;
15       }
16     }
1 这样看下去,代码确实清晰了很多。这种重构方法我们很多人都会这么做,而且效果还不错。上面的例子中代码行还很少,实际当中有可能会出现很多的case语句,
1 即使我们这样重构,在阅读的时候还是需要拉很长的滚动条才可以看到需要找的case语句对应的代码。所以,针对这种情况,我们还可以用更简洁的方法进行重构。
1 实现起来也挺简单的,即:建立全局的字典类型的变量,保存不同的case语句条件以及对应的方法,这样,在需要调用的时候直接查找到该方法,invoke即可,其实
1 也是用了委托来实现。看以下代码:
1 private static Dictionary<string, Action<string>> dict = new Dictionary<string, Action<string>>();
2       
3     public BuildDictionary()
4     {
5       dict.Add("A", DoSomethingForA);
6       dict.Add("B", DoSomethingForB);
7       dict.Add("C", DoSomethingForC);
8     }
1 上面是声明变量及填充字典,做好这部分的工作之后,调用起来就方便多了,同样的DoSomeThing方法,代码就少了很多
1 public void DoSomeThing(string str)
2     {
3       if (dict.ContainsKey(str)) {
4             Action<string> action = dict[str];
5             action.Invoke(str);
6       }
7     }

 

重构之后,是不是简洁了很多,即使再多的case语句,我们也只需要在BuildDictionary方法中把它添加进去就可以了。

那么,是不是所有的switch…case语句都可以这样重构呢,以这样的实现方式来看,需要case条件对应的方法参数类型相同,这样才容易构造出字典来保存。

如果方法还要返回值,那我们就不能用Action,需要用Func表达式来保存,因为它可以带有返回值。

以上,希望对大家有所帮助。

posted @ 2011-11-22 23:59 Amos.Liu 阅读(16) 评论(0) 编辑

6_CSLA.Net3.6中CommandBase类实践 .

CommandBase用来建立一个命令对象,就是执行一个批处理的命令来完成一个业务逻辑。它可以运行在客户端和服务器端。UI可以直接调用Command对象的方法。书中给的使用例子为判定业务对象是否存在,只有单一的功能。我为了把和此业务对象有关的所有操作都放到此类中,可能的操作有,根据属性判定业务对象是否存在、删除业务对象前的数据完整性检查、成批更新业务对象集合等。为此我采用了和ReadOnlyListBase相同的方法,增加一个操作类型,在调用参数中使用ReadOnlyBase的业务对象,这样可以传递尽可能多的参数,如果参数超出这个范围,你在特殊处理参数的传递和状态结果的返回。状态结果的返回采用公开多个属性的方式来实现。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Csla;

using Csla.Data;

using Csla.Security;

using Csla.Validation;

using CAC.SysPrivilege.DAL;

using CAC.SysPrivilege.DAL.DTO;

namespace CAC.SysPrivilege.BusinessLibrary

{

    [Serializable()]

    public class SHOP_INFOCmd : CommandBase

    {

        /// <summary>

        /// 操作类型枚举定义

        /// </summary>

        private enum OperationType { Exists, DeleteCheck };

        private OperationType _operationType;

        private string _message;

        private bool _exists;

        private SHOP_INFOInfo _shopInfo;

 

        /// <summary>

        /// 传入操作类型,和必要的参数,采用业务对象来做参数,根据不同的操作类型来使用业务对象的不同属性

        /// </summary>

        /// <param name="operationType"></param>

        /// <param name="shopInfo"></param>

        private SHOP_INFOCmd(OperationType operationType,SHOP_INFOInfo shopInfo)

        {

            _operationType = operationType;

            _shopInfo = shopInfo;

        }

 

        /// <summary>

        /// 对象公开的属性,只有在对应的方法调用后,访问这些属性才会有实际意义

        /// </summary>

        public bool ShopInfoExists

        {

            get { return _exists; }

        }

        public string Message

        {

            get{return _message;}

        }

 

        #region Static 对外公开的方法

        /// <summary>

        /// 指定的单位代码是否存在

        /// </summary>

        /// <param name="shopId"></param>

        /// <returns></returns>

        public static bool Exists(string shopId)

        {

            //调用 DataPortal.Execute数据访问门户,先构造一个SHOP_INFOCmd对象,SHOP_INFOCmd对象需要操作类型和SHOP_INFOInfo对象

            var shop = DataPortal.Execute<SHOP_INFOCmd>(new SHOP_INFOCmd(OperationType.Exists,new SHOP_INFOInfo() { SHOP_ID = shopId }));           

            return shop.ShopInfoExists;

        }

 

        /// <summary>

        /// 删除单位前的数据检查

        /// </summary>

        /// <param name="shopId"></param>

        /// <returns></returns>

        public static string DeleteCheck(string shopId)

        {

            var shop = DataPortal.Execute<SHOP_INFOCmd>(new SHOP_INFOCmd(OperationType.DeleteCheck,new SHOP_INFOInfo() { SHOP_ID = shopId }));           

            return shop.Message;

        }

        #endregion

 

        /// <summary>

        /// 具体实现是否存在的方法

        /// </summary>

        private void _CheckExists()

        {

            var dal = new SHOP_INFODal();

           _exists = dal.Exists(_shopInfo.SHOP_ID);

        }

 

        private void _DeleteCheck()

        {

            var dal = new SHOP_INFODal();

            _message = dal.DeleteCheck(_shopInfo.SHOP_ID);

        }

 

        #region DataPortal_Execute

        /// <summary>

        /// 数据访问门户,根据不同的操作类型_operationType和分发方法

        /// </summary>

        protected override void DataPortal_Execute()

        {

            switch (_operationType)

            {

                case OperationType.Exists:

                    _CheckExists();

                    break;

                case OperationType.DeleteCheck:

                    _DeleteCheck();

                    break;              

                default:

                    break;

            }

        }

        #endregion

 

 

    }

}

 

 

测试代码:

if (SHOP_INFOCmd.Exists(collection["SHOP_ID"]))

                {

                    ViewData.ModelState.AddModelError("SHOP_ID1", "单位代码:" + collection["SHOP_ID"] + "已经存在,不能增加");

                    return ShopInfoCreate();

                }

 

转至:http://blog.csdn.net/dacong/article/details/4274759

posted @ 2011-11-22 23:53 Amos.Liu 阅读(11) 评论(0) 编辑

3_CSLA.Net中BusinessBase类实践 .

摘要: 下面以一个表为例详细说明数据库表单位信息表(SHOP_INFO)CREATE TABLE SHOP_INFO( SHOP_ID VARCHAR2(13) NOT NULL, UP_SHOP_ID VARCHAR2(13), SHOP_NAME VARCHAR2(40) NOT NULL, SHOP_ABBRE VARCHAR2(20), POST_ADDR VARCHAR2(50), SHOP_TYPE VARCHAR2(32), POST_CODE VARCHAR2(10), PRINCIPAL VARCHAR2(10), TELEPHONE VARCHAR2(15), MOBILE VAR阅读全文

posted @ 2011-11-22 23:52 Amos.Liu 阅读(14) 评论(0) 编辑

5_CSLA.Net3.6中ReadOnlyListBase类实践 .

ReadOnlyListBase类主要用来管理业务对象集合,并且是个只读的,在Web开发中比较适合,我们一般很少有可编辑的业务对象集合。ReadOnlyListBase返回的是强类型的业务对象实体集合。可以作为任何需要数据绑定的web控件上。

ReadOnlyListBase与CSLA.NET的数据访问接口通过DataPortal.Fetch来得到数据。因为只一个Fetch方法,当继承此类的业务对象时有许多得到数据的方法,比如得到所有数据GetALL、得到某个部门下的所有子部门等,既对业务对象要求从不同角度来得到数据。为了解决这个问题,让我伤了点脑筋。先看下

DataPortal.Fetch的声明如下:

public static T Fetch<T>(object criteria);

只有一个条件参数,你可以考虑重载很多方法,但不能根本解决问题,因为有时只用参数类型或个数的不同来重载会出现重复的现在,所有重载不能解决有问题。我最后实现的方法来为,实现一个继承自Csla.CriteriaBase的类CriteriaHash,只重载一个private void DataPortal_Fetch(CriteriaHash criteria)方法,通过CriteriaHash的内容来判断具体调用那个方法及参数的获取。

CriteriaHash主要用一个数组来保存本次操作的类型及所有参数值得,这样可以解决传递多个参数及值的问题。用一个Dictionary<string, object>来保存数据值,可以使用索引器来访问,Key表示参数变量,Value为参数的值。并且约定数组的第一个为操作类型,后边为所有参数及值的表。

/// <summary>

/// 条件判断类

/// CriteriaHash必须继承Csla.CriteriaBase,不然在数据在服务器和客户端移动时会出问题。

/// 我按书说的方法不继承,总是出错。

    /// </summary>

    [Serializable()]

    public class CriteriaHash : CriteriaBase

    {

        Dictionary<string, object> _dic;

        //基类使用

        public CriteriaHash(Type type)

            : base(type)

        {

        }

 

        public CriteriaHash(Type type, CriteriaItems[] filters)

            : base(type)

        {

            _dic = new Dictionary<string, object>();

            foreach (CriteriaItems f in filters)

            {

                _dic.Add(f.ParmName, f.value);

            }

        }

        public object this[string Key]

        {

            get

            {

                if (_dic.ContainsKey(Key))

                    return _dic[Key];

                else

                    return null;

            }

        }

    }

 

    [Serializable()]

    public class CriteriaItems

    {

        private readonly string _parmName;

 

        public CriteriaItems(string parmName, object value)

        {

            _parmName = parmName;

            this.value = value;

        }

 

        public object value { get; private set; }

 

        public string ParmName

        {

            get { return _parmName; }

        }

}// http://blog.csdn.net/dacong

 

下边以单位信息表类SHOP_INFOList来具体说明

using System;

using System.Collections;

using System.Collections.Generic;

using System.Runtime.Serialization;

using Csla;

using Csla.Data;

    [Serializable()]

    public partial class SHOP_INFOList : Csla.ReadOnlyListBase<SHOP_INFOList, SHOP_INFOInfo>

    {

       

        /// <summary>

        /// 操作类型枚举定义,

        /// 为了标识所有包括的方法

        /// </summary>

        private enum OperationType { GetAll,GetAllLevel, GetByShopId };

 

        #region  Factory Methods

 

        /// <summary>

        /// 得到一个空的对象

        /// </summary>

        /// <returns></returns>

        public static SHOP_INFOList EmptyList()

        {

            return new SHOP_INFOList();

        }

 

        /// <summary>

        /// 得到所有单位信息

        /// </summary>

        /// <returns></returns>

        public static SHOP_INFOList GetALL()

        {

            return DataPortal.Fetch<SHOP_INFOList>(new CriteriaHash(typeof(SHOP_INFOList),new CriteriaItems[] { new CriteriaItems("OperationType", OperationType.GetAll) }));

            /*

             * 注意Fetch参数的构造,new CriteriaItems[]数据,第一个元素表示操作的类型,也就是方法的名称,如果没有参数只构造一个就可以

             * 如果有参数构造如:new CriteriaItems("ShopId", shopId),"ShopId":参数变量名,shopId,变量的值

             *

            */

        }

 

        public static SHOP_INFOList GetALLForLevel()

        {

            return DataPortal.Fetch<SHOP_INFOList>(new CriteriaHash(typeof(SHOP_INFOList), new CriteriaItems[] { new CriteriaItems("OperationType", OperationType.GetAllLevel) }));

        }

        /// <summary>

        /// 得到当前SHOP_ID下的所有子部门

        /// </summary>

        /// <param name="shopId"></param>

        /// <returns></returns>

        public static SHOP_INFOList GetByShopId(string shopId)

        {

            return DataPortal.Fetch<SHOP_INFOList>(new CriteriaHash(typeof(SHOP_INFOList), new CriteriaItems[] { new CriteriaItems("OperationType", OperationType.GetByShopId), new CriteriaItems("ShopId", shopId) }));

        }

 

        private SHOP_INFOList()

        { /* require use of factory methods */

        }

 

        #endregion

 

        #region Data Access

        #region 具体方法实现

      

        private void _GetALL()

        {

            var dal = new SHOP_INFODal();

            Fill(dal.GetAll(), 0, 0);

        }

        private void _GetALLForLevel()

        {

            Fill(new SHOP_INFODal().GetTreeLevel(), 0, 0);

        }

 

       

        private void _GetByShopId(string shopId)

        {

            //具体怎么实现,根据自己的DAL

            var dal = new SHOP_INFODal();

            Fill(dal.GetBySHOP_ID(shopId,true), 0, 0);

        }

 

        #endregion

        #region Data Access - Fetch dispatch

 

        /// <summary>

        /// 根据SafeDataReader来填充业务对象

        /// </summary>

        /// <param name="reader"></param>

        /// <param name="start"></param>

        /// <param name="pageLength"></param>

        private void Fill(SafeDataReader reader, int start, int pageLength)

        {

            // advance to the starting row

            for (int i = 0; i < start; i++)

            {

                if (!reader.Read())

                    return; // not enough rows, just return

            }

 

            RaiseListChangedEvents = false;

            IsReadOnly = false;

            if (pageLength <= 0)

                pageLength = int.MaxValue;

            SHOP_INFOInfo data = null;

            //得到所有字段列表

            List<string> fields = new List<string>();

            for (int i = 0; i < reader.FieldCount; i++)

            {

                fields.Add(reader.GetName(i));

            }

            for (int i = 0; i < pageLength; i++)

            {

                if (!reader.Read())

                    break; // we are done

                data = new SHOP_INFOInfo();

                if (fields.IndexOf("SHOP_ID") >= 0)

                    data.SHOP_ID = reader.GetString("SHOP_ID");

                if (fields.IndexOf("UP_SHOP_ID") >= 0)

                    data.UP_SHOP_ID = reader.GetString("UP_SHOP_ID");

                if (fields.IndexOf("SHOP_NAME") >= 0)

                    data.SHOP_NAME = reader.GetString("SHOP_NAME");

                if (fields.IndexOf("SHOP_ABBRE") >= 0)

                    data.SHOP_ABBRE = reader.GetString("SHOP_ABBRE");

                if (fields.IndexOf("POST_ADDR") >= 0)

                    data.POST_ADDR = reader.GetString("POST_ADDR");

                if (fields.IndexOf("SHOP_TYPE") >= 0)

                    data.SHOP_TYPE = reader.GetString("SHOP_TYPE");

                if (fields.IndexOf("POST_CODE") >= 0)

                    data.POST_CODE = reader.GetString("POST_CODE");

                if (fields.IndexOf("PRINCIPAL") >= 0)

                    data.PRINCIPAL = reader.GetString("PRINCIPAL");

                if (fields.IndexOf("TELEPHONE") >= 0)

                    data.TELEPHONE = reader.GetString("TELEPHONE");

                if (fields.IndexOf("MOBILE") >= 0)

                    data.MOBILE = reader.GetString("MOBILE");

                if (fields.IndexOf("NOTES") >= 0)

                    data.NOTES = reader.GetString("NOTES");

                this.Add(data);

            }

            IsReadOnly = true;

            RaiseListChangedEvents = true; // http://blog.csdn.net/dacong

        }

 

        /// <summary>

        /// 数据访问接口

        /// </summary>

        /// <param name="criteria"></param>

        private void DataPortal_Fetch(CriteriaHash criteria)

        {

            OperationType operationType = (OperationType)criteria["OperationType"];

            //根据操作类型来分发,调具体的实现方法

            switch (operationType)

            {

                case OperationType.GetAll:

                    _GetALL();

                    break;

                case OperationType.GetAllLevel:

                    _GetALLForLevel();

                    break;

                case OperationType.GetByShopId:

                    _GetByShopId(criteria["ShopId"].ToString());

                    break;

                default:

                    break;

            }

        }

        #endregion //Data Access - Fetch

        #endregion // Data Access

    }

   

//只读业务对象

[Serializable()]

    public partial class SHOP_INFOInfo : Csla.ReadOnlyBase<SHOP_INFOInfo>

    {

        //http://blog.csdn.net/dacong

        public string SHOP_ID { get; internal set; }

        public string UP_SHOP_ID { get; internal set; }

        public string SHOP_NAME { get; internal set; }

        public string SHOP_ABBRE { get; internal set; }

        public string POST_ADDR { get; internal set; }

        public string SHOP_TYPE { get; internal set; }

        public string POST_CODE { get; internal set; }

        public string PRINCIPAL { get; internal set; }

        public string TELEPHONE { get; internal set; }

        public string MOBILE { get; internal set; }

        public string NOTES { get; internal set; }

    }

 

转至:http://blog.csdn.net/dacong/article/details/4261671

posted @ 2011-11-22 23:50 Amos.Liu 阅读(6) 评论(0) 编辑

CSLA服务端如何使用多线程的解决方案

摘要: 前篇说到了使用异步线程来实现数据的预加载,以提高系统性能。 这样的操作一般是在客户端执行,用以减少用户的等待时间。客户端发送多次异步请求,到达服务端后,如果服务端不支持多线程处理操作,线性处理各个请求,必然导致客户端的异步请求变得没有意义。 大家肯定会说,谁会把服务端设计成单线程的啊,那不是明显的错误吗?是的!但是我们的系统使用了CSLA来作为实现分布式的框架,而它的服务端程序却只能支持单线程……这个问题我们一直想解决,但是查过CSLA官方论坛,作者说由于GlobalContext和ClientContext的一些原因,暂时不支持多线程。火大,这还怎么用啊!无奈目前系统已经极大地依赖了这个框架阅读全文

posted @ 2011-11-22 23:47 Amos.Liu 阅读(18) 评论(0) 编辑

和我一起学CSLA.NET----创建业务对象1

     我们已经准备好了环境,下面我们就来介绍CSLA业务对象的创建,如果你还没有下载CSLA,请在这里下载CSLA.NET,我们使用的是3.8.2版本(本来想使用4.0版本,后来发现是针对framework 4.0的)。
     建立类库项目CSLADemo.Library,引用下载的Csla.dll和System.Windows.Interactivity.dll(CSLA依赖),另外还需要引用我们的linq项目。在这一节我们先介绍CSLA业务对象一些基础的东西的实现,在这里包括业务属性,基本业务方法,基本数据验证,以及基本的数据门户.
   在这之前,先介绍一下CSLA的其中的几个主要的业务基类:
     Csla.BusinessBase         所有可编辑的业务类都要继承的基类
     Csla.BusinessListBase    所有可编辑的业务集合类都要继承的基类
     Csla.ReadOnlyBase        所有只读的业务类都要继承的基类
     Csla.ReadOnlyListBase   所有只读的业务集合类都要继承的基类

    使用之前版本的CSLA时,都是直接继承这几个基类,现在的版本已经利用泛型对这些基类进行了封装。我们以产品(Product)业务类为例来新建一个业务类,在这里有个建议,如果你的项目比较大,而且多人维护,或者是由代码生成,你可以考虑用partial类,这样可以将生成的代码与自定义的代码分开,或者是分成业务方法,工厂方法,数据门户等几个不同的分布类文件,这样也会方便源代码管控。
     我们添加Product的三个属性:  

隐藏行号 复制代码 Product
  1. 
    
  2. [Serializable()]
    
  3. public class Product : BusinessBase<Product>
    
  4. {
    
  5.     #region  Business Methods
    
  6. 
    
  7.     private static PropertyInfo<string> ProductIDProperty =
    
  8.       RegisterProperty<string>(p => p.ProductID, "产品编号");
    
  9.     public string ProductID
    
  10.     {
    
  11.         get { return GetProperty(ProductIDProperty); }
    
  12.         set { SetProperty(ProductIDProperty, value); }
    
  13.     }
    
  14. 
    
  15.     private static PropertyInfo<string> ItemNoProperty =
    
  16.     RegisterProperty<string>(p => p.ItemNo, "产品编号");
    
  17.     public string ItemNo
    
  18.     {
    
  19.         get { return GetProperty(ItemNoProperty); }
    
  20.         set { SetProperty(ItemNoProperty, value); }
    
  21.     }
    
  22. 
    
  23. 
    
  24.     private static PropertyInfo<string> ItemNameProperty =
    
  25.     RegisterProperty<string>(p => p.ItemName, "产品名称");
    
  26.     public string ItemName
    
  27.     {
    
  28.         get { return GetProperty(ItemNameProperty); }
    
  29.         set { SetProperty(ItemNameProperty, value); }
    
  30.     }
    
  31. 
    
  32. 
    
  33.     public override string ToString()
    
  34.     {
    
  35.         return ProductID.ToString();
    
  36.     }
    
  37. 
    
  38.     #endregion
    
  39. }
    

 

   这可和我们的通常使用的简单实体的属性差别很大,甚至和之前版本都不一样,有几点需要注意:
       1.类必须打上Serializable标签,因为整个对象是需要序列化传输的。
       2.因为Product是单个可编辑的业务基类,所以直接继承BusinessBase<T>
       3.同时使用了私有变量和属性,而且私有变量必须是static,这样能够保证在对象调用之前已经注册PropertyInfo,当然你可以在构造函数里面调用RegisterProperty,不过不推荐这样。主要原因是如果是非静态变量注册PropertyInfo,你的默认序列化时也会报错,另外使用静态变量可以节约内存,因为对于不同的实例来说,这些注册的属性信息也都是一样的。
详细讨论
       4.之前版本是通过属性上打标签的方法,通过反射来获取属性信息,而现在是通过直接PropertyInfo,通过FieldDataManager/PropertyInfoManager来管理,其中还将PropertyInfo列表缓存,主要是为了提供性能。
       5.可以提供PropertyInfo类型的信息包括属性类型,属性友好名称,默认值,关系类型等,而这些信息都会被CSLA直接使用(见下代码).
       6.每个属性的set,get都通过基类的GetProperty,SetProperty来实现,在读取和设置的过程中,其实是进行了权限的判断,判断对当前属性是否有相应的读或者写的权限.我们来看下GetProperty的部分源代码:

隐藏行号 复制代码 BusinessBase.GetProperty
  1. protected P GetProperty<P>(PropertyInfo<P> propertyInfo, Security.NoAccessBehavior noAccess)
    
  2.   {
    
  3.     P result = default(P);
    
  4.     if (_bypassPropertyChecks || CanReadProperty(propertyInfo.Name, noAccess == Csla.Security.NoAccessBehavior.ThrowException))
    
  5.       result = ReadProperty<P>(propertyInfo);
    
  6.     else
    
  7. result = propertyInfo.DefaultValue;
    
  8.     return result;
    
  9.   }
    

       7.另外一点,属性友好名称friend Name,如果有多语言需求,可以通过获取资源来实现.
    至于关系属性,SmartDate这些我们在遇到时再介绍,我们接着添加产品的验证规则,先只添加对编号,名称不为空的校验,以及长度的限制:

隐藏行号 复制代码 Product.BusinessRules
  1. #region  Business Rules
    
  2. protected override void AddBusinessRules()
    
  3. {
    
  4.     ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired,
    
  5.                             new Csla.Validation.RuleArgs(ItemNoProperty));
    
  6.     ValidationRules.AddRule(
    
  7.       Csla.Validation.CommonRules.StringMaxLength,
    
  8.        new Csla.Validation.CommonRules.MaxLengthRuleArgs(ItemNoProperty, 50));
    
  9. 
    
  10.     ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired,
    
  11.                           new Csla.Validation.RuleArgs(ItemNameProperty));
    
  12.     ValidationRules.AddRule(
    
  13.       Csla.Validation.CommonRules.StringMaxLength,
    
  14.        new Csla.Validation.CommonRules.MaxLengthRuleArgs(ItemNameProperty, 200));
    
  15. }
    
  16. #endregion
    

    基类里面维护了一个ValidationRules属性,专门存储验证规则,我们直接向这个集合里添加就行了,添加的规则可以是通用的一些规则,在CommentRules下面,有最大值最小值,长度,正则等通用规则.也可以是自定义自己的规则,比如我们需要规定所有的产品编号必须以字符开头,否则保存或者更新不成功:

隐藏行号 复制代码 Product.BusinessRules
  1. 
    
  2.       ValidationRules.AddRule<Product>(ItemNoRule, ItemNoProperty);
    
  3.   }
    
  4. 
    
  5. 
    
  6.   private static bool ItemNoRule<T>(
    
  7. T target, Csla.Validation.RuleArgs e) where T : Product
    
  8. {
    
  9.       if (!string.IsNullOrEmpty(target.ReadProperty(ItemNoProperty)) && !char.IsLetter(target.ReadProperty(ItemNoProperty)[0]))
    
  10.       {
    
  11.           e.Description = "编号必须以字母开头!";
    
  12.           return false;
    
  13.       }
    
  14.       else
    
  15. {
    
  16.           return true;
    
  17.       }
    
  18.   }
    

    这样当我们更新或者保存产品时,如果产品编号不是以字符开头,就会出异常,在业务对象的BrokenRulesCollection属性里加入导致失败的规则,我们可以直接查找到描述的错误信息。具体CSLA在内部是怎么调用的,我们会在后面介绍。其实之CSLA之所以让你感觉他的属性,他的业务方法,数据门户,业务规则,权限之间相互比较解耦,是因为他们之间的调用已经在框架部门,在CSLA的代码里面内部调用了。好了,睡了,今天就到这里,下一节添加数据门户的实现和基本工厂方法。

 代码下载

作者:孤独侠客似水流年
出处:http://lonely7345.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2011-11-22 23:19 Amos.Liu 阅读(16) 评论(0) 编辑

和我一起学CSLA.NET----设计模型及数据访问

     上一节和大家说了些CSLA.NET的一些功能,以及对这些功能提出一些疑问,从这一节开始,我们就带着这些问题,来学习CSLA 框架怎么使用。我并不打算在这里介绍CSLA框架的一些概念,如果你对面向对象这些概念仍然不是很熟悉的话,建议你还是大概看一下作者的书,我们还是采用CSLA.NET来结合实际使用完成一个简单的例子. 
     有很多人是先把数据库建好了再来用CSLA来写业务代码,有的是用Codesmith根据数据库生成。其实说实话这也未尝不可,不管黑猫白猫抓到耗子就是好猫嘛,呵呵,不过我个人认为这并不是最合理的,起码你在设计数据库时也应该考虑一下CSLA的业务对象怎么写,尤其是要考虑这些对象之间的关系实现起来的难易度,否则会使你的CSLA过于复杂。
     为了尽量简单化,例子不会掺杂过多的业务,这样来更多的关注用CSLA.NET来实现的过程。我们就拿订单模型来做吧,先设计出整个领域对象:
      Domain Model
     主要包含订单,订单项,产品三个领域类,在这里暂时不考虑客户,而且我们现在也暂时不考虑权限控制,这部分在后面再加上来。我们对这几个对象再进行细化 :
     Domain Objects
     我想同行们对这样的一个设计都已经相当熟悉了,订单包含订单项的集合,订单项又会关联一个产品(物料),我们尽量少的字段,只保留一些有代表性的,日期,金额,枚举类型的字段。同样的,我们在这里也没有设计业务方法,先不管这些(这并不合理),我们在这个时候也可以把我们的数据库建立起来了(可以通过EA来生成脚本): 

隐藏行号 复制代码 创建表结构脚本
  1. USE [CSLATest]
  2. GO
  3. /****** Object:  Table [dbo].[Product]    Script Date: 02/02/2010 21:25:25 ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8. CREATE TABLE [dbo].[Product](
  9. [ProductID] [nvarchar](50) NOT NULL,
  10. [ItemNo] [nvarchar](20) NOT NULL,
  11. [ItemName] [nvarchar](50) NOT NULL,
  12. CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED 
  13. (
  14. [ProductID] ASC
  15. )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  16. ) ON [PRIMARY]
  17. GO
  18. /****** Object:  Table [dbo].[OrderLine]    Script Date: 02/02/2010 21:25:25 ******/
  19. SET ANSI_NULLS ON
  20. GO
  21. SET QUOTED_IDENTIFIER ON
  22. GO
  23. CREATE TABLE [dbo].[OrderLine](
  24. [OrderLineID] [nvarchar](50) NOT NULL,
  25. [OrderID] [nvarchar](50) NOT NULL,
  26. [ProductID] [nvarchar](50) NOT NULL,
  27. [Num] [int] NULL,
  28. [Remark] [nvarchar](50) NULL,
  29. CONSTRAINT [PK_OrderLine] PRIMARY KEY CLUSTERED 
  30. (
  31. [OrderLineID] ASC
  32. )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  33. ) ON [PRIMARY]
  34. GO
  35. /****** Object:  Table [dbo].[Order]    Script Date: 02/02/2010 21:25:25 ******/
  36. SET ANSI_NULLS ON
  37. GO
  38. SET QUOTED_IDENTIFIER ON
  39. GO
  40. CREATE TABLE [dbo].[Order](
  41. [OrderID] [nvarchar](50) NOT NULL,
  42. [OrderDate] [datetime] NULL,
  43. [OrderType] [int] NULL,
  44. [EffecDateBegin] [datetime] NULL,
  45. [EffecDateEnd] [datetime] NULL,
  46. [OrderState] [int] NULL,
  47. [Amount] [decimal](18, 0) NULL,
  48. CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED 
  49. (
  50. [OrderID] ASC
  51. )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  52. ) ON [PRIMARY]
  53. GO
  54. 
    
     接下来,我们必须要做一个选择,那就是CSLA.Net的数据访问使用什么实现,之前的版本一般都是使用存储过程,Sql语句,还有的使用开源的ORM,比如NHibernate等,现在使用Linq的比较多,不过个人认为Linq to Sql或者EF和CSLA.Net还是有重叠的部分,使用时就会涉及到Linq的对象与CSLA业务对象属性的映射的问题,(不过即使你使用存储过程,你也要处理DataSet或者DataReader与你的对象的转换).我们同样也使用Linq To Sql来实现数据门户, 在这个例子中我们并不打算象官方Demo那样使用存储过程来实现业务逻辑,而是都由Linq to Sql来实现.
     先建立起我们的Linq项目CSLADemo.DalLinq,在这里需要进行一下设置,工具--选项--数据库工具--O/R Designer--名称的复数形式设置为True.然后拖动我们的三个表,并根据我们的模型建立他们之间的关联关系,当然,你也可以在数据库表中建立外键,那样的拖动的话这样的关系就会自动生成。
     2010-2-3 13-58-54

     注意我们Order与OrderLine之间,是一对多关系,双向生成属性,OrderLine与Product之间一对多,Product不需要生成属性。
     我们说一下Linq的一些题外话,其实Linq这个设计器就是比较典型的DSL+T4的例子,dbml文件主要是描述元数据文件,CSLADemo.designer.cs文件就是T4模板结合元数据生成的目标代码,我们甚至可以控制这个步骤,通过自己的模板文件,将目标代码生成的单独的类里面,而不是象现在这样,将所有的代码都生成都一个类里,不过这在我们这个例子里是没有必要的,因为我们并不打算去扩展linq to sql生成的代码,而只是简单的使用就足够了。
     另外还有一点需要提的就是,在这里建立的对象与对象的关系其实也是很关键的,在后面我们可以看到,我们在CSLA对象中其实是利用这些关系来加载相关联的对象,也就是通过Linq对象来实现的。
    
    到现在为止我们还没有介绍直接与CSLA相关的东西,不过我们已经完成了整个环境的准备,以及数据访问的完成,下一节我们来介绍CSLA的具体使用.

作者:孤独侠客似水流年
出处:http://lonely7345.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2011-11-22 23:18 Amos.Liu 阅读(21) 评论(0) 编辑

和我一起学CSLA.NET----创建业务对象2

    上一节我们已经创建了我们业务对象的属性和简单的验证规则,这一节我们主要来实现数据门户 ,首先先大概介绍一下CSLA数据访问相关的概念:
    Csla框架的数据门户通过通道适配器模式和消息路由模式合并起来,提供了对服务器上所有数据访问操作的一个简单明了的入口点。实际上,数据门户完全隐藏了服务器的参与,这就使得应用程序能在不改变代码的情况下,在两层和三层物理部署之间自由切换。数据门户有以下特点:
    
实现了移动对象
     隐藏了网络传输(通道适配器)
     暴露了对服务器的唯一入口点(消息路由)
     暴露了服务器端资源(数据库引擎、分布式事务等)
     统一了上下文(在客户端和服务器之间传递上下文)
    数据门户强制了你的对象和数据访问层或者是ORM交互的标准和结构. 这个标准化保留了灵活性,使你能够自由的使用几乎任何你选择的数据访问技术,包括但不仅限于:      
    纯ADO.NET(Connection,data readers等)
    DataSet和TableAdapter对象
    LINQ to SQL
    LINQ to XML
    ADO.NET Entity Framework
    Nhibernate  和其它第三方ORM工具
    简单的文件I/O
    远程XML或者是JSON服务

   CSLA的数据访问门户的标准就是每个业务对象提供DataPortal_XYZ方法,负责进行创建、提取、添加、更新和删除,然后在业务对象工厂方法中调用数据门户的对应方法就是转换成调用提供的DataPortal_XYZ方法:
   fig02
一个完整的可编辑的对象一般要实现如下几个数据门户的方法(这部分代码可以放到业务对象类中、分离在单独的类中、分离在单独的程序集中、使用对象工厂方法):
   DataPortal_Create()      对象被创建时将会被调用,在这里可以设置默认值.
   DataPortal_Fetch()        被数据门户调用来装载对象的数据
   DataPortal_Insert()       处理新对象的持久化保存
   DataPortal_Update()      处理脏对象的持久化更新
   DataPortal_DeleteSelf()
   DataPortal_Delete()        CSLA提供了两种删除对象的方法:立即删除 延迟删除. 延迟删除就是说在对象被装载到内存中的时候,IsDeleted属性被设置成true,当调用对象的Save()时,这时候会在内部调用DataPortal_DeleteSelf方法。立即删除是指显示调用DataPortal.Delete方法并且传递标志要删除对象的查询条件, 数据门户DataPortal在内部会调用此对象的DataPortal_Delete方法.
   我们一个一个的来实现以上几个方法,首先是DataPortal_Create:

隐藏行号 复制代码 Product.DataPortal_Create
  1. [RunLocal()]
  2. protected override void DataPortal_Create()
  3. {
  4.     using (BypassPropertyChecks)
  5.     {
  6.         ProductID = Guid.NewGuid().ToString();
  7.         ValidationRules.CheckRules();
  8.     }
  9. }

    1. [RunLocal()]特性标明此方法在本地运行,而不是在应用服务器上运行,这在分布式服务器部署上能够缩短处理时间.
    2. 由于此方法没有数据库相关的操作,只是简单的属性值初始化,没有必要调用服务器的相关资源,所以才会使用[RunLocal()]标明在本地运行,假如你需要调用数据库资源来进行初始化的话,就不需要打RunLocal标签了.
    3. Product业务对象只需要给主键生成一下Guid字符串,然后我们会手动调用ValidationRules.CheckRules验证一下此业务对象是否符合校验规则.
    4. BypassPropertyChecks是新增的特性,因为在上一节我们介绍过,如果直接通过Name = "TestName"其实是调用的GetProperty,这样在内部会进行一些检查,如果需要在业务对象内部给属性赋值的话,为了性能或者其它原因,想绕过安全或者其它检查,一般需要这样写
        LoadProperty<string>(NameProperty, "TestName")
而现在可以直接使用BypassPropertyChecks, 是不是这样方便多了.
     
    接下来添加DataPortal_Fetch:

隐藏行号 复制代码 Product.DataPortal_Fetch
  1. private void DataPortal_Fetch(SingleCriteria<Product, string> criteria)
  2. {
  3.     using (var ctx =
  4.       ContextManager<CSLADemo.DalLinq.CSLADemoDataContext>.
  5.       GetManager(CSLADemo.DalLinq.Database.CSLADemo))
  6.     {
  7.         var data = (from p in ctx.DataContext.Products
  8.                     where p.ProductID == criteria.Value
  9.                     select p).Single();
  10.         using (BypassPropertyChecks)
  11.         {
  12.             ProductID = data.ProductID;
  13.             ItemName = data.ItemName;
  14.             ItemNo = data.ItemNo;
  15.         }
  16.     }
  17. }
     1.参数为SingeCriteria,其实是表明能够代表当前业务对象的标识,在这里业务对象是Product,主键是string类型,如果在这里你的主键为多个(一般不推荐)的话,你需要实现自己的Criteria。
     2.这里包含根据ProductID获取Product对象的linq实现,在这里就不详细介绍了,其中CSLADemo.DalLinq.Database中的CSLADemo变量存储的是数据库的名字

    我们再来添加DataPortal_Insert: 
隐藏行号 复制代码 Product.DataPortal_Insert
  1. [Transactional(TransactionalTypes.TransactionScope)]
    
  2.        protected override void DataPortal_Insert()
    
  3.        {
    
  4.            using (var ctx =
    
  5.              ContextManager<CSLADemo.DalLinq.CSLADemoDataContext>.
    
  6.              GetManager(CSLADemo.DalLinq.Database.CSLADemo))
    
  7.            {
    
  8.                using (BypassPropertyChecks)
    
  9.                {
    
  10.                    var data = new CSLADemo.DalLinq.Product();
    
  11. 
    
  12.                    data.ProductID = ReadProperty(ProductIDProperty);
    
  13.                    data.ItemNo = ReadProperty(ItemNoProperty);
    
  14.                    data.ItemName = ReadProperty(ItemNameProperty);
    
  15. 
    
  16.                    ctx.DataContext.Products.InsertOnSubmit(data);
    
  17.                    ctx.DataContext.SubmitChanges();
    
  18.                }
    
  19.                FieldManager.UpdateChildren(this);
    
  20.            }
    
  21.        }
    

     1.[Transactional()]特性用来保证此方法事务性.
     2.当调用业务对象的Save方法时,如果此对象的IsNew=True,数据门户会自动调用此方法.
     3.FieldManager.UpdateChildren(this)是指在更新完后,更新所有的子对象,这个在后面介绍
 
   同样的,我们添加另外三个数据门户方法:

隐藏行号 复制代码
  1. [Transactional(TransactionalTypes.TransactionScope)]
    
  2.         protected override void DataPortal_Update()
    
  3.         {
    
  4.             using (var ctx =
    
  5.                 ContextManager<CSLADemo.DalLinq.CSLADemoDataContext>.
    
  6.                 GetManager(CSLADemo.DalLinq.Database.CSLADemo))
    
  7.             {
    
  8.                 using (BypassPropertyChecks)
    
  9.                 {
    
  10.                     var data = new CSLADemo.DalLinq.Product()
    
  11.                     {
    
  12.                         ProductID = ReadProperty(ProductIDProperty)
    
  13.                     };
    
  14.                     if (IsSelfDirty)
    
  15.                     {
    
  16.                         data.ItemNo = ReadProperty(ItemNoProperty);
    
  17.                         data.ItemName = ReadProperty(ItemNameProperty);
    
  18.                         ctx.DataContext.Products.Attach(data, true);
    
  19.                     }
    
  20.                 }
    
  21.                 FieldManager.UpdateChildren(this);
    
  22. 
    
  23.                 ctx.DataContext.SubmitChanges();
    
  24.             }
    
  25.         }
    
  26. 
    
  27.         [Transactional(TransactionalTypes.TransactionScope)]
    
  28.         protected override void DataPortal_DeleteSelf()
    
  29.         {
    
  30.             DataPortal_Delete(
    
  31.               new SingleCriteria<Product, string>(ProductID));
    
  32.         }
    
  33. 
    
  34.         [Transactional(TransactionalTypes.TransactionScope)]
    
  35.         private void DataPortal_Delete(SingleCriteria<Product, string> criteria)
    
  36.         {
    
  37.             using (var ctx =
    
  38.               ContextManager<CSLADemo.DalLinq.CSLADemoDataContext>.
    
  39.               GetManager(CSLADemo.DalLinq.Database.CSLADemo))
    
  40.             {
    
  41.                 var data = ctx.DataContext.Products
    
  42.                      .Single(r => r.ProductID == criteria.Value);
    
  43. 
    
  44.                 ctx.DataContext.Products.DeleteOnSubmit(data);
    
  45.                 ctx.DataContext.SubmitChanges();
    
  46.             }
    
  47.         }
    
     1.DataPortal_Delete为private,因为它不并不会被自动调用。在这里,提供了延迟加载功能,所以在DataPortal_DeleteSelf里调用DataPortal_Delete.

 代码下载

作者:孤独侠客似水流年
出处:http://lonely7345.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

posted @ 2011-11-22 23:17 Amos.Liu 阅读(11) 评论(0) 编辑

和我一起学CSLA.NET----创建业务对象3

      到现在为止, 我们已经为业务对象创建了属性,验证规则,数据门户.除了属性我们会直接使用,验证规则和数据门户我们不会手动直接调用的,而最通常调用的是我们的业务对象的工厂方法,在这一节,我们就为Product对象添加了些通用的工厂业务方法,并且介绍一下工厂方法调用的内部细节,以及业务对象的相关细节。
      首先我们必须要了解一下业务对象的Save方法,这个方法是CSLA约定的基本业务对象BusinessBase<T>内置实现的,源代码如下:

隐藏行号 复制代码 BusinessBase.Save
  1. public virtual T Save()
  2. {
  3.   T result;
  4.   if (this.IsChild)
  5.     throw new NotSupportedException(Resources.NoSaveChildException);
  6.   if (EditLevel > 0)
  7.     throw new Validation.ValidationException(Resources.NoSaveEditingException);
  8.   if (!IsValid && !IsDeleted)
  9.     throw new Validation.ValidationException(Resources.NoSaveInvalidException);
  10.   if (IsBusy)
  11.     throw new Validation.ValidationException(Resources.BusyObjectsMayNotBeSaved);
  12.   if (IsDirty)
  13.     result = (T)DataPortal.Update(this);
  14.   else
  15. result = (T)this;
  16.   OnSaved(result, null, null);
  17.   return result;
  18. }
     前几个判断和异常我们暂且不管,我们看到当属性IsDirty为True时,会调用数据门户的Update(以下是此方法代码片断),而在Update方法内部,CSLA其实是会根据当前业务对象不同的状态进行不同的处理,如果业务对象是新增对象(IsNew=True),则通过反射调用业务对象的DataPortal_Insert方法,如果为已经标记删除的对象(IsDeleted=True),则调用对象的DataPortal_DeleteSelf方法,否则调用业务对象的更新方法DataPortal_Update, 以下就是DataPortal.Update方法的内部代码片断:
隐藏行号 复制代码
  1.    string methodName;
  2.           if (obj is CommandBase)
  3.           {
  4.             //注意这里的方法名都是写死的,这和基类BusinessBase里定义的一致,所以数据门户的方法名是固定的
  5. methodName = "DataPortal_Execute";
  6.             operation = DataPortalOperations.Execute;
  7.             //反射之前会进行权限判断
  8. if (!Csla.Security.AuthorizationRules.CanEditObject(objectType))
  9.               throw new System.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException,
  10.                 "execute",
  11.                 objectType.Name));
  12.           }
  13.           else
  14. {
  15.             var bbase = obj as Core.BusinessBase;
  16.             if (bbase != null)
  17.             {
  18.               if (bbase.IsDeleted)
  19.               {
  20.                 methodName = "DataPortal_DeleteSelf";
  21.                 if (!Csla.Security.AuthorizationRules.CanDeleteObject(objectType))
  22.                   throw new System.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException,
  23.                     "delete",
  24.                     objectType.Name));
  25.               }
  26.               else
  27.                 if (bbase.IsNew)
  28.                 {
  29.                   methodName = "DataPortal_Insert";
  30.                   if (!Csla.Security.AuthorizationRules.CanCreateObject(objectType))
  31.                     throw new System.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException,
  32.                       "create",
  33.                       objectType.Name));
  34.                 }
  35.                 else
  36. {
  37.                   methodName = "DataPortal_Update";
  38.                   if (!Csla.Security.AuthorizationRules.CanEditObject(objectType))
  39.                     throw new System.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException,
  40.                       "save",
  41.                       objectType.Name));
  42.                 }
  43.             }
  44.             else
  45. {
  46.               methodName = "DataPortal_Update";
  47.               if (!Csla.Security.AuthorizationRules.CanEditObject(objectType))
  48.                 throw new System.Security.SecurityException(string.Format(Resources.UserNotAuthorizedException,
  49.                   "save",
  50.                   objectType.Name));
  51.             }
  52.           }
  53.           method = Server.DataPortalMethodCache.GetMethodInfo(obj.GetType(), methodName);
  54.         }
  55.        //...下面是调用这个方法了
  56. 
    

     这里列出来了比较多的代码,其实这也是一种学习CSLA框架的一个手段,他的源代码并不是很复杂,结合着实例去学习他的源代码对我们能够很好的使用这个开源框架很有帮助. 很多问题只有你知道了它的具体原理,你才能够真正的去充分利用它的价值,也就会避免来滥用它.不要迷恋它,也不能低估它.
      以上之所以介绍业务对象的Save方法,是想说明,在这个方法内部已经提供了业务对象的更新,延迟删除,保存,所以说我们工厂方法没有必要提供这三个方法了,这样我们暂时不考虑业务直接相关的方法,还差创建对象,直接删除,获取对象三个方法了,我们依次添加这三个方法: 

隐藏行号 复制代码 Factory Methods
  1. #region  Factory Methods
    
  2. 
    
  3. public static Product NewProduct()
    
  4. {
    
  5.     return DataPortal.Create<Product>();
    
  6. }
    
  7. public static Product GetProduct(string id)
    
  8. {
    
  9.     return DataPortal.Fetch<Product>(new SingleCriteria<Product, string>(id));
    
  10. }
    
  11. public static void DeleteProduct(string id)
    
  12. {
    
  13.     DataPortal.Delete(new SingleCriteria<Product, string>(id));
    
  14. }
    
  15. private Product()
    
  16. { }
    
  17. 
    
  18. #endregion
    
      1.首先是创建对象的方法NewProduct(),可能有些人可能会不太明白这个方法为什么存在,CSLA约定所有业务对象实现非public构造函数,防止用户new一个业务对象,强制UI开发人员使用工厂方法来使用业务对象,可以看到我们最下面的默认构造方法都是私有的,你不能够在外面直接通过new来创建我们的业务对象.
      2.业务方法调用的数据门户相应的实现,其实就和业务对象的数据门户的方法一一对应,这个我们在上一节有介绍,比如这里调用的DataProtal.Create其实就是调用的我们的业务对象内部实现的DataPortal_Create,只不在调用时自动封装了上下文,进行了通道处理,进行了安全验证.
      3.业务方法都是static的静态方法.

      到现在为止,我们已经完成了一个基本的业务对象,包括它的属性,验证,数据门户,业务方法,现在可以对我们的业务对象进行一下测试了,很简单,我们使用Vs.Net带的单元测试,新建一个测试项目, 然后注意新增一个配置文件来配置我们的数据库连接, 再添加我们针对Product业务对象的单元测试类即可了,我们先简单的测试一下创建和保存方法:
隐藏行号 复制代码 Test
  1. 
    
  2. /// <summary>
    
  3. ///NewProduct 的测试
    
  4. ///</summary>
    
  5. [TestMethod()]
    
  6. public void NewProductTest()
    
  7. {
    
  8.     Product actual;
    
  9.     actual = Product.NewProduct();
    
  10.     actual.ItemNo = "000001";
    
  11.     actual.ItemName = "山寨手机Nexus one";
    
  12.     Assert.IsNotNull(actual.ProductID);
    
  13. }
    
  14. 
    
  15. [TestMethod()]
    
  16. public void SaveProductTest()
    
  17. {
    
  18.     Product actual;
    
  19.     actual = Product.NewProduct();
    
  20.     actual.ItemNo = "000001";
    
  21.     actual.ItemName = "山寨手机Nexus one";
    
  22.     actual.Save();
    
  23. 
    
  24.     Product expected = Product.GetProduct(actual.ProductID);
    
  25.     Assert.IsNotNull(expected);
    
  26.     Assert.AreEqual(actual.ItemNo, expected.ItemNo);
    
  27. }
    

     右击运行单元测试,我们可以直接查看测试结果,直接测试我们的业务对象的相关特性,或者是调试过程来查看一些内部流程.

     接下来,还是来提示一些业务对象的一些使用上的细节:
     1.当使用工厂方法NewXXX创建一个对象后,在DataPortal_Create里对这个对象的默认值进行初始化,对象的属性IsNew=True,然后需要调用业务对象内置的Save方法,才保存这个对象
     2.如果你调用业务对象内置的Delete()方法,其实是设置这个业务对象的IsDeleted=True,IsDirty=True,然后需要调用业务对象内置的Save方法,来完成这个对象的持久化,这就是我们提到过的延迟删除(也可以通过查看我们上面提到的Save方法的内部处理看出来).
     3.如果你调用业务对象内置的Save方法,对象不是是以上说的两种情况,而且进行过更改,属于脏对象(IsDirty=True),那么会执行这个对象的更新操作,也就是会调用数据门户提供的DataPortal_Update方法.
   
  不过我们的业务对象还比较简单,到现在并没有考虑到主子关系,也没有处理安全验证,更没有应用CSLA的移动对象,这些概念我们会在下面一一介绍.

 代码下载

作者:孤独侠客似水流年
出处:http://lonely7345.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

posted @ 2011-11-22 23:16 Amos.Liu 阅读(7) 评论(0) 编辑

转.如何在CSLA框架中,使用状态机工作流

最近的一个项目里,使用了CSLA框架。CSLA有一个基本的设计原理,就是充分利用数据代理(同时规定BussinessObject is serializable),这个代理保证了csla的5层构架对各种物理部署构架的适应性。从它的框架可以看见,它的数据操作都是在DataPotol中定义,所以我们如果要在csla中使用工作流,切不可违反它的这一规定

 

DataPortalClient.IDataPortalProxy proxy;
     proxy = GetDataPortalProxy(method.RunLocal);

 

 

普通的WebApplication的做法是在globel文件里加载工作流,如下所示:

protected void Application_Start(object sender, EventArgs e)
      {
          WorkflowRuntime workflowRuntime = new WorkflowRuntime(ConfigurationParameters.WorkflowName);
          workflowRuntime.AddService(new SqlWorkflowPersistenceService(ConfigurationParameters.WorkflowDBConn));
          workflowRuntime.AddService(new SqlTrackingService(ConfigurationParameters.WorkflowDBConn));
          ExternalDataExchangeService dataService = new ExternalDataExchangeService();
          workflowRuntime.AddService(dataService);
          WorkflowServices service = new WorkflowServices();
          dataService.AddService(service);
          workflowRuntime.StartRuntime();
          Application[ConfigurationParameters.WorkflowName] = workflowRuntime;
      }

在CSLA框架里,我们这样做:建立一个Command对象:

  [Serializable]
    public class WorkflowCommand : CommandBase
    {
        private static WorkflowRuntime m_workflowRuntime;
        private static ExternalDataExchangeService m_exchangeService;
        private static bool m_unloadFlag = false;
        public static WorkflowService WorkflowServiceInstance { get; private set; }
        public WorkflowInstance WorkflowInstance { get; private set; }

        public static WorkflowRuntime WorkflowRuntime
        {
            get { return WorkflowCommand.m_workflowRuntime; }
        }

        #region Factory Methods


        public static void InitializeWorkflowRuntime()
        {
            m_unloadFlag = false;
            WorkflowCommand cmd = new WorkflowCommand();
            DataPortal.Execute<WorkflowCommand>(cmd);
        }

        public static void UnloadWorkflowRuntime()
        {
            m_unloadFlag = true;
            WorkflowCommand cmd = new WorkflowCommand();
            DataPortal.Execute<WorkflowCommand>(cmd);
        }

        #endregion

        #region Server-side Code

        protected override void DataPortal_Execute()
        {
            if (!m_unloadFlag)
            {
                m_workflowRuntime = new WorkflowRuntime();
                m_exchangeService = new ExternalDataExchangeService();
                m_workflowRuntime.AddService(new ManualWorkflowSchedulerService());
                m_workflowRuntime.AddService(m_exchangeService);
                m_workflowRuntime.AddService(new SqlWorkflowPersistenceService(ConfigrationParameters.WorkflowConn));
                m_workflowRuntime.AddService(new SqlTrackingService(ConfigrationParameters.WorkflowConn));

                WorkflowServiceInstance = new WorkflowServiceInstance();
                m_exchangeService.AddService(WorkflowServiceInstance);

                m_workflowRuntime.StartRuntime();
            }
            else if (m_workflowRuntime != null)
            {
                m_workflowRuntime.StopRuntime();
            }
        }

        #endregion

 

 

然后在globel里加载:

protected void Application_Start(object sender, EventArgs e)
     {
         WorkflowCommand.InitializeWorkflowRuntime();
     }

这样的方式,保证了工作流的初始化在服务端完成,充分体现CSLA的思想。

 

那么如何在Bussiness object里使用这个Command对象呢,我们需要在Bussiness Object的DataPotal里控制工作流的流动和可持续化的操作,比如:

 

 protected override void DataPortal_Update()
        {
            WorkflowInstance wfInstance = null;
            ManualWorkflowSchedulerService schec = null;
            WorkflowServiceInstance service = null;
            Guid workflowID = Guid.Empty;

            if (isrunWorkflow)
            {
                workflowID = ReadProperty(WorkflowIdProperty);
                wfInstance = WorkflowCommand.WorkflowRuntime.GetWorkflow(workflowID);
                wfInstance.Load();
                schec = WorkflowCommand.WorkflowRuntime.GetService<ManualWorkflowSchedulerService>();
                service = WorkflowCommand.WorkflowRuntime.WorkflowServiceInstance;          
                service.Start(Id, workflowID);
                schec.RunWorkflow(workflowID);
            }

            //TODO:其他数据操作

            wfInstance.Unload();
        }
    }

 

Ok,如上所示,把BussinessObject的数据操作和工作流数据操作都封装进DataPortal方法里,这样保证了工作流可持续化及跟踪服务在服务端运行。

这样,工作流就嵌入到CSLA框架中。

 

在界面,我们需要进入2层的innerException来捕捉工作流的异常:

catch (Exception ex)
{
	if (ex.InnerException.InnerException is System.Workflow.Activities.EventDeliveryFailedException)
	{
		//alert("not allowed!")
		return;
	}
	else	{	}	
}

 

 

整个过程结束。

 

转:http://www.cnblogs.com/llopx/archive/2009/12/28/1634501.html

 

posted @ 2011-11-22 23:11 Amos.Liu 阅读(19) 评论(0) 编辑

  

导航

公告