(译)ABP之Entities
原文地址:https://aspnetboilerplate.com/Pages/Documents/Entities#DocAuditing
实体是DDD(领域驱动模型)的核心概念之一,Eric Evans 把它描述成“一个对象本质上不是通过其属性定义的,而是通过一系列连续性和标识来定义“。所以,实体都有一个Id,并保存在数据库里。一个实体类通常映射成关系型数据库的一个表。
Entity 类
在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属性,它是这个实体类的主键,所以,所有实体类的主键都是一样的,都是为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类重写了equality操作符(==),使得检查两个实体是否相等(它们的Id是否相等)变得很容易;它也定义了一个**IsTransient()方法去检查是否有Id.
AggregateRoot类
“聚合是领域驱动模型里的一种模式,DDD聚合是聚合领域对象,可以把它当成一个单元,在例子中聚合根可能是一个订单和订单里的子项,这些是单独的对象,但是把订单(连同他的子项)当成一个单一的聚合来对待是很有帮助的。“(Martin Fowler 全文
虽然ABP不强制你使用聚合,你也可能想在你的应用程序里创建聚合和聚合根,ABP里定义了一个继承了Entity的AggregateRoot为聚合来创建聚合根实体。
领域事件
AggregateRoot定义了DomainEvents集合来生成领域事件,在当前工作单元完成前,这些事件会自动触发。事实上,任意一个实体都可以通过实现IGeneratesDomainEvents接口来生成领域事件,但是通常(最近实践)是在聚合根里来生成领域事件,这就是为什么把它默认定义在AggregateRoot里,而不是Entity类里。
常规接口
在许多应用程序里,实体类里都会用到一些相同的属性(和数据库表字段),例如CreateTime,用来表示实体被创建的时间。ABP里提供了一些有用的接口,使得这些通用属性更明显及更富表达性,这也为实现了这些接口的实体类提供了一种公共代码的方式。
审计
IHasCreationTime使得实体类的“创建时间”信息使用一个公共属性成为可能,当一个实现了这个接口的类对象插入到数据库里时,ABP会自动设置这个实体的创建时间属性值。
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会自动把CreatorUserId设置成当前用户的Id,针对不同类型的Id属性,它也有泛型版本的。
对于修改也有类似的接口:
public interface IHasModificationTime
{
DateTime? LastModificationTime { get; set; }
}
public interface IModificationAudited : IHasModificationTime
{
long? LastModifierUserId { get; set; }
}
当更新实体时,ABP也会自动设置这些属性,而你只要为你的实体定义它们就可以了。
如果你想实现所有这些审计属性,你可以直接实现IAudited接口:
public interface IAudited : ICreationAudited, IModificationAudited
{
}
作为一种快捷方式,你可以直接派生自AuditedEntity类,而不是直接去实现IAudited接口,AuditedEntity针对不同类型的Id属性有一个泛型版本。
注意:ABP是通过ABP Session取得当前用户。
软删除
软删除时一种通用的做法来标识实体被删除了,而不是真的从数据库里删除它。例如,你可能不想直接从数据库里删除一个用户的数据,因为它关联了其它一些表。ISoftDelete接口就是为这个目的而建的:
public interface ISoftDelete
{
bool IsDeleted { get; set; }
}
ABP实现了软删除模式,当一个实体被软删除时,ABP就会侦测到,阻止它直接删除,而把它的IsDeleted设置为true,并更新到数据库里,在查询时,ABP会自动过滤掉被软删除的数据。
如果你使用了软删除,你可能想保存这条数据是什么时候删除的,被谁删除的,那这样的话,你可以实现IDeletionAudited接口,如下所示:
public interface IDeletionAudited : ISoftDelete
{
long? DeleterUserId { get; set; }
DateTime? DeletionTime { get; set; }
}
如你所见,IDeletionAudited继承了ISoftDelete,当实体被删除时,ABP会自动设置这些属性。
如果你想对一个实体实现所有审计接口(创建、修改和删除),你可以直接实现IFullAudited接口,因为它继承那些接口:
public interface IFullAudited : IAudited, IDeletionAudited
{
}
作为一种快捷方式,你可以让你的类派生自实现了上面那些接口的FullAuditedEntity类。
- 注意1:所有的审计接口和类都有一个对应的泛型版本,用于定义你的User实体的导航属性(如ICreationAudited 和 FullAuditedEntity<TPrimaryKey, TUser>)。
- 它们也有一个AggregateRoot,如AuditedAggregateRoot。
激活/失效状态实体
有些实体需要标识为激活或失效,然后你可能对实体的激活/失效状态做一些操作,你可以实现IPassivable接口,它就是为了这个目的而建的,它定义了IsActive属性。
如果你的实体在第一次创建时就要被激活,你可以在构造函数里把IsActive设置为true。
这跟软删除(IsDeleted)有些不一样,如果一个实体被软删除了,它是无法从数据库里抓取出来的(ABP默认阻止获取软删除数据),但是,对于激活/失效状态的实体,从数据库获取数据时,就完全有你来决定了。
实体变更事件
当一个实体被插入、修改或删除时,ABP会自动触发一些特定的事件。因此,你可以注册这些事件去执行任何你需要的逻辑。想了解更多可以查看event bus documentation里的Predefined Events。
IEntity接口
实际上,Entity类实现了IEntity接口(和Entity实现了IEntity),如果你不想派生自Entity类,你可以直接实现这些接口,对于其它实体类也有相应的接口,但是不建议你这样做,除非你有一个好的理由不去派生自Entity类。
IExtendableObject接口
ABP提供了一个简单的接口,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");
在有些情况下,这是很有用的(当你需要提供向实体类上动态添加额外数据的能力时),正常情况下你应该使用常规属性,动态类型的用法类型既不明确,也不是安全。
(译)ABP之Entities的更多相关文章
- [译]ABP框架使用AngularJs,ASP.NET MVC,Web API和EntityFramework构建N层架构的SPA应用程序
本文转自:http://www.skcode.cn/archives/281 本文演示ABP框架如何使用AngularJs,ASP.NET MVC,Web API 和EntityFramework构建 ...
- [译]ABP vNext微服务演示,项目状态和路线图
译注: ABP的主要负责人hikalkan最近又发布了一篇博客, 说明了ABP vNext的微服务演示,项目状态和路线图.其中特意对ABP的中文社区进行了感谢! 本文翻译自该博客文章(https:// ...
- [译]ABP vNext介绍
译者注 ASP.NET Boilerplate是.Net平台非常优秀的一个开源Web应用程序框架,在国内也有大量的粉丝. 近日, 本人在github上闲逛, 发现ASP.NET Boilerplate ...
- 【译】DTD - Entities
原文:DTD - Entities 实体用于定义XML文档中特殊字符的快捷方式. 实体主要有四种类型: 内置实体(Built-in entities) 字符实体(Character entities) ...
- [译]ABP v1.0终于发布了!
ABP v1.0终于发布了! 今天是个大日子!经过约3年的不断开发,第一个稳定的ABP版本,1.0,已经发布了.感谢为该项目做出贡献或试用过的每个人. 立即开始使用新的ABP框架:abp.io/get ...
- [译][ABP vNext]ABP CLI,v0.18版本的新模板和其他功能
ABP CLI,v0.18版本的新模板和其他功能 ABP v0.18已发布, 包含解决的70+个issue,500+次提交 网站更改 abp.io网站完全更新以突出ABP框架的目标和重要功能.文档和博 ...
- [译]ABP框架v2.3.0已经发布!
在新冠病毒的日子里,我们发布了ABP框架v2.3, 这篇文章将说明本次发布新增内容和过去的两周我们做了什么. 关于新冠病毒和我们的团队 关于冠状病毒的状况我们很难过.在Volosoft的团队,我们有不 ...
- ABP(现代ASP.NET样板开发框架)系列之10、ABP领域层——实体
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板 ...
- 基于DDD的.NET开发框架 - ABP初探
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
随机推荐
- JSONPath使用
JSONPath是fastjson在1.2.0之后支持的.JSONPath是一个很强大的功能.关于JSONPath的介绍请查看官方文档 JSONPath. 官方文档上给出了详细的说明以及使用.但是官方 ...
- HDU_1698 Just a Hook(线段树+lazy标记)
pid=1698">题目请点我 题解: 接触到的第一到区间更新,须要用到lazy标记.典型的区间着色问题. lazy标记详情请參考博客:http://ju.outofmemory.cn ...
- 【Android使用Shape绘制虚线,在4.0以上的手机显示实线】解决方式
问题描写叙述: 用下面代码绘制虚线: <span style="font-family:Comic Sans MS;font-size:18px;"><? xml ...
- mysql优化专题」90%程序员都会忽略的增删改优化(2)
补充知识点:操作数据语句优化的认识 通常情况下,当访问某张表的时候,读取者首先必须获取该表的锁,如果有写入操作到达,那么写入者一直等待读取者完成操作(查询开始之后就不能中断,因此允许读取者完成操作). ...
- dubbo2.5.6从下载到编译成功并且部署成功过程
本文基于dubbo2.5.6版本 原文链接:http://www.cnblogs.com/zhuwenjoyce/ 1,下载dubbo 首先从 github 下载源代码并阅读 readme ...
- Spring 链接数据库
一.前言 Spring 现在是我们在做 JavaWeb 开发中,用的最主流的框架.以后是不是我们暂时不知道,但现在是.废话不多我就介绍 Spring 中.链接数据库的三种方式: git源码地址 需要的 ...
- Asp.Net Web API(六)
Asp.Net Web API不可以需要IIS.可以自己在主机上承载一个Web API 创建WebAPI.Server项目 创建一个控制器项目的服务端 在Nuget中添加Microsoft.AspNe ...
- Android项目实战(三十七):Activity管理及BaseActivity的实现
Ps:7-10月 完成公司两个app项目上架.漏掉的总结 开始慢慢补上. 一.写一个Activity的管理类 1.单例模式,以栈(先进后出)的形式存储Activity对象 public class A ...
- AMD与CMD区别
区别: 1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行.不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同).CMD 推崇 as lazy as ...
- iOS 应用中加载文档pdf/word/txt
一.加载PDF文档:应用内打开文档.手机中其他应用打开文档 Demo 首先拖入一个文档pdf.word.txt,打开不同的文档知识 文件名字.类型修改即可 #import "ReadView ...