实体

实体是DDD(域驱动设计)的核心概念之一。Eric Evans将其描述为“ 一个不是由其属性从根本上定义的对象,而是一个连续性和身份的线程 ”。

实质上,实体具有Id并存储在数据库中。实体通常映射到关系数据库中的表。

实体类

在ASP.NET Boilerplate中,实体派生自Entity类。请参阅以下示例:

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

    public virtual DateTime CreationTime { get; set; }

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

类被定义为一个实体。它有两个属性以及Entity基类中定义的 Id属性。编号主键的实体。所有实体的主键名称相同,为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;
    }
}

您可以将其设置为字符串,Guid或其他内容。

实体类重写等于运算符(==)以轻松检查两个实体是否相等(它们的Id相等)。它还定义了 IsTransient()方法来检查它是否具有Id。

AggregateRoot类

“ 聚合是域驱动设计中的模式.DDD聚合是一个域对象的集群,可以被视为一个单元。例如,订单及其行项目,这些将是单独的对象,但它是有用的将订单(连同其订单项)视为单一聚合。 “(Martin Fowler - 请参阅完整说明

虽然ABP不强制您使用聚合,但您可能希望在应用程序中创建聚合和聚合根。ABP定义了AggregateRoot类,该类扩展了实体以为聚合创建聚合根实体。

域事件

AggregateRoot定义DomainEvents 集合以通过聚合根类生成域事件。这些事件在当前工作单元完成之前自动触发事实上,任何实体都可以通过实现IGeneratesDomainEvents接口生成域事件,但在聚合根中生成域事件是常见的(最佳实践)。这就是为什么它是AggregateRoot的默认值而不是Entity类的默认值。

传统接口

在许多应用程序中,使用类似的实体属性(和数据库表字段),如CreationTime,它指示何时创建实体。ASP.NET Boilerplate提供了一些有用的接口,使这些公共属性具有显性和表达能力。这为实现这些接口的实体提供了编码公共代码的方法。

审计

IHasCreationTime使得可以将公共属性用于实体的“ 创建时间 ”信息。实现此接口后,ASP.NET Boilerplate会自动将CreationTime设置为将实体插入数据库时当前时间

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

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

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; }
}

在保存新实体时,ASP.NET Boilerplate会自动将CreatorUserId属性设置为当前用户的id您还可以通过从CreationAuditedEntity派生您的实体轻松实现ICreationAudited 它还具有针对不同类型的Id属性的通用版本。

还有类似的修改接口:

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

public interface IModificationAudited : IHasModificationTime
{
    long? LastModifierUserId { get; set; }
}

更新实体时,ASP.NET Boilerplate会自动设置这些属性。您只需为您的实体定义它们即可。

如果要实现所有审计属性,可以直接实现IAudited接口:

public interface IAudited : ICreationAudited, IModificationAudited
{

}

作为一种快捷方式,您可以从AuditedEntity派生,而不是直接实现IAuditedAuditedEntity类还具有针对不同类型的Id属性的通用版本。

注意:ASP.NET Boilerplate从ABP会话中获取当前用户的ID 

软删除

软删除是一种常用模式,用于将实体标记为已删除,而不是实际从数据库中删除它。例如,您可能不想从数据库中删除用户,因为它与其他表有很多关系。ISoftDelete接口用于此目的:

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

ASP.NET Boilerplate实现了开箱即用的软删除模式。删除软删除实体时,ASP.NET Boilerplate会检测到此情况,阻止删除,将IsDeleted设置为true,然后更新数据库中的实体。它也不会通过自动过滤它们来从数据库中检索(选择)软删除的实体。

如果使用软删除,您可能还希望存储删除实体和删除实体的信息。您可以实现 IDeletionAudited接口,如下所示:

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

    DateTime? DeletionTime { get; set; }
}

您可能已经注意到,IDeletionAudited扩展了ISoftDelete。删除实体时,ASP.NET Boilerplate会自动设置这些属性。

如果要实现实体的所有审计接口(创建,修改和删除),可以直接实现IFullAudited, 因为它继承自其他实体

public interface IFullAudited : IAudited, IDeletionAudited
{

}

作为一种快捷方式,您可以从 实现它们FullAuditedEntity派生您的实体

主动/被动实体

某些实体需要标记为“主动”或“被动”。您可以对实体的主动/被动状态采取措施。您可以实现为此原因创建IPassivable接口。它定义了IsActive属性。

如果您的实体在第一次创建时处于活动状态,则可以在构造函数中将IsActive设置为true。

这与软删除(IsDeleted)不同。如果实体被软删除,则无法从数据库中检索它(默认情况下ABP会阻止它)。对于主动/被动实体,完全取决于您如何控制这些实体的检索。

实体变更事件

在插入,更新或删除实体时,ASP.NET Boilerplate会自动触发某些事件。您可以注册这些事件并执行您需要的任何逻辑。有关详细信息,请参阅事件总线文档中的“预定义事件”部分

IEntity接口

实体类实现IEntity接口(和 实体<TPrimaryKey>实现 IEntity <TPrimaryKey> )。如果您不想从Entity类派生,则可以直接实现这些接口。其他实体类也有相应的接口。我们不建议您这样做,除非您有充分的理由不从实体类派生。

多语言实体

ASP.NET Boilerplate提供了一种定义和使用多语言实体的简单方法。有关更多信息,请参阅多语言实体

IExtendableObject接口

ASP.NET Boilerplate提供了一个简单的接口IExtendableObject,可以轻松地将任意名称 - 值数据与实体相关联考虑这个简单的实体:

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

    public string ExtensionData { get; set; }

    public Person(string name)
    {
        Name = name;
    }
}

IExtendableObject定义ExtensionData字符串属性,该属性用于存储JSON格式的名称值对象。例:

var person = new Person("John");

person.SetData("RandomValue", RandomHelper.GetRandom(1, 1000));
person.SetData("CustomData", new MyCustomObject { Value1 = 42, Value2 = "forty-two" });

我们可以使用任何类型的对象作为SetData方法的值当我们使用上面的代码时,ExtensionData将如下所示:

{"CustomData":{"Value1":42,"Value2":"forty-two"},"RandomValue":178}

然后我们可以使用GetData来获取值:

var randomValue = person.GetData<int>("RandomValue");
var customData = person.GetData<MyCustomObject>("CustomData");

虽然这种技术在某些情况下非常有用(当您需要提供向实体动态添加额外数据的能力时),但通常应该使用常规属性。这种动态使用不是类型安全和明确的。

nidie.com.cn - 用心与你沟通