Entities are one of the core concepts of DDD (Domain Driven Design). Eric Evans describe it as "An object that is not fundamentally defined by its attributes, but rather by a thread of continuity and identity". So, entities have Id's and stored in a database. An entity is generally mapped to a table for relational databases.
实体是DDD(领域驱动设计)的核心概念之一。Eric Evans把它描述为“一个没有被它的属性基本定义的对象,而是一个连续性和同一性的线程”。因此,实体拥有id并存储在数据库中。实体通常映射到关系数据库的表。
Entity Class(实体类)
In ASP.NET Boilerplate, 实体是从实体类派生的. See the sample below:
public class Person : Entity { public virtual string Name { get; set; } public virtual DateTime CreationTime { get; set; } public Person() { CreationTime = DateTime.Now; } }
Person class is defined as an entity. It has two properties. Also, Entity class defines an Id property. It's primary key of the Entity. So, name of primary keys of all Entities are same, it's Id.
Type of Id (primary key) can be changed. It's int (Int32) by default. If you want to define another type as Id, you should explicitly declare it as shown below:
人员类定义为一个实体。它有两个属性。此外,实体类定义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; } }
Also, you can set it as string, Guid or something else.
Entity class overrides equality operator (==) to easily check if two entities are equal (their Id is equal). It also defines the IsTransient() method to check if it has an Id or not.
另外,可以将其设置为字符串、GUID或其他内容。
实体类重写相等运算符(=)以轻松检查两个实体是否相等(它们的ID是相等的)。它还定义了istransient()方法检查它是否有一个ID或不。
AggregateRoot Class(聚合根类)
"Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate." (Martin Fowler - see full description)
“聚合是领域驱动设计中的一种模式。DDD聚合是一组域对象,可以作为单个单元处理。一个例子可能是一个订单和它的行项目,它们将是单独的对象,但是将订单(连同它的行项目)作为单个集合处理是有用的。”(Martin Fowler -参见完整描述)
While ABP does not enforce you to use aggregates, you may want to create aggregates and aggregate roots in your application. ABP defines AggregateRoot class that extends Entity to create aggregate root entities for an aggregate.
虽然ABP不强制您使用聚合,但您可能希望在应用程序中创建聚合和聚合根。 ABP定义聚合根类,为了类扩展的实体创建一个聚集根实体。
(1)Domain Events(领域事件)
AggregateRoot defines DomainEvents collection to generate domain events by the aggregate root class. These events are automatically triggered just before the current unit of work is completed. Actually, any entity can generate domain events by implementing IGeneratesDomainEvents interface, but it's common (best practice) to generate domain events in aggregate roots. That's why it's default for AggregateRoot but not for Entity class.
聚合根定义领域事件集合,通过聚合根类自动生成领域事件。这些事件是在当前工作单元完成之前自动触发的。事实上,任何实体可以通过继承 Igeneratesdomainevents接口自动生成领域的事件,但它是常见的(最佳实践),用聚合根自动生成领域事件。这就是为什么它的默认聚合根而不是实体类。
Conventional Interfaces(接口约定)
In many application, similar entity properties (and database table fields) are used like CreationTime indicates that when this entity is created. ASP.NET Boilerplate provides some useful interfaces to make this common properties explicit and expressive. Also, this provides a way of coding common code for Entities which implement these interfaces.
在许多应用中,类似的实体属性(和数据库表的字段)是用于 CreationTime表明这个实体的创建。ASP.NET的模板提供了一些有用的接口,使这个共同的属性的明确和有表达力。此外,这为实现这些接口的实体提供了一种编码公共代码的方法。
(1)Auditing(审计)
IHasCreationTime makes it possible to use a common property for 'creation time' information of an entity. ASP.NET Boilerplate automatically sets CreationTime to current time when an Entity is inserted into database which implements this interface.
ihascreationtime使得它可以使用一个共同的属性的一个实体的创建时间信息。ASP.NET样板自动设置的创建时间为当前时间当实体插入到数据库中,只要实现这个接口
public interface IHasCreationTime { DateTime CreationTime { get; set; } }
Person class can be re-written as shown below by implementing IHasCreationTime interface:
人员类能被重写如下通过实现 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 extens IHasCreationTime by adding CreatorUserId:
ICreationAudited扩展IHasCreationTime,增加 CreatorUserId属性
public interface ICreationAudited : IHasCreationTime { long? CreatorUserId { get; set; } }
ASP.NET Boilerplate automatically sets CreatorUserId to current user's id when saving a new entity. You can also implement ICreationAudited easily by deriving your entity from CreationAuditedEntity class. It has also a generic version for different type of Id properties.
ASP.NET 样板自动设置CreatorUserId作为当前用户的ID,当存储一个新的实体时。你也可以很容易的实现icreationaudited,通过CreationAuditedEntity获得你的实体类。它还有一个通用版本,用于不同类型的id属性。
There is also similar interfaces for modifications:
public interface IHasModificationTime { DateTime? LastModificationTime { get; set; } } public interface IModificationAudited : IHasModificationTime { long? LastModifierUserId { get; set; } }
ASP.NET Boilerplate also automatically sets these properties when updating an entity. You just define them for your entity.
If you want to implement all of audit properties, you can direcly implement IAudited interface:
ASP.NET样板也自动设置这些属性,当一个实体时更新。你只需为你的实体定义它们。
如果你想实现所有审计的属性,你可以直接实现iaudited接口:
public interface IAudited : ICreationAudited, IModificationAudited { }
As a shortcut, you can derive from AuditedEntity class instead of direcly implementing IAudited. AuditedEntity class has also a generic version for different type of Id properties.
Note: ASP.NET Boilerplate gets current user's Id from ABP Session.
作为一种快捷方式,你可以从auditedentity类而不是直接实现iaudited。auditedentity类也有不同类型的ID属性的通用版本。
注:ASP.NET样板从会话获取当前用户的ID ABP。
(2)Soft Delete(软删除)
Soft delete is a commonly used pattern to mark an Entity as deleted instead of actually deleting it from database. For instace, you may not want to hard delete a User from database since it has many releations to other tables.ISoftDelete interface is used for this purpose:
软删除是一种常用模式,用于将实体标记为已删除,而不是实际从数据库中删除它。如,你可能不想从数据库中删除用户因为它有许多关系到其他tables.isoftdelete接口用于此目的的:
public interface ISoftDelete { bool IsDeleted { get; set; } }
ASP.NET Boilerplate implements soft delete pattern out-of-the-box. When a soft-delete entity is being deleted, ASP.NET Boilerplate detects this, prevents deleting, sets IsDeleted as true and updates entity in the database. Also, it does not retrive (select) soft deleted entities from database, automatically filters them.
ASP.NET样板实现软删除模式开箱。当软删除实体被删除,ASP.NET样板检测,防止删除、设置isDeleted作为真正的更新数据库实体。而且,它没有获得(选择)软删除实体数据库,自动过滤。
If you use soft delete, you may also want to store information when an entity is deleted and who deleted it. You can implement IDeletionAudited interface that is shown below:
如果你使用软删除,则可能希望在删除实体和谁删除它时记录信息。你可以实现ideletionaudited接口,如下图所示:
public interface IDeletionAudited : ISoftDelete { long? DeleterUserId { get; set; } DateTime? DeletionTime { get; set; } }
IDeletionAudited extends ISoftDelete as you noticed. ASP.NET Boilerplate automatically sets these properties when an entity is deleted.
If you want to implement all audit interfaces (creation, modification and deletion) for an entity, you can directly implement IFullAudited since it inherits all:
当你注意到 ideletionaudited扩展isoftdelete 。ASP.NET样板自动设置这些属性,当一个实体被删除。
如果你想实现所有审计接口(创建,修改和删除)一个实体,可以直接实现ifullaudited因为它继承了所有:
public interface IFullAudited : IAudited, IDeletionAudited { }
As a shortcut, you can derive your entity from FullAuditedEntity class that implements all.
- NOTE 1: All audit interfaces and classes have a generic version for defining navigation property to your User entity (like ICreationAudited<TUser> and FullAuditedEntity<TPrimaryKey, TUser>).
-
NOTE 2: Also, all of them has an AggregateRoot version, like AuditedAggregateRoot.
-
作为一种快捷方式,你可以从fullauditedentity得到你的实体类,实现了所有。
注1:所有审计接口和类都定义导航属性用户实体的通用版本(如 ICreationAudited<TUser> and FullAuditedEntity<TPrimaryKey, TUser>)。
注2:同时,他们都有一个aggregateroot版,如AuditedAggregateRoot。
(3)Active/Passive Entities(激活/闲置实体)
Some entities need to be marked as Active or Passive. Then you may take action upon active/passive state of the entity. You can implement IPassivable interface that is created for this reason. It defines IsActive property.
If your entity will be active on first creation, you can set IsActive to true in the constructor.
This is different than soft delete (IsDeleted). If an entity is soft deleted, it can not be retrieved from database (ABP prevents it as default). But, for active/passive entities, it's completely up to you to control getting entities.
一些实体需要标记为激活或闲置。然后你可以对实体的激活或闲置状态采取行动。你可以实现ipassivable接口就是这个原因了。它定义了IsActive属性。
如果你的实体将第一次创作是激活的,你可以设置 IsActive = true在构造函数。
这是不同于软删除(isDeleted)。如果一个实体被软删除,它不能从数据库中检索(ABP防止它作为默认)。但是,对于激活/闲置实体,完全由您来控制获取实体。
Entity Change Events(实体改变事件)
ASP.NET Boilerplate automatically triggers certain events when an entity is inserted, updated or deleted. Thus, you can register to these events and perform any logic you need. See Predefined Events section in event bus documentation for more information.
ASP.NET样板自动触发某些事件时,一个实体的插入、更新或删除。因此,您可以注册这些事件并执行您需要的任何逻辑。有关详细信息,请参阅事件总线文档中的预定义事件部分。
IEntity Interfaces(IEntity 接口)
Actually, Entity class implements IEntity interface (and Entity<TPrimaryKey> implements IEntity<TPrimaryKey>). If you do not want to derive from Entity class, you can implement these interfaces directly. There are also corresponding interfaces for other entity classes. But this is not the suggested way, unless you have a good reason to do not derive from Entity classes.
实际上,实体类实现的接口(and Entity<TPrimaryKey> implements IEntity<TPrimaryKey>)。如果不希望从实体类派生,则可以直接实现这些接口。其他实体类也有相应的接口。但这不是建议的方法,除非您有很好的理由不从实体类派生出来。
IExtendableObject Interface(IExtendableObject接口)
ASP.NET Boilerplate provides a simple interface, IExtendableObject, to easily associate arbitrary name-value data to an entity. Consider this simple entity:
ASP.NET的模板提供了一个简单的接口,iextendableobject,很容易联想到一个实体任意name-value数据。考虑这个简单的实体:
public class Person : Entity, IExtendableObject { public string Name { get; set; } public string ExtensionData { get; set; } public Person(string name) { Name = name; } }
IExtendableObject just defines ExtensionData string property which is used to store JSON formatted name value objects. Example:
IExtendableObject 定义ExtensionData string 属性用来存储json格式的name-value objects,如:
var person = new Person("John"); person.SetData("RandomValue", RandomHelper.GetRandom(1, 1000)); person.SetData("CustomData", new MyCustomObject { Value1 = 42, Value2 = "forty-two" });
We can use any type of object as value to SetData method. When we use such the code above, ExtensionData will be like that:
我们可以使用任何类型的对象作为值SetData方法。当我们使用上面的代码,extensiondata如下:
{"CustomData":{"Value1":42,"Value2":"forty-two"},"RandomValue":178}
Then we can use GetData to get any value:
用GetData取得任意的值
var randomValue = person.GetData<int>("RandomValue"); var customData = person.GetData<MyCustomObject>("CustomData");
While this technique can be very useful in some cases (when you need to provide ability to dynamically add extra data to an entity), you normally should use regular properties. Such a dynamic usage is not type safe and explicit.
虽然这种技术在某些情况下非常有用(当您需要提供向实体动态添加额外数据的能力时),但您通常应该使用常规属性。这种动态用法不是类型安全的和显式的。