在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识、相等性比较、输出实体状态等。本文将介绍领域实体的一个核心内容——验证,它是应用程序健壮性的基石。为了完成领域实体的验证,我们在前面已经准备好了验证公共操作类异常公共操作类

  .Net提供的DataAnnotations验证方法非常强大,Mvc会自动将DataAnnotations特性转换为客户端Js验证,从而提升了用户体验。但是客户端验证是靠不住的,因为很容易绕开界面向服务端提交数据,所以服务端必须重新验证。换句话说,服务端验证才是必须的,客户端验证只是为了提升用户体验而已。

  为了在服务端能够进行验证,Mvc提供了ModelState.IsValid。

[HttpPost]
public ActionResult 方法名( 实体名 model ) {
if ( ModelState.IsValid == false ) {
//验证失败就返回,可能会添加错误消息,也可能要转换为客户端能识别的消息格式
}
//验证成功就执行后面的代码
}

  在控制器里写if ( ModelState.IsValid == false )判断有几个问题,下面进行一些讨论。

  第一,可能误导初学者,导致分层不清。

  从分层架构的角度来讲,验证属于业务层,在DDD分层架构就是领域层。观察ModelState.IsValid可以发现,这句代码并不是在定义验证规则,而是调用验证。在控制器上直接调用验证可能并不是什么问题,但初学者可能会认为,既然可以在控制器上调用ModelState.IsValid进行验证,那么其它验证代码也可以放到控制器上。

        [HttpPost]
public ActionResult 方法名( 实体名 model ) {
if ( ModelState.IsValid == false ) {
//验证失败就返回
}
if ( model.A > ) {
//验证失败就返回
}
if ( model.B > ) {
//验证失败就返回
}
//验证成功就执行后面的代码
}

  观察上面代码,model.A > 1 已经将本属于领域层的验证定义规则泄露到表现层来了,因为这句代码访问了实体的属性,所谓验证规则,就是对实体属性值进行某些约束。

  既然可以在控制器上写验证,那么就会有人在这里写业务逻辑,所以到了后面,DDD分层架构如同虚设。

  第二,错误的验证时机可能导致验证失败。

  考虑这样的场景,如果实体中某些属性需要调用特定方法来产生结果,当提交到控制器操作时,这些属性还是空值,由于还没有调用特定方法,所以调用ModelState.IsValid可能导致验证失败。

  可以看出,这其实是因为验证的时机不对,验证几乎一定要在某些操作之后来进行,比如初始化操作,当然你可以在调用ModelState.IsValid之前调用特定方法,但这会导致分层不清的问题。

  打个比方,实体中有一个订单号,它是一个字符串类型,并且添加了[Required]特性,需要调用某个方法来创建订单号,当订单实体被提交到控制器操作时,调用ModelState.IsValid就会失败,因为订单号现在是空值。当然你可以把生成订单号的操作提前到创建订单界面之前,这样再提交过来就没问题了,在这个例子上一般是可行的,但有些操作你可能无法提前。

  第三,无法保证验证完整性,可能需要多次验证。

  很多时候,DataAnnotations无法满足我们的需求,所以我们还需要为特定业务需求写一些定制的验证代码。而ModelState.IsValid只能验证DataAnnotations特性,所以这时候验证通过意义不大,因为你需要在后面再验证一次。当然你可以通过一些手段进行扩展,让ModelState.IsValid能够验证你的特定规则,但没有多大必要,因为表现层在分层上的要点就是尽量不要写代码。

  第四,导致冗余代码。

  现在来观察每个ModelState.IsValid判断都干了些什么工作,一般都会转换成客户端的特定消息,比如某种格式的Json,然后返回给客户端显示出来。为了这样一个简单的功能,需要在大量的方法上添加这个判断吗?更好的方法是把这个判断抽象到控制器基类,由基类来进行处理,其它地方有错误抛出异常就可以了。这样可以得到一个统一的异常处理模型,并且消除了大量冗余代码。从这里也可以看出,打造你的应用程序框架,总是从这些不起眼的地方着手,反复考虑每个判断,每行代码是不是可以消灭,把尽量多的东西抽象到框架中,这样在开发过程中更多工作就会自动完成,不断提炼可以让你的工作越来越轻松。

  综上所述,在表现层进行验证并不是一个好方法,执行验证可以在应用层,而定义验证就一定要在领域层。下面开始介绍如何对领域实体进行验证支持。

  现在有一个员工实体,叫Employee,如下所示。

    /// <summary>
