在N-Tier应用程序中使用DTO时的OrderBy

本文关键字:DTO 时的 OrderBy N-Tier 应用程序 | 更新日期: 2023-09-27 18:00:22

我有一个用C#-编写的分层应用程序

前端-(获取Customers的页面)

业务-(CustomerServiceCustomer类)

数据合约-(CustomerDTO

数据访问-(UnitOfWorkRepositories

该数据库有一个Customer表,其中包含超过一百列(许多冗余列),因此我使用DTO在业务层填充Customer对象。在Customer类中,我也将许多字段的名称从数据库名称更改为,例如ID改为CustomerID,FName改为Firstname。

前端使用业务层的服务来获得客户,例如GetAll(), GetByID(int customerID).

我还想提供一个GetPaged method如下。

客户服务等级

public IEnumerable<Customer> GetPaged(
               Func<IQueryable<Customer>, IOrderedQueryable<Customer>> orderBy, 
               int skip, 
               int take)
{
     foreach (var customerDTO in 
                     unitOfWork.CustomerRepository.GetPaged(orderBy, skip, take))
     {
          yield return new Customer(customerDTO);
     }
}

但这将不起作用,因为CustomerRepository期望基于CustomerDTO而不是CustomerorderBy

我不想让前端知道任何关于CustomerDTO的信息。

我怎样才能做到这一点?

在N-Tier应用程序中使用DTO时的OrderBy

您可以使用可用的订单选项公开enum,然后在GetPaged中,您只需根据收到的枚举值创建OrderBy。

可能的实施方式是:

public enum OrderCustomersBy
{
    FirstNameAsc, FirstNameDesc, AgeAsc, AgeDesc
}

在客户服务中:

private IOrderedQueryable<CustomerDTO> GetOrderBy(OrderCustomersBy orderOption)
{
    IOrderedQueryable<CustomerDTO> orderBy = null;
    switch (orderOption)
    {
        case OrderCustomersBy.FirstNameAsc: 
            // Create order by...
            break;
        case OrderCustomersBy.FirstNameDesc: 
            // Create order by...
            break;
        case OrderCustomersBy.AgeAsc: 
            // Create order by...
            break;
        case OrderCustomersBy.AgeDesc: 
            // Create order by...
            break;
        default:
            throw new NotImplementedException("Order option not implemented: " + orderOption.ToString());
    }
    return orderBy;
}
public IEnumerable<Customer> GetPaged(Func<IQueryable<Customer>> func, OrderCustomersBy orderOption, int skip, int take)
{
    IOrderedQueryable<CustomerDTO> orderBy = this.GetOrderBy(orderOption);
    foreach (var customerDTO in unitOfWork.CustomerRepository.GetPaged(orderBy, skip, take))
    {
        yield return new Customer(customerDTO);
    }
}

我看到两个问题:第一,如何将业务实体映射到DTO;第二,如何在任何列上执行订单?

将业务实体映射到DTO。这可以通过多种方式实现。举几个例子:可以使用AutoMapper,你可以使用属性进行简单的映射(属性将包含customDTO或数据库列的属性名(使用数据库列可能会否定DTO的值)),也可以从数据库中驱动它,数据库方法有点重手和元。

我将按照评论中的建议使用动态顺序。是的,它确实增加了一层抽象,但减少了维护。

动态订单依据

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> items, string propertyName, SortDirection direction)
    {
        var typeOfT = typeof(T);
        var parameter = Expression.Parameter(typeOfT, "parameter");
        var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
        var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
        var orderExpression = Expression.Lambda(propertyAccess, parameter);
        string orderbyMethod = (direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending");
        var expression = Expression.Call(typeof(Queryable), orderbyMethod, new[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
        return items.Provider.CreateQuery<T>(expression);
    }

在一天结束时,当我更改实体时,我对在应用程序的其他位置更改代码不感兴趣。