随笔- 26  文章- 0  评论- 35 
置顶随笔
摘要: 看时间还早,写篇日志消遣消遣。今天来回顾一下Csla框架中的业务与验证规则,这个功能也是作者在框架中重 点实现的一个方面,意图是在业务对象中以一种标准的方式来支持和处理数据有效性规则的验证。(提前说明的一点是,学习此框架的首要目的是提高软件开发思维能力,所以内容可能有些针对此框架,同时夹杂着个人想法,如有错误请指教。) 前面在属性联想的章节中也提到在框架中对业务规则的验证,是通过调用属性set时框架自动触发当前属性的业务验证规则;此外,在调用工厂方法创建一个新对象时也可以显示的调用CheckRules()方法来遍历所有验证规则,在特殊的属性关联时,也可以通过属性名称来调用其他属性的验证规则来.阅读全文
posted @ 2011-05-18 00:28 Simply 阅读(646) 评论(1) 编辑
2011年6月20日

  新的工作环境已经半年了,虽然技术上进步不大,但也总想写一些东西,算是这周的纪念品吧。

  公司的项目开发是基于平台的,所以大多数的时间除了了解和讨论客户需求,剩下的时间都是在配置表单或流程,打开VS的机会是少之又少,倒是数据库使用的更频繁了一些。因为自己也没有使用过其他工作平台,所以可能思想有一些束缚,在也挑选自己感觉非常有道理的,今天就来说一下关于项目中的信息选择功能,如人员选择,部门选择,类别选择等。

  在说之前更想说的是,对于一个系统来说,风格及操作的一致性有多么的重要,现在流程的苹果产品,很贵但卖的也很好,仅仅是外观样式及他的简洁流畅的操作就应该非常有说服力。今天听技术广播时还听到关于展现层的规范性,用户首先接触和关注的是样子,即便底层有多么强大,它很重要!

  回来说我们的选择,系统中的选择功能无处不在,项目中选择项目经理,发送信息时选择发送人员。有可能是选择单个人员,也有可能是选择多个人员,很多时间还需要关键字查询等功能。以前有时会单独创建选项页面去实现,但仔细想,这些功能有着共性,简单的是从固定的选项中选择信息,复杂些的就是根据传递的条件进行筛选,这些功能完全可以使用选择配置来进行操作,其中会包括查询待选项的SQL配置信息以及已选项的SQL配置信息,至于查询的结果,我感觉使用Key.Value的形式比较好,当然显示文本可以根据实际的需要进行字符串拼接,以便显示的更明确些。

  还有个关键问题是查询参数替换,这里要说的就是约定,我们必须将查询的配置方式约定好,传递参数的名称、格式等,否则很容易乱的。

  关于操作执行结果,可以分为自动保存及选择项返回。可以通过配置保存表及主、外键信息,将选项信息自动保存到相应的数据表中。也有很多时候是需要将选项信息返回给客户代码,如上面说的项目经理选择,选择完后是需要显示在文本框中的,这时就需要将选择后的键值信息返回。

  还有其他功能,如是否启动查询功能,是否进行即时筛选等,可扩展的功能还是挺多。

  关于主要内容就这些。

  其实最近挺纠结,以现在的开发方式,大多时间都已经将技术抛在脑后,而更多的时间是放在了具体的业务流程的学习和思考中,不知道会不会出问题,哎...

posted @ 2011-06-20 00:11 Simply 阅读(115) 评论(0) 编辑
2011年6月19日

  曾经写过一篇关于微软工作流基础框架(Windows Workflow Foundation)的文章,也曾经说过要写一个学习过程,但因为种种原因一直没有兑现,其中最大的问题就是发现当时自己的学习方向存在很大的问题,今天就再简单的回忆一下,同时也提供以前做的一个小例子下载,虽然很久了,算是自己学习的一个里程碑。

  因为是第一次接触工作流,对它的概念非常糊糊,但郁闷的是从微软的WF开始学习,所以经历了很郁闷的一段日子。下载文件中包含了自己曾经开发的一个例子(WF3.5),没有深奥的技术,仅供参考而已,唯一的目的就是想说明它只是一个基础框架,要用更广的知识去将它武装起来。(此项目也是从wxwinter文章中启发和借鉴的,所以如果项目中对引用存在问题的话我会马上收摊)

  直接贴结构图:



  

  显示的就是主要的工作模块,项目只支持状态机工作流,功能也是仅支持流程的简单流转,其中流程定义包含winform版本与silverlight(根据wxwinter的流程设计改的),当时最大的发现就是创建流程基类去自定义流程,将流程模板保存在数据库中,实现了简易的自定义流程,流程加载时也是根据业务对应的流程模板去动态加载创建。

  下载地址:WorkflowSolution.rar

