An Abp module that help you automatically load related DTO (like ProductDto in OrderDto) under DDD.
-
Install the following NuGet packages. (see how)
- EasyAbp.Abp.RelatedDtoLoader
- EasyAbp.Abp.RelatedDtoLoader.Abstractions
-
Add
DependsOn(typeof(AbpRelatedDtoLoaderModule))
attribute to configure the module dependencies. (see how)
-
Make your
Order
entity (or aggregate root) like this.public class Order : AggregateRoot<Guid> { public virtual Guid ProductId { get; protected set; } // do not add navigation properties to other aggregate roots! // public virtual Product Product { get; set; } protected Order() { } public Order(Guid id, Guid productId) : base(id) { ProductId = productId; } }
-
Add
RelatedDto
attribute inOrderDto
.public class OrderDto : EntityDto<Guid> { public Guid ProductId { get; set; } //Automatic matching rules $"{nameof(Product)}Id" [RelatedDto] public ProductDto Product { get; set; } //Manually matching the ProductId [RelatedDto(nameof(ProductId))] public ProductDto ProductInfo { get; set; } }
-
Create
MyProjectRelatedDtoLoaderProfile
and add a rule.public class MyProjectRelatedDtoLoaderProfile : RelatedDtoLoaderProfile { public MyRelatedDtoLoaderProfile() { // the following example gets entities from a repository and maps them to DTOs. UseRepositoryLoader<ProductDto, Product>(); // or load it by a customized function. UseLoader(GetOrderDtosAsync); // a target type need to be enabled to load its related Dtos properties. // LoadForDto<OrderDto>(); } }
-
Configure the RelatedDtoLoader to use the profile.
public override void ConfigureServices(ServiceConfigurationContext context) { // ... Configure<RelatedDtoLoaderOptions>(options => { // add the Profile options.AddProfile<MyProjectRelatedDtoLoaderProfile>(); }); // ... }
-
Enable the target type to load its related Dto properties.
either in the Profile
public class MyProjectRelatedDtoLoaderProfile : RelatedDtoLoaderProfile { public MyRelatedDtoLoaderProfile() { // ... // a target type need to be enabled to load its related Dtos properties. LoadForDto<OrderDto>(); } }
or via
RegisterTargetDtosInModule method
of RelatedDtoLoaderOptionspublic override void ConfigureServices(ServiceConfigurationContext context) { // ... Configure<RelatedDtoLoaderOptions>(options => { // adding module will auto register all the target dto types which contain any property with RelatedDto attribute. options.LoadForDtosInModule<MyApplicationContractsModule>(); }); // ... }
-
Try to get OrderDto with ProductDto.
public class OrderAppService : ApplicationService, IOrderAppService { private readonly IRelatedDtoLoader _relatedDtoLoader; private readonly IRepository<Order, Guid> _orderRepository; public OrderAppService(IRelatedDtoLoader relatedDtoLoader, IRepository<Order, Guid> orderRepository) { _relatedDtoLoader = relatedDtoLoader; _orderRepository = orderRepository; } public async Task<OrderDto> GetAsync(Guid id) { var order = await _orderRepository.GetAsync(id); var orderDto = ObjectMapper.Map<Order, OrderDto>(order); //LoadAsync return await _relatedDtoLoader.LoadAsync(orderDto); // orderDto.Product should have been loaded. } protected override async Task<List<OrderDto>> MapToGetListOutputDtosAsync(List<Order> entities) { var orderDtos = await base.MapToGetListOutputDtosAsync(entities); //LoadListAsync return (await _relatedDtoLoader.LoadListAsync(orderDtos)).ToList(); // OrderDto.Product should have been loaded. } }
See more: Custom DTO source examples.
- Custom DTO source
- Support one-to-many relation
- Support non Guid keys
- Support multi module development
- Support nested DTOs loading
- Get duplicate DTO from memory
- DTO cache
- An option to enable loading deleted DTO
- Unit test
Thanks @wakuflair and @itryan for their contribution in the first version.