仓储IRepository接口
-
在ABP中,仓储类要实现IRepository接口。最好的方式是针对不同仓储对象定义各自不同的接口
-
IRepository,用来定义Id的类型为int(Int32)的实体。如果你的实体Id数据类型不是int,你可以继承IRepository<TEntity, TPrimaryKey>接口
查询
注意:
1.Get方法被用于根据主键值(Id)取得对应的实体。当数据库中根据主键值找不到相符合的实体时,它会抛出例外。
2.Single方法类似Get方法,但是它的输入参数是一个表达式而不是主键值(Id)。因此,我们可以写Lambda表达式来取得实体。
3.Single方法会在给出的条件找不到实体或符合的实体超过一个以上时,都会抛出例外
4.FirstOrDefault也一样,但是当没有符合Lambda表达式或Id的实体时,会回传null(取代抛出异常)。当有超过一个以上的实体符合条件,它只会返回第一个实体。
注意:
1.GetAllList被用于从数据库中检索所有实体。重载并且提供过滤实体的功能
2.GetAll返回IQueryable类型的对象。因此我们可以在调用完这个方法之后进行Linq操作,调用GetAll方法,那么几乎所有查询都可以使用Linq完成。甚至可以用它来编写Join表达式
3.当你调用GetAll这个方法在Repository对象以外的地方,必定会开启数据库连接。这是因为IQueryable允许延迟执行。它会直到你调用ToList方法或在forEach循环上(或是一些存取已查询的对象方法)使用IQueryable时,才会实际执行数据库的查询。因此,当你调用ToList方法时,数据库连接必需是启用状态。我们可以使用ABP所提供的UnitOfWork特性在调用的方法上来实现。注意,Application Service方法预设都已经是UnitOfWork。因此,使用了GetAll方法就不需要如同Application Service的方法上添加UnitOfWork特性。
List<Person> personList2 = _personRepository.GetAll().Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();
4ABP也有一个额外的方法来实现IQueryable的延迟加载效果,而不需要在调用的方法上添加UnitOfWork这个属性卷标。查询方法接受Lambda(或一个方法)来接收IQueryable并且返回任何对象类型
ar people = _personRepository.Query(q => q.Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).ToList());
新增
- Insert
- InsertAndGetId
- InsertOrUpdate
- InsertOrUpdateAndGetId
注意:
1新增方法会新增实体到数据库并且返回相同的已新增实体。
2nsertAndGetId方法返回新增实体的标识符(Id)。
3InsertOfUpdate会新增或更新实体,选择那一种是根据Id是否有值来决定
4InsertOrUpdatedAndGetId会在实体被新增或更新后返回Id值
更新
1.Update
现更新一个已存在于数据库中的实体。它更新实体并返回相同的实体对象
删除
1.Delete(Id/实体/表达式)
其他
1Count(空/表达式)
2LongCount
实体
Entity
- Id(主键)数据类型可以被更改。默认是int(int32)类型。如果你想给Id定义其它类型,你应该像下面示例一样来声明Id的类型。(string,Guid)
public class Person : Entity<long>
{
public virtual string Name { get; set; }
public virtual DateTime CreationTime { get; set; }
public Task()
{
CreationTime = DateTime.Now;
}
}
接口约定
IHasCreationTime
实体类实现 IHasCreationTime 接口就可以具有CreationTime的属性。当该实体被插入到数据库时, ABP会自动设置该属性的值为当前时间。
ICreationAudited
扩展自 IHasCreationTime 并且该接口具有属性 CreatorUserId
可以直接继承CreationAuditedEntity 类就实现了上述功能
AuditedEntity 类有一个实现不同ID数据类型的泛型版本(默认是int),可以为ID(Entity类中的ID)赋予不同的数据类型。
ISoftDelete
当一个实现了软删除的实体正在被被删除,ABP会察觉到这个动作,并且阻止其删除,设置IsDeleted 属性值为true并且更新数据库中的实体
IDeletionAudited
扩展自 ISoftDelete接口。当一个实体被删除的时候ABP会自动的为这些属性设置值。
你可以直接实现IFullAudited接口,因为该接口已经继承了这些接口,
直接从FullAuditedEntity 类派生你的实体类,因为该类已经实现了IFullAudited接口
注意:如果你不想从Entity 类派生,你能直接的实现这些接口。其他实体类也可以实现相应的接口。但是不建议你用这种方式。除非你有一个很好的理由不从Entity 类派生。
FullAuditedEntity
FullAuditedEntity 带有id, 创建人,创建时间,修改人,修改时间,软删除(详细查看数据库字段),
IPassivable
IPassivable 带有IsActive 是否激活,实体不需要验证,后面的dto会验证
数据库连接(Code First)
创建实体
创建DbContext
通过Database Migrations创建数据库表
internal sealed class Configuration : DbMigrationsConfiguration<SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
{
context.People.AddOrUpdate(
p => p.Name,
new Person {Name = "Isaac Asimov"},
new Person {Name = "Thomas More"},
new Person {Name = "George Orwell"},
new Person {Name = "Douglas Adams"}
);
}
}
Add-Migration InitialCreate
Update-Database
定义仓储接口
接口继承自IRepository/IDimainService
实现仓储类
编写Dto
创建应用服务
接口继承自IApplicationService
工作单元
什么是工作单元
在ABP中,默认情况下,每种应用程序服务方法都假定为工作单元。它会自动创建一个连接,并在方法开始时开始一个事务。如果该方法无例外地成功完成,则将提交事务并处置连接。即使此方法使用不同的存储库或方法,它们也都是
原子的(事务性的)。提交事务后,将自动保存对实体的所有更改。我们甚至不需要调用上面所示的_repository.Update(task)方法。
工作单元在后台替仓储和应用服务的方法工作(即不用加[UnitOfWork]特性)
直接操作工作单元
- [UnitOfWork]
[UnitOfWork]
public void CreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
}
- IUnitOfWorkManager.Begin(…)
private readonly IUnitOfWorkManager _unitOfWorkManager;//构造函数注入
public void CreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
using (var unitOfWork = _unitOfWorkManager.Begin())
{
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
unitOfWork.Complete();
}
}
你可以注入并且使用IUnitOfWorkManager,如上所示。因此,你可以创建更多的有限范围 (limited scope)的工作单元。在这个机制中,你通常可以手动调用Complete方法。如果你不调用,事务会回滚并且所有的异常都不会被储存。Begin方法被重写从而设置工作单元的选项。
禁用工作单元
禁用应用服务方法的工作单元(因为它默认是启用的)
- [UnitOfWork(IsDisabled = true)]
[UnitOfWork(IsDisabled = true)]
public virtual void RemoveFriendship(RemoveFriendshipInput input)
{
_friendshipRepository.Delete(input.Id);
}
在有些情况下,你或许会想要禁用应用服务的工作单元:
- 你的方法不需要任何数据库操作且你不想要开启那些不需要的数据库连接
- 你想要使用工作单元于UnitOfWorkScope类的有限范围内,如上所述
无事务的工作单元
工作单元默认上是具事务性的(这是它的天性)。在此这些情境下, 你或许会想要禁用数据库等级的事务。
- [UnitOfWork(false)]
[UnitOfWork(false)]
public GetTasksOutput GetTasks(GetTasksInput input)
{
var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
return new GetTasksOutput
{
Tasks = Mapper.Map<List<TaskDto>>(tasks)
};
}
自动化的saving changes
[UnitOfWork]
public void UpdateName(UpdateNameInput input) {
var person = _personRepository.Get(input.PersonId);
person.Name = input.NewName;
}
使用工作单元到方法上
就这样,名称就被修改了!我们甚至没有调用_personRepository.Update方法。ORM框架会持续追踪实体所有的变化于工作单元内,且反映所有变化到数据库中
注意,这不需要在应用服务声明UnitOfWork,因为它们默认就是采用工作单元。
仓储接口的GetAll()方法
当你在仓储方法外调用GetAll方法, 这必定得有一个开启状态的数据库连接,因为它返回IQueryable类型的对象,
所有得开启工作单元 应用服务除外
应用服务
IApplicationService接口
在ABP中,一个应用服务需要实现IApplicationService接口。最好的实践是针对每个应用服务都创建相应的接口。
- PersonAppService通过IRepository来执行数据库操作。它通过构造器注入模式来生成。我们在这里使用了依赖注入。
- PersonAppService实现了IApplicationService(通过IPersonAppService继承IApplicationService)。ABP会自动地把它注册到依赖注入系统中,并可以注入到别的类型中使用。
- CreatePerson方法需要一个CreatePersonInput类型的参数。这是一个作为输入的DTO,它将被ABP自动验证其数据有效性。可以查看DTO和数据有效性验证(Validation)文档获取相关细节。
- 在ABP中,一个应用服务方法默认是一个工作单元
IAsyncCrudAppService接口
该接口定义了CRUD方法
定义一个应用服务接口
public interface IPersonAppService : IApplicationService
{
SearchPeopleOutput SearchPeople(SearchPeopleInput input);
}
Dto
ABP建议命名input/ouput对象类似于MethodNameInput/MethodNameOutput,对于每个应用服务方法都需要将Input和Output进行分开定义。甚至你的方法只接收或者返回一个值,也最好创建相应的DTO类型。这样,你的代码才会更具有扩展性,你可以添加更多的属性而不需要更改方法的签名
IInputDto/ IOutputDto
- 输入DTO实现了 IInputDto接口,输出DTO实现了 IOutputDto接口。当实现了IInputDto时,ABP会在方法执行前自动验证输入。
public class SearchPeopleInput : IInputDto
{
[StringLength(40, MinimumLength = 1)]
public string SearchedName { get; set; }
}
public class SearchPeopleOutput : IOutputDto
{
public List<PersonDto> People { get; set; }
}
public class PersonDto : EntityDto
{
public string Name { get; set; }
public string EmailAddress { get; set; }
}
EntityDto
using System;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using ZSC.Course.Entitys;
namespace ZSC.Course.Lessons.Dto
{
[AutoMapFrom(typeof(Lesson))] // 查询出来一个实体应该有什么数据,可以作为列表单条数据,单条详情数据的结构
public class LessonDto : EntityDto // 继承这个就带id,查询出来的值不需要验证
{
/// <summary>
/// 课程编号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 院系编号
/// </summary>
public string DepartmentCode { get; set; }
/// <summary>
/// 课程名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 课程积分
/// </summary>
public int Credits { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remarks { get; set; }
/// <summary>
/// 状态: 0 正常, 1 废弃
/// </summary>
public bool IsActive { get; set; }
}
}
映射
预定义数据过滤器
软删除
软删除过滤器(Soft-delete filter )会过滤从数据库查询出来的实体且是自动套用(从结果集中提取出来)。如果实体需要被软删除,它需要实现ISoftDelete接口,该接口仅定义了一个IsDeleted属性。
多租户
如果你创建一个多租户的应用程序(储存所有租户的数据于单一一个数据库中),你肯定不会希望某个租户看到其它租户的资料。你可以实现IMustHaveTenant接口于此案例中,示例如下:
禁用过滤器
其他过滤器
AsyncCrudAppService说明
泛型参数
- TEntity:CRUD操作对应的实体类
- TEntityDto:GetAll方法返回的实体DTO
- TPrimaryKey:实体的主键
- TGetAllInput:GetAll方法接收的输入参数
- TCreateInput:Create方法接收的输入参数
- TUpdateInput:Update方法接收的输入参数
filter )会过滤从数据库查询出来的实体且是自动套用(从结果集中提取出来)。如果实体需要被软删除,它需要实现ISoftDelete接口,该接口仅定义了一个IsDeleted属性。
多租户
如果你创建一个多租户的应用程序(储存所有租户的数据于单一一个数据库中),你肯定不会希望某个租户看到其它租户的资料。你可以实现IMustHaveTenant接口于此案例中,示例如下:
禁用过滤器
其他过滤器
AsyncCrudAppService说明
泛型参数
- TEntity:CRUD操作对应的实体类
- TEntityDto:GetAll方法返回的实体DTO
- TPrimaryKey:实体的主键
- TGetAllInput:GetAll方法接收的输入参数
- TCreateInput:Create方法接收的输入参数
- TUpdateInput:Update方法接收的输入参数
转载自原文链接, 如需删除请联系管理员。
原文链接:菜鸟abp框架学习入门总结,转载请注明来源!