/// 员工
/// </summary>
public class Employee : EntityBase {
/// <summary>
/// 姓名
/// </summary>
[Required( ErrorMessage = "姓名不能为空" )]
public string Name { get; set; } /// <summary>
/// 性别
/// </summary>
[Required( ErrorMessage = "性别不能为空" )]
public string Gender { get; set; } /// <summary>
/// 年龄
/// </summary>
[Range(,,ErrorMessage = "年龄范围为18岁到50岁")]
public int Age { get; set; } /// <summary>
/// 职业
/// </summary>
[Required(ErrorMessage = "职业不能为空")]
public string Job { get; set; } /// <summary>
/// 工资
/// </summary>
public double Salary { get; set; }
}

  为了简单起见,我把一些东西简化了,比如性别用枚举更好,但用了字符串类型,而年龄根据出生年月推断会更好等等。这个例子只是想说明验证的方法,所以不用考虑它的真实性。

  可以看见,在员工实体的属性上添加了一些DataAnnotations特性,这些特性保证了基本的验证。现在定义了验证规则,那么怎么执行验证呢?前面已经说了,用ModelState.IsValid虽然可以实现这个功能,但不是最优方法,所以我们要另谋出路。

  执行验证的最简单方法可能长成这样:employee.Validate(),employee是Employee的实例,Validate是Employee中的一个实例方法。

  注意,现在我们在领域实体中定义了一个方法,这可能会打破你平时的习惯和认识。多年的习惯可能让你对实体的认识就是,只有一堆属性的对象。现在要把思维转变过来,这个转变至关重要,它是你进入面向对象开发的第一步。

  想想看,你现在要进行验证,应该上哪才能找到这个能执行验证的方法呢?如果它不在实体中,那么它可能在表现层,也可能在应用层,还可能在领域服务中,当然还有可能不存在,都还没人实现呢。

  所以我们需要给业务逻辑安家,这样才能帮你统一的管理业务逻辑,并提供唯一的访问点。这个家最好的地方就是实体本身,因为属性全都在这里面,属性上执行的逻辑也全部放进来,就能实现对象级别的高内聚。当属性和逻辑发生变化时,对外的方法接口可能不变,这时候所有变化引起的影响就被限制在实体内部,这样就达到了更低的耦合。

  下面,我们来实现Validate方法。

  首先考虑,这个方法应该被定义在哪呢?是不是每个实体上都定义一个,由于验证对于绝大部分实体都是必须的功能,所以需要定义到层超类型上,即EntityBase。

  再来考虑一下Validate的方法签名。需要一个返回值吗,比如bool值,我在之前的文章已经讨论了返回bool值来指示是否验证通过不是一个好方法,所以我们现在返回void。那么方法参数呢?由于现在是直接在实体上调用,所以参数也不是必须的。

        /// <summary>
/// 验证
/// </summary>
public void Validate() {
}

  为了实现这个方法,我们必须要能够验证实体上的DataAnnotations特性,这在前面的验证公共操作类已经准备好了。我们在Util.Validations命名空间中定义了IValidation接口,并使用企业库实现了这个接口。

  考虑在EntityBase的Validate方法中该如何获得IValidation的实例呢?依赖程度最低的方法是使用构造方法注入。

    /// <summary>
