Home > OS >  Clean architecture using eShopOnWeb reference application - Entity Framework Core Performance Effici
Clean architecture using eShopOnWeb reference application - Entity Framework Core Performance Effici

Time:04-02

I'm reading up on Clean architecture on Microsoft Docs.

enter image description here

Looking at how entities are converted to View Models and Dtos it looks like this:

var items = await _catalogBrandRepository.ListAsync();

response.CatalogBrands.AddRange(items.Select(_mapper.Map<CatalogBrandDto>));

or

var orders = await _orderRepository.ListAsync(specification, cancellationToken);

return orders.Select(o => new OrderViewModel

Code that sets up IRepository for _catalogBrandRepository and _orderRepository.

builder.Services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
builder.Services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));

or:

services.AddScoped(typeof(IReadRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));

This works really well until performance is needed and I only want to project properties I need.

https://docs.microsoft.com/en-us/ef/core/performance/efficient-querying#project-only-properties-you-need

Given the set up I can not not select directly to a Dto given that the references will be wrong:

public class EfRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
    private readonly CatalogContext _dbContext;

    public EfRepository(CatalogContext dbContext) : base(dbContext)
    {
        _dbContext = dbContext;
    }

    public List<OrderViewModel> GetAllOrderViewModels()
    {
        //Does not work
        return _dbContext.Orders.Select(o => new OrderViewModel
        {
            OrderDate = o.OrderDate,
            OrderNumber = o.Id,
            ShippingAddress = o.ShipToAddress,
            Total = o.Total()
        }).ToList();
    }
}

I could of course return the anonymous type as dynamic but this is not good architecture imao.

public List<dynamic> GetAllOrderViewModels()
{
    return _dbContext.Orders.Select(o => new 
    {
        OrderDate = o.OrderDate,
        OrderNumber = o.Id,
        ShippingAddress = o.ShipToAddress,
        Total = o.Total()
    }).ToList<dynamic>();
}

Is the correct approach to add specific Dtos to ApplicationCore, Infrastructure or is there something I'm missing?

The most similar I found was BasketQueryService but it only returns an int.

public class BasketQueryService : IBasketQueryService
{
    private readonly CatalogContext _dbContext;

    public BasketQueryService(CatalogContext dbContext)
    {
        _dbContext = dbContext;
    }

    /// <summary>
    /// This method performs the sum on the database rather than in memory
    /// </summary>
    /// <param name="username"></param>
    /// <returns></returns>
    public async Task<int> CountTotalBasketItems(string username)
    {
        var totalItems = await _dbContext.Baskets
            .Where(basket => basket.BuyerId == username)
            .SelectMany(item => item.Items)
            .SumAsync(sum => sum.Quantity);

        return totalItems;
    }
}

Given that the question is about Clean architecture I deemed it OK to be asked.

https://stackoverflow.com/help/dont-ask

CodePudding user response:

Application Core

The Application Core holds the business model, which includes entities, services, and interfaces. These interfaces include abstractions for operations that will be performed using Infrastructure, such as data access, file system access, network calls, etc. Sometimes services or interfaces defined at this layer will need to work with non-entity types that have no dependencies on UI or Infrastructure. These can be defined as simple Data Transfer Objects (DTOs).

https://docs.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/common-web-application-architectures#application-core

Given this documentation I created a OrderDto and placed it in ApplicationCore -> Dto.

namespace Microsoft.eShopWeb.ApplicationCore.Dto;
public class OrderDto
{

        private const string DEFAULT_STATUS = "Pending";

        public int OrderNumber { get; set; }
        public DateTimeOffset OrderDate { get; set; }
        public decimal Total { get; set; }
        public string Status => DEFAULT_STATUS;
        public Address ShippingAddress { get; set; }
}

Example in EfRepository even though something like OrderQueryService based on BasketQueryService would be better:

public List<OrderDto> GetAllOrderDtos()
{
    return _dbContext.Orders.Select(o => new OrderDto
    {
        OrderDate = o.OrderDate,
        OrderNumber = o.Id,
        ShippingAddress = o.ShipToAddress,
        Total = o.Total()
    }).ToList();
}
  • Related