posted @ 2011-06-19 23:28 Simply 阅读(529) 评论(3) 编辑
2011年6月8日

  中午时看到一篇关于工作流角色的应用问题,没想到晚上又和一个朋友偶然间谈论起了项目开发中的角色问题,把自己的一点想法在园里留个痕迹,希望对大家有所帮助。

  对于应用程序来说,权限控制是一个关键功能,其贯穿于整个软件的开发与使用周期,能有一个好的权限控制解决方案能提升项目开发速度,特别是系统的维护阶段,更能够体现出来。

  权限的控制,理所当然的引出角色,组织结构等。项目经理拥有查看本人的项目信息权限,而且可以修改项目信息;设计代表可以查看本人的项目信息,但只能以只读的方式查看;老总可以查看公司所有的项目信息,并且可以修改所有的项目,所有的一切都是角色与权限的结合,而人,只是站在了某个角色,或者说某几个的角色角度,系统不会知道这个人能干什么,只能够根据当前的用户所拥有的角色去对应数据集权限,表单操作权限等,当然首先是菜单目录权限的过滤,还有很多别的权限类别。

  说一下目前的项目开发中关于组织机构模块的操作过程,因为可能存在子部门,所以部门是以树形结构展现,根节点当然是公司,以下的节点会根据关系可能分为多级,当然我们会叫做子部门,关系也可能是直属本公司部门,也可能是以分公司的形式,这些应该只是根据实际需求理解上的问题。根据实际的公司结构,其实公司越大越能够清晰的体现,部门里面是分为某某职位的,在系统中也就是对应到角色,相同职责不同部门的部门经理其实角色一样,所以如果说部门经理开会,只需要根据经理这个角色编号去每个部门查找该角色对应的人即可,也引出部门中的角色应该可以是多人。具体的操作就是进入部门后要为部门选择当前部门中存在的角色,这样部门角色关系就确定下来。最后就是为当前部门里的角色任命某些人来就职。这样公司的结构就完整了,而且每个人也找到了各自的职位,一提起某某部门的经理,就可以清晰的知道是谁。按照这样的结果,可以看得出每个人是可能拥有多种职责,甚至如果是小公司的话一个人可能会参与到多个部门的多个职位,这种情况没法拿理论来说,因为实际中已经发生了,而且什么情况都有可能,你想不到的,他们也能办得到。。

  根据上段落所说的,我们可以得到一个人在各个部门中所拥有的角色信息(职位),有一点就是在目前开发中对于一个成员来说会指定一个部门,也就是如果添加一个用户时应该为用户选择一个部门,这应该是理论上应该的,但这样并不妨碍一个人多部门角色,只是如果在选择部门用户时会根据这个初始化的部门来提取,当然所有的都有例外~

  想起了webcast中的一句话:软件就像是变色龙...

  上述中说到的角色是相对来说固定不变的,所以我们平常都称之为静态角色,也就是在组织结构中明确指定的部门角色成员信息。系统中还有另外一种角色,我们称之为动态角色,这也是自己新工作后接触到的,下面举例说明,挺好理解的。

  对于一个项目来说需要指定项目经理,而项目经理是个角色,但对于不同的项目来说都会各自有不同的项目负责人,而且可能会有多个人拥有该角色,这种情况我们就要为该项目指定某些人来充当该项目的项目经理这个临时角色,这些人就是该项目的动态角色。其他情况还很多,特别是这种随着"项目"走的模块。(最后这个项目是个广义上的项目,可能是一次演讲,可能是一份合同等)

  了解了动态角色,就可以想象得出角色是需要分类的,也就是动态角色与静态角色之分。而我们在项目中动态角色并非直接得应用,而是对其进行了一次分组操作,也就是指定某几个动态角色归属哪类,这个跟我们的实际情况有关,因为有一种情况是同一个项目需要同时并列得为多个动态角色选择人员。平常情况下是不需要的,比如为项目经理指定PM组。

  最后就是保存了,保存时是保存有四个字段:主键、宿主、角色、用户,已足够。

  回到权限的控制,上面提到,对于列表要进行数据集授权,角色范围就应该是拥有该项目操作的静态角色以及拥有操作该项目的某动态角色所对应的成员。

  就谈这些,这块还有好多想法,以后再说,希望对大家有帮助~