/// 领域实体
/// </summary>
/// <typeparam name="TKey">标识类型</typeparam>
public abstract class EntityBase<TKey> {
/// <summary>
/// 验证器
/// </summary>
private IValidation _validation; /// <summary>
/// 标识
/// </summary>
[Required]
public TKey Id { get; private set; } /// <summary>
/// 初始化领域实体
/// </summary>
/// <param name="id">标识</param>
/// <param name="validation">验证器</param>
protected EntityBase( TKey id, IValidation validation ) {
Id = id;
_validation = validation;
}
}

  在外部通过构造方法把需要的验证器实例传进来,这样甚至不需要在Util.Domains中引用任何程序集。这看起来很诱人,但不要盲目的追求低耦合。考虑验证器的稳定性,这应该非常高,你基本不会随便换掉它,更不会动态更换它。再看构造方法,多了一个参数,这会导致实体使用起来非常困难。所以为了不必要的扩展性牺牲易用性,并不划算。

  另一种方法是通过Validate方法的参数注入,这样可能要好些,但还是会让方法在调用时变得难用。

  应用程序框架只是给你或你的团队在小范围使用的,它不像.Net Framework或第三方框架在全球范围使用,所以你没有必要追求非常高的扩展性,如果发生变化导致你需要修改应用程序框架,你打开来改一下也不是啥大问题,因为框架和项目源码都在你的控制范围内,不见得非要达到OCP原则。当然,如果发生变化的可能性高,你还是需要考虑降低依赖。在依赖性和易用性间取舍,一定要根据实际情况,不要盲目追求低耦合。

  另外再考虑每个实体可能需要更换不同的验证器吗?如果需要,那就得引入工厂方法模式。由于这个验证器只是用来验证DataAnnotations特性的,所以没这必要。

  那么直接在EntityBase中new一个Validation实例好不好呢?嘿嘿,这我也只能说要求太低了。一个折中的方案是使用简单静态工厂,如果需要更换验证器实现,你就把这个工厂打开来改改,其它地方不动,一般来讲这已经够用。

  为Util.Domains引用Util.Validations.EntLib程序集,并在Util.Domains中添加ValidationFactory类。

using Util.Validations;
using Util.Validations.EntLib; namespace Util.Domains {
/// <summary>
/// 验证工厂
/// </summary>
public class ValidationFactory {
/// <summary>
/// 创建验证操作
/// </summary>
public static IValidation Create() {
return new Validation();
}
}
}

  在EntityBase类中添加Validate方法。

        /// <summary>
/// 验证
/// </summary>
public void Validate() {
var result = ValidationFactory.Create().Validate( this );
if ( result.IsValid )
return;
throw new Warning( result.First().ErrorMessage );
}

  我们在Validate方法中将领域实体本身传入Validation实例中进行验证,获得验证结果以后,判断如果验证失败就抛出异常,这里的异常是我们在上一篇定义的异常公共操作类Warning,这样我们就知道是业务上发生了错误,可以把这个抛出的消息显示给客户。

  完成了上面的步骤以后,就可以进行基本的验证了。但是只能用DataAnnotations进行基本验证,很明显无法满足我们的实际需求。

  现在来假想一个验证需求,你的老板是个好人,你们的人力资源系统也是自己开发的,他要求程序员老男人的工资不能小于一万。换句话说,如果是一个程序员老男人,他的信息被保存到数据库的时候,工资不能小于一万,否则就是非法数据。程序员老男人这个词汇很明显不存在,为了加深你的印象,用它来给你演示业务概念如何被映射到系统中。

  程序员老男人包含三个条件:

  1. 职业 == 程序员
  2. 年龄 > 40
  3. 性别 == 男

  你为了验证这个需求,能使用DataAnnotations特性吗,也许你真的可以,但是大部分人都做不到,哪怕做到也异常复杂。

  为了实现这个功能,你可能在调用了Validate()方法之后,紧接着进行判断。

 employee.Validate();
