ABP理论学习之实体类

返回总目录


本篇目录

实体是DDD(领域驱动设计)的核心概念之一。Eirc Evans是这样描述的实体的:“它根本上不是通过属性定义的,而是通过一系列连续性和标识定义的”。因此,实体都有Id属性并且都存储到数据库中。一个实体一般会映射到数据库的一张表。

实体类

在ABP中,实体派生自Entity类,看下面的例子:

public class Person : Entity
{
    public virtual string Name { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public Person()
    {
        CreationTime = DateTime.Now;
    }
}

上面定义了一个Person实体类,而且在Entity类中定义了一个Id属性,它是该Entity类的 主键。因此,所有实体的主键名都是相同的,都是Id

Id(主键)的类型是可以改变的,默认是int(int32)的。如果你想将Id定义为其他类型,可以像下面那样显示声明:

public class Person : Entity<long>
{
    public virtual string Name { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public Person()
    {
        CreationTime = DateTime.Now;
    }
}

而且,你也可以把它设置为string,Guid或其他类型的。

Entity类重写了等号运算符(==),可以轻松地检查两个实体是否相同了(实体的Id相同则认为它们相同)。它也定义了IsTransient方法来检测它是否有Id。

惯例接口

在许多应用中,使用了相似的实体属性(和数据表中的字段),如CreationTime表明该实体是何时创建的。ABP提供了很多有用的接口来使得这些通用的属性变得明确并富有表现力。此外,这也为实现了这些接口的实体类提供了一种编写通用代码的方式。

审计

IHasCreationTime使得使用一个通用的属性来描述一个实体的“创建时间”信息成为可能。当实现了该接口的实体类插入到数据库中时,ABP会自动地将当前的时间设置给CreationTime。

public interface IHasCreationTime
{
    DateTime CreationTime { get; set; }
}

Person类可以通过实现IHasCreationTime接口来重写,如下所示:

public class Person : Entity<long>, IHasCreationTime
{
    public virtual string Name { get; set; }

    public virtual DateTime CreationTime { get; set; }

    public Person()
    {
        CreationTime = DateTime.Now;
    }
}

ICreationAudited通过增加了CreatorUserId扩展了IHasCreationTime:

public interface ICreationAudited : IHasCreationTime
{
    long? CreatorUserId { get; set; }
}

当保存一个新的实体时,ABP会自动地将当前的用户Id设置为CreatorUserId。

你也可以通过从CreationAuditedEntity类派生实体,从而轻易地实现ICreationAudited。

对于修改也有相似的接口:

public interface IModificationAudited
{
    DateTime? LastModificationTime { get; set; }

    long? LastModifierUserId { get; set; }
}

当更新一个实体的时候,ABP也会自动地设置这些属性。你只需要为你的实体实现这些接口即可。

如果你想实现所有的审计属性,那么你可以直接实现IAudited接口:

public interface IAudited : ICreationAudited, IModificationAudited
{
        
}

作为一个快捷方式,你可以从AuditedEntity类派生,而不需要直接实现IAudited。AuditedEntity类对于不同类型的Id属性也有泛型的版本。

软删除

软删除是将一个实体标记为已删除的通常使用的模式,而不是直接从数据库中删除。比如,你可能不想从数据库中硬删除一个User,因为它可能关联其他的表。ISoftDelete接口用于下面的目的:

public interface ISoftDelete
{
    bool IsDeleted { get; set; }
}

ABP实现了开箱即用的软删除模式。当一个软删除实体被删除后,ABP检测到之后,会阻止删除,将IsDeleted设置为true并更新数据库中的实体。而且,它会自动地过滤数据库中软删除的实体,不会检索(select)它们。

如果使用了软删除,那么你可能想存储一些信息,比如何时删除以及谁删除了一个实体等等。你可以实现下面演示的IDeletionAudited接口:

public interface IDeletionAudited : ISoftDelete
{
    long? DeleterUserId { get; set; }

    DateTime? DeletionTime { get; set; }
}

IDeletionAudited扩展了ISoftDelete,当删除一个实体时,ABP会自动设置这些属性。

如果你想为一个实体实现所有的审计接口(创建,修改和删除),那么可以直接实现IFullAudited,因为它继承了所有的这些接口:

public interface IFullAudited : IAudited, IDeletionAudited
{
        
}

同样的,作为一个快捷方式,你可以从FullAuditedEntity类派生你的实体类从而实现所有的审计接口。

注意:所有的审计接口和类都有一个定义导航属性到User实体的泛型版本(比如ICreationAudited和FullAuditedEntity<TPrimaryKey,TUser>)。

激活/未激活

一些实体需要标记为激活的或未激活的。这样,你就可以根据实体的激活或者未激活状态来采取行动。你可以实现IPassivable接口来达到目的。该接口定义了IsActive属性。

如果实体在第一次创建时是激活的,那么你可以在构造函数中将IsActive设置为true。

这与软删除(IsDeleted)是不同的。如果一个实体是软删除的,那么它就不会从数据库中检索到了(ABP默认会阻止),但是,对于激活或者未激活的实体,控制获取实体完全取决于你。

IEntity接口

实际上,Entity类实现了IEntity接口(且Entity实现了IEntity)。如果不想从Entity类中派生,那么可以直接实现这些接口。但是,除非你有一个好的原因不从Entity类派生,否则,不建议这么做。

posted @ 2015-12-20 22:57 tkbSimplest 阅读(...) 评论(...) 编辑 收藏