posted @ 2011-06-08 00:37 Simply 阅读(253) 评论(0) 编辑
2011年5月18日

  看时间还早,写篇日志消遣消遣。今天来回顾一下Csla框架中的业务与验证规则,这个功能也是作者在框架中重 点实现的一个方面,意图是在业务对象中以一种标准的方式来支持和处理数据有效性规则的验证。(提前说明的一点是,学习此框架的首要目的是提高软件开发思维能力,所以内容可能有些针对此框架,同时夹杂着个人想法,如有错误请指教。)

  前面在属性联想的章节中也提到在框架中对业务规则的验证,是通过调用属性set时框架自动触发当前属性的业务验证规则;此外,在调用工厂方法创建一个新对象时也可以显示的调用CheckRules()方法来遍历所有验证规则,在特殊的属性关联时,也可以通过属性名称来调用其他属性的验证规则来实现验证。

  对象规则定义的方法有两种,一种被称为每类型的规则,也就是同样的对象类型只实例化一次业务规则,这样会减少系统内在开支,也是框架默认的实现方法;另一种是每实例类型的规则,也就是创建新对象实例时就会为当前对象创建该对象的业务规则信息,可想而之,有些对象的操作权限会根据当前登陆用户来决定,这种情况就会适用。
  在实现验证规则时,作者引入了委托,也就是将验证规则提升为了一定规则的对象类型,通过约定好的规则来定

义每类验证方法,如必填项,数字范围,复杂的可能还会对数据库进行操作等,典型的方法签名如下:

   1:  public delegate bool RuleHandler(object target, RuleArgs e);   
   2:  public delegate bool RuleHandler<T, R>(T target, R e) where R : RuleArgs;
 

可以看出它们的样式特别像事件,其中第2行是实现的范型类型。这样,开发者就可以按规则定义自己的业务方法,再分配给需要规则定义的属性。方法参数中的RuleArgs保存属性信息及验证结果描述信息。

  另外一个辅助类是失效业务规则的跟踪,它会存在于每个对象实例中(而且是在数据违背规则时才会初始化),当赋值操作违背业务规则时系统就会记录当前的错误信息,并通过错误处理接口向客户端的数据绑定提供错误信息(框架实现了数据绑定接口),当用户更新失效数据时系统及时清除当前失效记录。值得一提的是失效记录会随着N层撤销功能一块被拍照及撤销,错误信息也会是在对象保存时系统判断是否能保存的重要依据。

  此功能使用了反射,这个应该不会是讨论点了,常提到的系统性能与可扩展性及可维护性的权衡问题大家心里都明白,这东西不能钻牛角尖。

  在这里也就是大体说一下,希望不会太让大家失望。最后贴一个验证规则样例(源于框架CommonRules类):

   1:          /// <summary>
   2:          /// Rule ensuring a string value contains one or more
   3:          /// characters.
   4:          /// </summary>
   5:          /// <param name="target">Object containing the data to validate</param>
   6:          /// <param name="e">Arguments parameter specifying the name of the string
   7:          /// property to validate</param>
   8:          /// <returns><see langword="false" /> if the rule is broken</returns>
   9:          /// <remarks>
  10:          /// This implementation uses late binding, and will only work
  11:          /// against string property values.
  12:          /// </remarks>
  13:          [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
  14:          public static bool StringRequired(object target, RuleArgs e)
  15:          {
  16:              string value = (string)Utilities.CallByName(
  17:                target, e.PropertyName, CallType.Get);
  18:              if (string.IsNullOrEmpty(value))
  19:              {
  20:                  e.Description = string.Format(Resources.StringRequiredRule, RuleArgs.GetPropertyName(e));
  21:                  return false;
  22:              }
  23:              return true;
  24:          }

使用方法也挺简单,如下:

        protected override void AddBusinessRules()
        {
            // TODO: add validation rules
            ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, NameProperty);
        }