if ( employee.Job == "程序员" && employee.Age > && employee.Gender == "男" && employee.Salary < )
throw new Warning( "程序员老男人的工资不能低于1万" );

  如果你调用Validate是在应用层,这下好了,把验证逻辑泄露到应用层去了,很快,你的分层架构就会乱成一团。

  时刻记住,只要是业务逻辑,你就一定要放到领域层。验证是业务逻辑的一个重要组成部分,这就是说,没有验证,业务逻辑可能是错的,因为进来的数据不在合法范围。

  现在把这句判断移到Employee实体,最合适的地方就是Validate方法中,但这个方法是在基类EntityBase上定义的,为了能够给基类方法添加行为,可以把EntityBase中的Validate方法设为虚方法,这样子类就可以重写了。

  基类EntityBase中的Validate方法修改如下。

        /// <summary>
/// 验证
/// </summary>
public virtual void Validate() {
var result = ValidationFactory.Create().Validate( this );
if ( result.IsValid )
return;
throw new Warning( result.First().ErrorMessage );
}

  在Employee实体中重写Validate方法,注意必须调用base.Validate(),否则对DataAnnotations的验证将丢失。

        public override void Validate() {
base.Validate();
if ( Job == "程序员" && Age > && Gender == "男" && Salary < )
throw new Warning( "程序员老男人的工资不能低于1万" );
}

  对于应用层来讲,它并不关心具体怎么验证,它只知道调用employee.Validate()就行了。这样就把验证给封装了起来,为应用层提供了一个清晰而简单的API。

  一般说来,DataAnnotations和重写Validate方法添加自定义验证可以满足大部分领域实体的验证需求。但是,如果验证规则很多,而且很复杂,会发现重写的Validate方法很快变成一团乱麻。

  除了代码杂乱无章之外,还有一个问题是,业务概念被淹没在大量的条件判断中,比如Job == "程序员" && Age > 40 && Gender == "男" && Salary < 10000这个条件实际上代表的业务概念是程序员老男人的工资规则。

  另一个问题是,有些验证规则只在某些特定条件下进行,直接固化到实体中并不合适。

  当验证变得逐渐复杂时,就需要考虑将验证从实体中拆分出来。将一条验证规则封装到一个验证规则对象中,这就是规约模式在验证上的应用。规约的概念很简单,它是一个谓词,用来测试一个对象是否满足某些条件。规约的强大之处在于,将一堆相关的条件表达式封装起来,清晰的表达了业务概念。

  把程序员老男人的工资规则提取到一个OldProgrammerSalaryRule类中,如下所示。

    /// <summary>
/// 程序员老男人的工资验证规则
/// </summary>
public class OldProgrammerSalaryRule {
/// <summary>
/// 初始化程序员老男人的工资验证规则
/// </summary>
/// <param name="employee">员工</param>
public OldProgrammerSalaryRule( Employee employee ) {
_employee = employee;
} /// <summary>
/// 员工
/// </summary>
private readonly Employee _employee; /// <summary>
/// 验证
/// </summary>
public bool Validate() {
if ( _employee.Job == "程序员" && _employee.Age > && _employee.Gender == "男" && _employee.Salary < )
return false;
return true;
}
}

  上面的验证规则对象,通过构造方法接收业务实体,然后通过Validate方法进行验证,如果验证失败就返回false。

  返回bool值的一个问题是,错误描述就拿不到了。为了获得错误描述,我把返回类型从bool改成ValidationResult。

using System.ComponentModel.DataAnnotations; 

namespace Util.Domains.Tests.Samples {
/// <summary>
/// 程序员老男人的工资验证规则
/// </summary>
public class OldProgrammerSalaryRule {
/// <summary>
/// 初始化程序员老男人的工资验证规则
/// </summary>
/// <param name="employee">员工</param>
public OldProgrammerSalaryRule( Employee employee ) {
_employee = employee;
} /// <summary>
/// 员工
/// </summary>
private readonly Employee _employee; /// <summary>
/// 验证
/// </summary>
public ValidationResult Validate() {
if ( _employee.Job == "程序员" && _employee.Age > && _employee.Gender == "男" && _employee.Salary < )
return new ValidationResult( "程序员老男人的工资不能低于1万" );
return ValidationResult.Success;
}
}
}

  验证规则对象虽然抽出来了,但是在哪调用它呢?最好的地方就是领域实体的Validate方法,因为这样应用层将非常简单。

  为了能够在领域实体的Validate方法中调用验证规则对象,需要将验证规则添加到该实体中,这可以在Employee中增加一个AddValidationRule方法。

    /// <summary>
