使用 eShopOnWeb 参考应用程序的干净架构 - Entity Framework 核心性能高效查询 - 仅项目您需要的属性

Clean architecture using eShopOnWeb reference application - Entity Framework Core Performance Efficient Querying - Project only properties you need

我正在阅读 Microsoft Docs 上的清洁架构。

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

我还下载了 eShopOnWeb 参考应用程序。

https://github.com/dotnet-architecture/eShopOnWeb

如下图所示,参考实现视图模型保存在 Web 项目中,Dtos 保存在 PublicApi 项目中。

查看实体如何转换为视图模型和 Dto,如下所示:

var items = await _catalogBrandRepository.ListAsync();

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

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

return orders.Select(o => new OrderViewModel

_catalogBrandRepository_orderRepository设置IRepository的代码。

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

或:

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

在需要性能并且我只想投影我需要的属性之前,这非常有效。

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

鉴于设置我不能不select直接给一个Dto给定引用会出错:

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();
    }
}

我当然可以 return 匿名类型是动态的,但这不是好的体系结构。

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

将特定 Dto 添加到 ApplicationCoreInfrastructure 的正确方法是正确的还是我遗漏了什么?

我发现最相似的是 BasketQueryService,但它只有 return 一个 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;
    }
}

鉴于问题是关于 Clean architecture 我认为可以被问到。

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

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

鉴于此文档,我创建了一个 OrderDto 并将其放置在 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; }
}

EfRepository 中的示例,尽管基于 BasketQueryServiceOrderQueryService 会更好:

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