c# - EF Core 规范模式添加所有列以使用自定义规范对数据进行排序

标签 c# .net-core entity-framework-core specification-pattern

我申请了规范图案对于我的 .net 核心项目。
我还为包含、排序、分页等创建了自定义规范。
我得到 sort来自带有 queryString 的 api url 的值并传递给自定义规范类。
在这个类中,我添加了一些 switch case用于确定应该使用哪一列 orderByorderByDescending但是该表中的列太多。那么有什么方法可以应用 sort一次变量到所有列?还是我必须将所有列写入 switch ?
这是我的自定义规范类。

public class PersonsWithGroupsAndPrivileges : BaseSpecification<Person>
{
public PersonsWithGroupsAndPrivileges(string sort) : base()
{
    AddInclude(x => x.Group);
    AddInclude(x => x.Privilege);
    
    if(!string.IsNullOrEmpty(sort))
    {
        switch (sort)
        {
            case "gender": ApplyOrderBy(x => x.Gender); break;
            case "genderDesc": ApplyOrderByDescending(x => x.Gender); break;
            case "roomNo": ApplyOrderBy(x => x.RoomNo); break;
            case "roomNoDesc": ApplyOrderByDescending(x => x.RoomNo); break;
            default: ApplyOrderBy(x => x.Name); break;
        }
    }
}
}
ISpecification.cs 接口(interface)
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace XXXX.Core.Specifications
{
    public interface ISpecification<T>
    {
        Expression<Func<T, bool>> Criteria { get; }
        List<Expression<Func<T, object>>> Includes { get; }
        List<string> IncludeStrings { get; }
        Expression<Func<T, object>> OrderBy { get; }
        Expression<Func<T, object>> OrderByDescending { get; }
        Expression<Func<T, object>> GroupBy { get; }

        int Take { get; }
        int Skip { get; }
        bool IsPagingEnabled { get; }
    }
}
BaseSpecification.cs
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace XXXX.Core.Specifications
{
    public abstract class BaseSpecification<T> : ISpecification<T>
    {
    protected BaseSpecification(Expression<Func<T, bool>> criteria)
    {
        Criteria = criteria;
    }
    protected BaseSpecification()
    {

    }
    public Expression<Func<T, bool>> Criteria { get; }
    public List<Expression<Func<T, object>>> Includes { get; } = new List<Expression<Func<T, object>>>();
    public List<string> IncludeStrings { get; } = new List<string>();
    public Expression<Func<T, object>> OrderBy { get; private set; }
    public Expression<Func<T, object>> OrderByDescending { get; private set; }
    public Expression<Func<T, object>> GroupBy { get; private set; }

    public int Take { get; private set; }
    public int Skip { get; private set; }
    public bool IsPagingEnabled { get; private set; } = false;

    protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)
    {
        Includes.Add(includeExpression);
    }

    protected virtual void AddInclude(string includeString)
    {
        IncludeStrings.Add(includeString);
    }

    protected virtual void ApplyPaging(int skip, int take)
    {
        Skip = skip;
        Take = take;
        IsPagingEnabled = true;
    }

    protected virtual void ApplyOrderBy(Expression<Func<T, object>> orderByExpression)
    {
        OrderBy = orderByExpression;
    }

    protected virtual void ApplyOrderByDescending(Expression<Func<T, object>> orderByDescendingExpression)
    {
        OrderByDescending = orderByDescendingExpression;
    }

    protected virtual void ApplyGroupBy(Expression<Func<T, object>> groupByExpression)
    {
        GroupBy = groupByExpression;
    }
    }
}
SpecificationEvaluator.cs
using System.Linq;
using DesTech.Core.Entities;
using DesTech.Core.Specifications;
using Microsoft.EntityFrameworkCore;

namespace XXXX.Infrastructure.Data
{
public class SpecificationEvaluator<TEntity> where TEntity : BaseEntity
{
    public static IQueryable<TEntity> GetQuery(IQueryable<TEntity> inputQuery, ISpecification<TEntity> specification)
    {
        var query = inputQuery;

    if (specification.Criteria != null)
    {
        query = query.Where(specification.Criteria);
    }

    query = specification.Includes.Aggregate(query, (current, include) => current.Include(include));

    query = specification.IncludeStrings.Aggregate(query, (current, include) => current.Include(include));

    if (specification.OrderBy != null)
    {
        query = query.OrderBy(specification.OrderBy);
    }
    else if (specification.OrderByDescending != null)
    {
        query = query.OrderByDescending(specification.OrderByDescending);
    }

    if (specification.GroupBy != null)
    {
        query = query.GroupBy(specification.GroupBy).SelectMany(x => x);
    }
    if (specification.IsPagingEnabled)
    {
        query = query.Skip(specification.Skip)
                     .Take(specification.Take);
    }
    return query;
   }
}
}

最佳答案

您基本上需要两个辅助方法 - 一个从排序字符串中提取排序信息(名称和降序),另一个动态构建并应用 Expression<Func<T, object>>从中。两者都进入基础泛型类。
第一个处理像 {property}[[ ]{Desc}] 这样的模式(不区分大小写)可能是这样的:

protected virtual void ExtractSortInfo(string sort, out string propertyPath, out bool descending)
{
    const string Desc = "Desc";
    propertyPath = sort;
    descending = false;
    if (propertyPath.EndsWith(Desc, StringComparison.OrdinalIgnoreCase))
    {
        propertyPath = sort.Substring(0, sort.Length - Desc.Length).TrimEnd();
        descending = true;
    }
}
第二个是这样的:
public virtual void ApplySort(string sort)
{
    if (string.IsNullOrEmpty(sort)) return;
    ExtractSortInfo(sort, out var propertyPath, out var descending);
    var parameter = Expression.Parameter(typeof(T), "x");
    var body = propertyPath.Split('.').Aggregate((Expression)parameter, Expression.Property);
    if (body.Type.IsValueType) body = Expression.Convert(body, typeof(object));
    var selector = Expression.Lambda<Func<T, object>>(body, parameter);
    if (descending)
        ApplyOrderByDescending(selector);
    else
        ApplyOrderBy(selector);
}
另外,它支持点分隔的嵌套属性,如 blog.name类(class) Person有导航属性Blog有属性(property)Name .Expression.Convert用于支持值类型(intdecimalDateTime 等)属性

关于c# - EF Core 规范模式添加所有列以使用自定义规范对数据进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63082758/

相关文章:

c# - 在任务中报告/记录

c# - 异步测试在 Nunit 2.6.2 中挂起

docker-compose 构建卡住。没有错误

c# - EF 核心 : Create a SQL user-defined type from a C# class

c# - EF Core开发过程中如何处理数据库变化?

c# - LINQ 与 @Html.DropDownList 不同

c# - 在 C# 中的 Windows Phone 8.1 上向人员添加联系人

c# - 'Extensions.GetBotPerUserInConversationData<TypeT>(Message, string)' 抛出异常

c# - 多目标 .net core 2.2 和 .net 4.6.1

c# - EF7 一对多映射