/// 员工
/// </summary>
public class Employee : EntityBase {
//构造方法和属性 /// <summary>
/// 验证规则集合
/// </summary>
private List<OldProgrammerSalaryRule> _rules; /// <summary>
/// 添加验证规则
/// </summary>
/// <param name="rule">验证规则</param>
public void AddValidationRule( OldProgrammerSalaryRule rule ) {
if ( rule == null )
return;
_rules.Add( rule );
} /// <summary>
/// 验证
/// </summary>
public override void Validate() {
base.Validate();
foreach ( var rule in _rules ) {
var result = rule.Validate();
if ( result == ValidationResult.Success )
continue;
throw new Warning( result.ErrorMessage );
}
}
}

  如果另一个领域实体需要使用验证规则,就要复制代码过去改一下,这显然是不行的,所以需要把添加验证规则抽到基类EntityBase中。为了支持这个功能,首先要为验证规则抽象出一个接口,代码如下。

using System.ComponentModel.DataAnnotations; 

namespace Util.Validations {
/// <summary>
/// 验证规则
/// </summary>
public interface IValidationRule {
/// <summary>
/// 验证
/// </summary>
ValidationResult Validate();
}
}

  在EntityBase中添加AddValidationRule方法,并修改Validate方法,代码如下。

        /// <summary>
/// 验证规则集合
/// </summary>
private readonly List<IValidationRule> _rules; /// <summary>
/// 添加验证规则
/// </summary>
/// <param name="rule">验证规则</param>
public void AddValidationRule( IValidationRule rule ) {
if ( rule == null )
return;
_rules.Add( rule );
} /// <summary>
/// 验证
/// </summary>
public virtual void Validate() {
var result = ValidationFactory.Create().Validate( this );
foreach ( var rule in _rules )
result.Add( rule.Validate() );
if ( result.IsValid )
return;
throw new Warning( result.First().ErrorMessage );
}

  现在让OldProgrammerSalaryRule实现IValidationRule接口,应用层可以像下面这样调用。

employee.AddValidationRule( new OldProgrammerSalaryRule( employee ) );
employee.Validate();

  可以在几个地方为领域实体设置验证规则对象。

  1. 领域实体的构造方法中。
  2. 具体的领域实体重写Validate方法中。
  3. 当工厂创建领域实体(聚合)时。
  4. 领域服务或应用服务调用领域实体进行验证时。

  设置验证规则的要点是,稳定的验证规则尽量放到实体中,以方便使用。

  现在还有一个问题是,验证处理是抛出一个异常,这个异常的消息设置为验证结果集合的第一个消息。这在大部分时候都够用了,但是某些时候对错误的处理会有所不同,比如你现在要显示全部验证失败的消息,这时候将要修改框架。所以把验证的处理提取出来是个不错的方法。

  定义一个验证处理的接口IValidationHandler,这个验证处理接口有一个Handle的处理方法,接收一个验证结果集合的参数,代码如下。

    /// <summary>
/// 验证处理器
/// </summary>
public interface IValidationHandler {
/// <summary>
/// 处理验证错误
/// </summary>
/// <param name="results">验证结果集合</param>
void Handle( ValidationResultCollection results );
}

  由于只需要在特殊情况下更换验证处理实现,所以定义一个默认的实现,代码如下。

    /// <summary>
/// 默认验证处理器,直接抛出异常
/// </summary>
public class ValidationHandler : IValidationHandler{
/// <summary>
/// 处理验证错误
/// </summary>
/// <param name="results">验证结果集合</param>
public void Handle( ValidationResultCollection results ) {
if ( results.IsValid )
return;
throw new Warning( results.First().ErrorMessage );
}
}

  为了能够更换验证处理器,需要在EntityBase中提供一个方法SetValidationHandler,代码如下。

        /// <summary>