posted @ 2011-05-18 00:28 Simply 阅读(646) 评论(1) 编辑
2011年4月12日

  (若感觉以下思想存在问题,请速速离开!)

  关于对象状态的维护,开始有所体会还是在linq2sql和ef中,称作上下文的应该就会对当前所使用过的对象进行状态跟踪,无论是新建对象,还是从数据库获取对象进而对其进行更新删除操作,在上下文中都会对它们进行状态跟踪。无论是简单对象还是包含子对象的复杂对象,都应该是有状态的。同样的思想,Csla框架中也是这么来处理的(当然,可以具体的处理方法不同)。

  Csla框架中,状态的管理只对可编辑的对象有用,包括可编辑的根、子对象,可编辑的根、子对象集,还有一个就是动态可编辑对象。对于只读的对象本身就不具可编辑性,所以也谈不上状态了。

  框架中的状态属性包括:IsNew,IsSelfDirty,IsDirty,IsSelfValid,IsValid,IsSavable,IsDeleted。其中在以前的版本中没有IsSelf与IsSelfValid,其展现形式是通过用户自己来实现的,在现在的版本中因为有了字段管理功能,所以这些功能就直接实现在了框架内部,比以前方便很多。也许从名称就可以看得出各个状态的意义,以下就个人看法作小小说明:

  (状态不会独立工作,有些状态的依据是来自于其他某些的状态,如IsDirty与IsSelfDirty有关,是否可保存状态IsSavable与IsDeleted,IsNew等多个状态有关(当然也可能与对象的IsBusy状态有关);在保存时以上多个状态都会先后起作用!)

  IsNew,顾名思义,代表新对象的意思,它标识如果内存中的对象主键存在于数据库中,那么值为False,否则值为True,在框架中创建新对象时默认为True,或者旧对象被删除后,系统也会将值赋予True(内在与数据库中主键不再对应)。在保存时,如果IsNew为True,系统会调用新增方法,否则就会是修改或删除。

  IsSelfDirty(这个属性在看2.0版本时还没有),标识对象创建/获取后是否对其数据进行更新过(书上称为脏对象),所以无论是新建对象时还是对象重获取时,对象的脏状态默认是False的,当对象在对对象属性进行赋值或通过方法间接操作时,脏属性就会标识为True。IsDirty与IsSelfDirty大体一致,但IsDirty的状态还会与它的子对象的状态相关,也就是只有对象自身的IsSelfDirty为False并且所有子对象的IsDirty的状态为False时此对象的IsDrity才为False,否则当前的对象就是标识为脏的,所以每次访问该属性时它都会去遍历它的所有子对象的状态(当然如果有为True的就会直接返回),这个功能在旧版本中是在对象中通过重写IsDirty来实现的,在现在的版本中框架通过字段管理就可以自动进行跟踪(前提是属性中的子对象是通过框架内部的字段管理方式来实现的)。因为在IsDirty为False时当前对象(及子对象)是未编辑状态,所以为True时系统才会继续向下根据其他状态判断该执行什么数据操作方法,

  IsValid与IsSelfValid与脏对象原理一致,只不过是否通过验证是根据对象的数据有效性有关,只有当前对象的所有字段都有效,也就是没有违反任何规则时才为True。数据有效性及验证规则的统一管理也是框架的一个实现目标,以后再说。

  IsDeleted,标识当前对象是否被删除。框架支持直接删除和延迟删除,是否删除属性就是延迟删除进行操作的依据(注意的是子对象的的删除方法是DeleteChild(),根对象的为Delete(),不通用!),它会将当前可编辑的对象标识为IsDeleted=True,系统并不会直接提交,而是作删除标记,当用户最后调用Save()方法时系统会根据当前对象或子对象的删除状态进行调用删除方法。

  最后的是IsSavable属性,它是以上属性的集合体,标识当前对象是否可保存,在这引用一下框架属性代码:

   1:          /// <summary>
   2:          /// Returns <see langword="true" /> if this object is both dirty and valid.
   3:          /// </summary>
   4:          /// <remarks>
   5:          /// An object is considered dirty (changed) if 
   6:          /// <see cref="P:Csla.BusinessBase.IsDirty" /> returns <see langword="true" />. It is
   7:          /// considered valid if IsValid
   8:          /// returns <see langword="true" />. The IsSavable property is
   9:          /// a combination of these two properties. 
  10:          /// </remarks>
  11:          /// <returns>A value indicating if this object is both dirty and valid.</returns>
  12:          [Browsable(false)]
  13:          public virtual bool IsSavable
  14:          {
  15:              get
  16:              {
  17:                  bool auth;
  18:                  if (IsDeleted)
  19:                      auth = Csla.Security.AuthorizationRules.CanDeleteObject(this.GetType());
  20:                  else if (IsNew)
  21:                      auth = Csla.Security.AuthorizationRules.CanCreateObject(this.GetType());
  22:                  else
  23:                      auth = Csla.Security.AuthorizationRules.CanEditObject(this.GetType());
  24:                  return (auth && IsDirty && IsValid && !IsBusy);
  25:              }
  26:          }

