/// <param name="id">标识</param>
protected EntityBase( Guid id ) { Id = id; } /// <summary>
/// 标识 /// </summary>
public Guid Id { get; private set; } } }
它们的唯一变化是Id数据类型不同,我们可以把Id类型设为object,从而支持所有类型。
namespace Util.Domains { /// <summary>
/// 领域实体 /// </summary>
public abstract class EntityBase{ /// <summary>
/// 初始化领域实体 /// </summary>
/// <param name="id">标识</param>
protected EntityBase( object id ) { Id = id; } /// <summary>
/// 标识 /// </summary>
public object Id { get; private set; } } }
但弱类型的object将导致装箱和拆箱,另外也不太易用,这时候是泛型准备登场的时候了。
namespace Util.Domains { /// <summary>
/// 领域实体 /// </summary>
/// <typeparam name="TKey">标识类型</typeparam>
public abstract class EntityBase<TKey> { /// <summary>
/// 初始化领域实体 /// </summary>
/// <param name="id">标识</param>
protected EntityBase( TKey id ) { Id = id; } /// <summary>
/// 标识 /// </summary>
[Required] public TKey Id { get; private set; } } }
将标识类型通过泛型参数TKey传进来,由于标识类型可以任意,所以不需要进行泛型约束。另外在Id上方加了一个Required特性,当Id为字符串或其它引用类型的时候,就能派上用场了。
下面要解决的问题是实体对象相等性比较,需要重写Equals,GetHashCode方法,另外需要重写==和!=两个操作符重载。
/// <summary>
/// 相等运算
/// </summary>
public override bool Equals( object entity )
{
if ( entity == null )
return false;
if ( !( entity is EntityBase<TKey> ) )
return false;
return this == (EntityBase<TKey>)entity;
}
/// <summary>
/// 获取哈希
/// </summary>
public override int GetHashCode()
{
return Id.GetHashCode();
}
/// <summary>
/// 相等比较
/// </summary>
/// <param name="entity1">领域实体1</param>
/// <param name="entity2">领域实体2</param>
public static bool operator ==( EntityBase<TKey> entity1, EntityBase<TKey> entity2 )
{
if ( (object)entity1 == null && (object)entity2 == null )
return true;
if ( (object)entity1 == null || (object)entity2 == null )
return false;
if ( entity1.Id == null )
return false;
if ( entity1.Id.Equals( default( TKey ) ) )
return false;
return entity1.Id.Equals( entity2.Id );
}
/// <summary>
/// 不相等比较
/// </summary>
/// <param name="entity1">领域实体1</param>
/// <param name="entity2">领域实体2</param>
public static bool operator !=( EntityBase<TKey> entity1, EntityBase<TKey> entity2 )
{
return !( entity1 == entity2 );
}
在操作符==的代码中,有一句需要注意,entity1.Id.Equals( default( TKey ) ),比如,一个实体的标识为int类型,这个实体在刚创建的时候,Id默认为0,另外创建一个该类的实例,Id也为0,那么这两个实体是相等还是不等?从逻辑上它们是不相等的,属于不同的实体, 只是标识目前还没有创建,可能需要等到保存到数据库中才能产生。这有什么影响呢?当进行某些集合操作时,如果你发现操作N个实体,但只有一个实体操作成功,那很有可能是因为这些实体的标识是默认值,而你的相等比较没有识别出来,这一句代码能够解决这个问题。
考虑领域实体基类还能帮我们干点什么,其实还很多,比如状态输出、初始化、验证、日志等。下面先来介绍一下状态输出。
当我在操作每个实体的时候,我经常需要在日志中记录完整的实体状态,即实体所有属性名值对的列表。这样方便我在查找问题的时候,可以了解某个实体当时是个什么情况。
要输出实体的状态,最方便的方法是重写ToString,然后把实体状态列表返回回来。这样ToString方法将变得有意义,因为它输出一个实体的类名基本没什么用。
要输出实体的全部属性值,一个办法是通过反射在基类中进行,但这可能会造成一点性能下降,由于通过代码生成器可以轻松生成这个操作,所以我没有采用反射的方法。
/// <summary>
/// 描述
/// </summary>
private StringBuilder _description;
/// <summary>
/// 输出领域对象的状态 |