/// 验证处理器
/// </summary>
private IValidationHandler _handler; /// <summary>
/// 设置验证处理器
/// </summary>
/// <param name="handler">验证处理器</param>
public void SetValidationHandler( IValidationHandler handler ) {
if ( handler == null )
return;
_handler = handler;
}

  在EntityBase构造方法中初始化_handler = new ValidationHandler(),并修改Validate方法。

        /// <summary>
/// 验证
/// </summary>
public virtual void Validate() {
var result = ValidationFactory.Create().Validate( this );
foreach ( var rule in _rules )
result.Add( rule.Validate() );
if ( result.IsValid )
return;
_handler.Handle( result );
}

  最后,用提取方法重构来改善一下Validate代码。

        /// <summary>
/// 验证
/// </summary>
public virtual void Validate() {
var result = GetValidationResult();
HandleValidationResult( result );
} /// <summary>
/// 获取验证结果
/// </summary>
private ValidationResultCollection GetValidationResult() {
var result = ValidationFactory.Create().Validate( this );
Validate( result );
foreach ( var rule in _rules )
result.Add( rule.Validate() );
return result;
} /// <summary>
/// 验证并添加到验证结果集合
/// </summary>
/// <param name="results">验证结果集合</param>
protected virtual void Validate( ValidationResultCollection results ) {
} /// <summary>
/// 处理验证结果
/// </summary>
private void HandleValidationResult( ValidationResultCollection results ) {
if ( results.IsValid )
return;
_handler.Handle( results );
}

  注意,这里添加了一个Validate( ValidationResultCollection results )虚方法,这是一个钩子方法,提供它的目的是允许子类向ValidationResultCollection中添加自定义验证的结果。它和重写Validate()方法的区别是,如果重写Validate()方法,那么你将需要自己处理验证,而Validate( ValidationResultCollection results )方法将以统一的方式被handler处理。

  这样,我们就实现了验证规则定义与验证处理的分离。

  最后,再对这个小例子完善一下,可以将“程序员老男人”这个概念封装到Employee的一个方法中。

        /// <summary>
/// 是否程序员老男人
/// </summary>
public bool IsOldProgrammer() {
return Job == "程序员" && Age > && Gender == "男";
}

  OldProgrammerSalaryRule验证规则的实现修改为如下代码。

        /// <summary>
/// 验证
/// </summary>
public ValidationResult Validate() {
if ( _employee.IsOldProgrammer() && _employee.Salary < )
return new ValidationResult( "程序员老男人的工资不能低于1万" );
return ValidationResult.Success;
}

  这样不仅概念上更清晰,而且当多个地方需要对“程序员老男人”进行验证时,还能体现出更强的封装性。

  由于代码较多,完整代码就不粘贴了,如有需要请自行下载。

  如果你有更好的验证方法,请一定要告诉我,等我理解以后分享给大家。

  .Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

  谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

  下载地址:http://files.cnblogs.com/xiadao521/Util.2014.11.20.1.rar