可以看出,除了上面的状态,还有auth和IsBusy,auth是获取对应状态下是否可操作的用户权限。至于后面的IsBusy,没注意过…

  属性的应用主要是在页面状态的控制与数据保存,如当对象IsSavable为False时让保存按钮失效,当对象是新对象时可以禁用掉删除按钮,还有个主要应用点就是保存时,上面也提到,下面也贴下代码:

   1:          /// <summary>
   2:          /// Saves the object to the database.
   3:          /// </summary>
   4:          /// <remarks>
   5:          /// <para>
   6:          /// Calling this method starts the save operation, causing the object
   7:          /// to be inserted, updated or deleted within the database based on the
   8:          /// object's current state.
   9:          /// </para><para>
  10:          /// If <see cref="Core.BusinessBase.IsDeleted" /> is <see langword="true"/>
  11:          /// the object will be deleted. Otherwise, if <see cref="Core.BusinessBase.IsNew" /> 
  12:          /// is <see langword="true"/> the object will be inserted. 
  13:          /// Otherwise the object's data will be updated in the database.
  14:          /// </para><para>
  15:          /// All this is contingent on <see cref="Core.BusinessBase.IsDirty" />. If
  16:          /// this value is <see langword="false"/>, no data operation occurs. 
  17:          /// It is also contingent on <see cref="Core.BusinessBase.IsValid" />. 
  18:          /// If this value is <see langword="false"/> an
  19:          /// exception will be thrown to indicate that the UI attempted to save an
  20:          /// invalid object.
  21:          /// </para><para>
  22:          /// It is important to note that this method returns a new version of the
  23:          /// business object that contains any data updated during the save operation.
  24:          /// You MUST update all object references to use this new version of the
  25:          /// business object in order to have access to the correct object data.
  26:          /// </para><para>
  27:          /// You can override this method to add your own custom behaviors to the save
  28:          /// operation. For instance, you may add some security checks to make sure
  29:          /// the user can save the object. If all security checks pass, you would then
  30:          /// invoke the base Save method via <c>base.Save()</c>.
  31:          /// </para>
  32:          /// </remarks>
  33:          /// <returns>A new object containing the saved values.</returns>
  34:          public virtual T Save()
  35:          {
  36:              T result;
  37:              if (this.IsChild)
  38:                  throw new NotSupportedException(Resources.NoSaveChildException);
  39:              if (EditLevel > 0)
  40:                  throw new Validation.ValidationException(Resources.NoSaveEditingException);
  41:              if (!IsValid && !IsDeleted)
  42:                  throw new Validation.ValidationException(Resources.NoSaveInvalidException);
  43:              if (IsBusy)
  44:                  throw new Validation.ValidationException(Resources.BusyObjectsMayNotBeSaved);
  45:              if (IsDirty)
  46:                  result = (T)DataPortal.Update(this);
  47:              else
  48:                  result = (T)this;
  49:              OnSaved(result, null, null);
  50:              return result;

 

  Over!

posted @ 2011-04-12 01:06 Simply 阅读(476) 评论(0) 编辑
2011年4月8日
摘要: 今晚整理了一下前段时间做的一个小型工作流应用程序,其核心的功能块是用的Csla框架试用开发,今天就对Csla框架中的命令对象做下个人理解。 众所周知,在工作流的开发过程中,可以分为流程定义过程、流程运行过程和工作任务的跟踪,特别是流程实例运行的过程中,主要的实现可以总结为流程实例创建、任务提交、任务回退、办结、抄送等等,其中的每个功能具有一定的相对独立性,而且大多数的操作都是命令提交后一次性办理,基于这种想法,自己在实现过程中对于上述的功能使用Csla框架的命令对象进行实现。 区别于Csla框架中的其他对象,可以说它的基本代码块是所有的对象类型中最简单的,代码中只有静态工厂方法及命令的执行代.阅读全文
posted @ 2011-04-08 00:08 Simply 阅读(459) 评论(0) 编辑
2011年4月6日
摘要: 今天在园子里看到一篇非常不错的文章,其中一句对是对加班与时间的说法:“没钱的人,时间就是你的财富.有钱人把钱花出去,得到收益.穷人把时间撒出去,也可以得到收益.”,看完后感受颇深,在这分享给大家:Href 回味过去一年的学习,很多的时候都是围绕着csla框架,从中汲取的一些想法及感受,刚看到桌上的两本csla的书(2005与2008),就翻开看了几页,正巧看到了属性一章,对于属性的一些学习回忆,一些想法,希望能给.net/C#初学者一些帮助。 csla框架中以一定的规则定义了很多功能的实现方式,其中很多的功能都是在封装属性的过程中实现的,诸如数据有效性验证,数据访问权限控制,以及之后的数据绑.阅读全文
posted @ 2011-04-06 22:24 Simply 阅读(303) 评论(1) 编辑
2011年4月1日
摘要: 进入新的公司也有段时间,折腾了很长时间心里才慢慢的平静下来,所以之前很多的话好像都成了空话,自己也说过,很多的学习过程想记录下来,但很多时候想法总是在不断的随时间而变化,就像之前在学习微软wf,开始时感觉它很强大,后来才发现被它的功能一直束缚着,虽然后来也是下了功能对它进行扩展,也实现了自定义工作流,但随着业务的不断变化,流程功能也不够用的了,搞到最后结果把wf的整个过程已经实现了大多数,到了最后还是毅然放弃了wf,自己来定义数据结构,今天的自己也抱着同样的心情,写下了这篇总结日志,也希望不久之后自己会有不同的想法。 新公司的规模并不大,当时唯一能说服自己留下的原因就是它的开发方式,经过这段.阅读全文
posted @ 2011-04-01 20:07 Simply 阅读(143) 评论(0) 编辑
2010年7月6日
摘要: 如标题,这里说的是CSLA框架中业务对象的分类,刚开始看到时很不理解,为什么对象还要分类呢,先来以相反的方向描述下平时大家(或者说是我的知识范围内)所应用的方法。  平常操作对象大都是简单的对象,这里说简单是指它只具备对象属性及对象行为这种单一功能(还有些只是数据填充器),如获取一个客户信息,我们只返回包装了客户信息数据的实体就可以,而要获取列表,只需要返回List<T>的列表,不管客...阅读全文
posted @ 2010-07-06 10:52 Simply 阅读(836) 评论(0) 编辑
2010年6月30日
摘要: 工作流这个词是去年11月才进入自己的大脑词汇,借着一点时间回顾下自己学习的过程,在这作个记录,希望和大家多多交流,里面的很多都是自己的片面之词,对于这么高科技的东西自己也没把握,还是希望多多指教,小弟感激不尽。  我是用的微软Workflow foundation作为基础框架来开发的,在这里还要提一下,工作流应该是属于一个抽象的名词,而微软的WF只是实现工作流的其中一种基础框架,所以理解这个fou...阅读全文
posted @ 2010-06-30 14:23 Simply 阅读(592) 评论(2) 编辑