应用程序框架实战十五:DDD分层架构之领域实体(验证篇)的更多相关文章

  1. 应用程序框架实战十四:DDD分层架构之领域实体(基础篇)

    上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示例代码. 什么是实体 由标识来区分的 ...

  2. 应用程序框架实战十八:DDD分层架构之聚合

    前面已经介绍了DDD分层架构的实体和值对象,本文将介绍聚合以及与其高度相关的并发主题. 我在之前已经说过,初学者第一步需要将业务逻辑尽量放到实体或值对象中,给实体“充血”,这样可以让业务逻辑高度内聚, ...

  3. 应用程序框架实战十六:DDD分层架构之值对象(介绍篇)

    前面介绍了DDD分层架构的实体,并完成了实体层超类型的开发,同时提供了验证方面的支持.本篇将介绍另一个重要的构造块——值对象,它是聚合中的主要成分. 如果说你已经在使用DDD分层架构,但你却从来没有使 ...

  4. 应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)

    前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持. 什么是仓储 仓储表示聚合的集合. 仓储所表现出来的集合外观,仅仅是一种模 ...

  5. DDD分层架构之领域实体(验证篇)

    DDD分层架构之领域实体(验证篇) 在应用程序框架实战十四:DDD分层架构之领域实体(基础篇)一文中,我介绍了领域实体的基础,包括标识.相等性比较.输出实体状态等.本文将介绍领域实体的一个核心内容—— ...

  6. DDD分层架构之领域实体(基础篇)

    DDD分层架构之领域实体(基础篇) 上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示 ...

  7. 应用程序框架(一):DDD分层架构:领域实体(基础篇)

    一.什么是实体 由标识来区分的对象称为实体. 实体的定义隐藏了几个信息: 两个实体对象,只要它们的标识属性值相等,哪怕标识属性以外的所有属性值都不相等,这两个对象也认为是同一个实体,这意味着两个对象是 ...

  8. 应用程序框架实战二十二 : DDD分层架构之仓储(层超类型基础篇)

    前一篇介绍了仓储的基本概念,并谈了我对仓储的一些认识,本文将实现仓储的基本功能. 仓储代表聚合在内存中的集合,所以仓储的接口需要模拟得像一个集合.仓储中有很多操作都是可以通用的,可以把这部分操作抽取到 ...

  9. Apworks框架实战(五):EasyMemo的领域模型设计

    在上一讲中,我们已经新建了一个聚合根对象Account,并已经可以开始设计领域模型了.在这一讲中,我们会着重介绍EasyMemo领域模型的分析和设计,并引入Visual Studio Ultimate ...

随机推荐

  1. android开发中使不同的listview同时联动

    在做一个Android程序时,需要在一个屏幕上显示两个不同的listview,开始用< linearlayout>包裹这两个listview在<ScrollView >设置时, ...

  2. C# DataTable的Select()方法不支持 != 判断

    异常描述: 用户代码未处理 System.Data.SyntaxErrorException HResult=-2146232032 Message=无法解释位置 23 的标记“!”. Source= ...

  3. javascript 全局对象--w3school

    JavaScript全局对象 1.  decodeURI()解析某个编码的URI. 2.decodeURInComponent()解析一个编码的URI组件. 3.encodeURI()把字符串编码为U ...

  4. 深入浅出HTTP协议(WEB开发和面试必备)

    1. 基础概念篇   a.简介 HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写.它的发展是万维网协会(World Wide Web Consortium)和 ...

  5. ESLint 规则

    ESLint由 JavaScript 红宝书 作者 Nicholas C.Zakas 编写, 2013 年发布第一个版本. ESLint是一个以可扩展.每条规则独立的,被设计为完全可配置的lint工具 ...

  6. js实现div居中

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  7. 关于DOM的一些笔记(一)

    这篇文章整理的是关于DOM的一些学习笔记,这样以后查找起来也方便许多.(以前js看的是入门经典和DOM编程艺术,现在在看高级程序设计,本文就以高级程序为主整理) 1.Node (1):类型 node. ...

  8. SQL Server 高级sql总结

    ----------------------------------------------------------------------Select------------------------ ...

  9. Linq中使用反射实现--LINQ通用数据表绑定DataGrid控件的方法(原创)

    项目需求,因为项目中存在很多表,这些表的内容需要呈现给客户浏览.转载请注明出处 相信很多写过程序的设计者很容易的用以下方式实现 在SqlConnect ,DataSet 的方式,我们很轻松的可以通过S ...

  10. 多线程相关------临界区CriticalSection

    多线程一直是短板,整理相关知识方便查询 临界区(Critical Section) 临界区是一段供线程独占式访问的代码.在任意时刻,若有一个线程正在访问该代码段,如果其他所有试图访问的线程都将被挂起, ...