使用c#Linq表达式的自定义排序

使用c#Linq表达式的自定义排序,c#,linq,sorting,lambda,entity-framework-core,C#,Linq,Sorting,Lambda,Entity Framework Core,我正试图使C#应用程序和C#Web API的排序更容易。我正在使用实体框架核心进行持久性和测试 在我的应用程序或Web API中,我决定属性名的降序或升序 我将这些知识传递到我的存储库中,在那里创建并执行Linq查询。问题是当我有一个十进制列时,它执行的是字符串顺序而不是十进制顺序 public static class SortingExtensions { public static IQueryable<T> SortBy<T>( this

我正试图使C#应用程序和C#Web API的排序更容易。我正在使用实体框架核心进行持久性和测试

在我的应用程序或Web API中,我决定属性名的降序或升序

我将这些知识传递到我的存储库中,在那里创建并执行Linq查询。问题是当我有一个十进制列时,它执行的是字符串顺序而不是十进制顺序

public static class SortingExtensions
{
    public static IQueryable<T> SortBy<T>(
        this IQueryable<T> queryable,
        Sorting sorting)
    {
        IOrderedQueryable<T> orderedQueryable = null;

        sorting.SortableEntities
            .OrderBy(x => x.Order)
            .ToList()
            .ForEach(sortableEntity =>
            {
                Expression<Func<T, object>> expression = QueryHelper.GetDefaultSortExpression<T>(sortableEntity);
                if (expression != null)
                {
                    orderedQueryable = orderedQueryable == null
                        ? queryable.OrderBy(expression, sortableEntity.Descending)
                        : orderedQueryable.OrderBy(expression, sortableEntity.Descending);
                }
            });

        return orderedQueryable;
    }

    private static IOrderedQueryable<T> OrderBy<T, TKey>(
        this IOrderedQueryable<T> query,
        Expression<Func<T, TKey>> keySelector,
        bool descending) => descending ? query.ThenByDescending(keySelector) : query.ThenBy(keySelector);

    private static IOrderedQueryable<T> OrderBy<T, TKey>(
        this IQueryable<T> query,
        Expression<Func<T, TKey>> keySelector,
        bool descending) => descending ? query.OrderByDescending(keySelector) : query.OrderBy(keySelector);
}

public static class QueryHelper
{
    public static Expression<Func<T, object>> GetDefaultSortExpression<T>(SortableEntity sortableEntity)
    {
        Type entityType = typeof(T);
        ParameterExpression arg = Expression.Parameter(entityType, "x");

        string[] fieldNames = sortableEntity.Name.Split('.');
        MemberExpression memberExpression = null;
        foreach (string name in fieldNames)
        {
            Expression expressionToUse = memberExpression ?? (Expression) arg;
            memberExpression = Expression.Property(expressionToUse, name.ToProperCase());
        }

        Expression propertyExpression = Expression.Convert(memberExpression, typeof(object));
        Expression<Func<T, object>>
            complexExpression = Expression.Lambda<Func<T, object>>(propertyExpression, arg);
        return complexExpression;
    }
}

public class SortableEntity
{
    public int Order { get; set; }
    public bool Descending { get; set; }
    public string Name { get; set; }
}

public class Sorting
{
    IEnumerable<SortableEntity> SortableEntities { get; }
}

public class TestDecimalPropertyClass : Entity
{
    public TestDecimalPropertyClass(decimal @decimal) => Decimal = @decimal;

    protected TestDecimalPropertyClass()
    {
    }

    public decimal Decimal { get; set; }
}

public class TestDecimalPropertyClassRepository
{
    private readonly DbContext _dbContext;

    public TestDecimalPropertyClassRepository(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<IEnumerable<TestDecimalPropertyClass>> GetAllAsync(Sorting sorting)
    {
        List<TestDecimalPropertyClass> entities = await _dbContext.Set<TestDecimalPropertyClass>()
            .SortBy(sorting)
            .ToListAsync();

        return entities;
    }

    public async Task SaveAsync(TestDecimalPropertyClass testDecimalPropertyClass)
    {
        _dbContext.Set<TestDecimalPropertyClass>().Add(testDecimalPropertyClass);
        await _dbContext.SaveChangesAsync();
    }
}
公共静态类排序扩展
{
公共静态可读取的SortBy(
这是一个可以质疑的问题,
排序(排序)
{
IOrderedQueryable orderedQueryable=null;
排序。可排序实体
.OrderBy(x=>x.Order)
托利斯先生()
.ForEach(可排序实体=>
{
Expression=QueryHelper.GetDefaultSortExpression(sortableEntity);
if(表达式!=null)
{
orderedQueryable=orderedQueryable==null
?queryable.OrderBy(表达式,sortableEntity.Descending)
:orderedQueryable.OrderBy(表达式,sortableEntity.Descending);
}
});
退货订单可查询;
}
私有静态IOrderedQueryable OrderBy(
此IOrderedQueryable查询,
表达式键选择器,
bool descending)=>descending?query.ThenBy descending(keySelector):query.ThenBy(keySelector);
私有静态IOrderedQueryable OrderBy(
这是一个易懂的问题,
表达式键选择器,
bool descending)=>descending?查询.OrderByDescending(键选择器):查询.OrderBy(键选择器);
}
公共静态类QueryHelper
{
公共静态表达式GetDefaultSortExpression(SortableEntity SortableEntity)
{
类型entityType=类型(T);
ParameterExpression arg=Expression.Parameter(entityType,“x”);
string[]FieldName=sortableEntity.Name.Split('.');
MemberExpression MemberExpression=null;
foreach(字段名中的字符串名)
{
表达式expressionToUse=memberExpression???(表达式)arg;
memberExpression=Expression.Property(expressionToUse,name.TopropertCase());
}
Expression propertyExpression=Expression.Convert(memberExpression,typeof(object));
表情
complexExpression=Expression.Lambda(propertyExpression,arg);
返回complexExpression;
}
}
公共类可排序实体
{
公共整数顺序{get;set;}
公共布尔下降{get;set;}
公共字符串名称{get;set;}
}
公共类排序
{
IEnumerable可排序实体{get;}
}
公共类TestDecimalPropertyClass:实体
{
公共TestDecimalPropertyClass(decimal@decimal)=>decimal=@decimal;
受保护的TestDecimalPropertyClass()
{
}
公共十进制{get;set;}
}
公共类TestDecimalPropertyClassRepository
{
私有只读dbcontextu DbContext;
公共TestDecimalPropertyClassRepository(DbContext DbContext)
{
_dbContext=dbContext;
}
公共异步任务GetAllAsync(排序)
{
List entities=await_dbContext.Set()
.SortBy(分类)
.ToListAsync();
返回实体;
}
公共异步任务SaveAsync(TestDecimalPropertyClass TestDecimalPropertyClass)
{
_dbContext.Set().Add(testDecimalPropertyClass);
wait_dbContext.saveChangesSync();
}
}
下面是我为它编写的一个测试:

[TestFixture]
public class GenericSortingTests
{
    private SqliteConnection SqliteConnection { get; set; }

    [SetUp]
    public void DbSetup()
    {
        SqliteConnectionStringBuilder sqliteConnectionStringBuilder = new SqliteConnectionStringBuilder
        {
            Mode = SqliteOpenMode.Memory,
            Cache = SqliteCacheMode.Private
        };
        SqliteConnection = new SqliteConnection(sqliteConnectionStringBuilder.ToString());
        SqliteConnection.Open();
    }

    [TearDown]
    public void DbTearDown()
    {
        SqliteConnection.Close();
    }
    [Test]
    public async Task GivenADecimalProperty_WhenISortByColumn_ThenItSorts()
    {
        decimal[] decimals = new[] {7m, 84.3m, 13.4m};

        using (DbContext dbContext = GetDbContext())
        {
            TestDecimalPropertyClassRepository testRepository = new TestDecimalPropertyClassRepository(dbContext);

            foreach (decimal @decimal in decimals)
            {
                TestDecimalPropertyClass entity = new TestDecimalPropertyClass(@decimal);
                await testRepository.SaveAsync(entity);
            }
        }

        IEnumerable<TestDecimalPropertyClass> entities;
        using (DbContext dbContext = GetDbContext())
        {
            TestDecimalPropertyClassRepository testRepository = new TestDecimalPropertyClassRepository(dbContext);

            entities = await testRepository.GetAllAsync(new Sorting
            {
                SortableEntities = new[]
                {
                    new SortableEntity
                    {
                        Descending = false,
                        Name = "decimal",
                        Order = 0
                    }
                }
            });
        }

        List<TestDecimalPropertyClass> list = entities.ToList();
        Assert.That(list.Count(), Is.EqualTo(decimals.Length));
        Assert.That(list.ToArray()[0].Decimal, Is.EqualTo(7m));
        Assert.That(list.ToArray()[1].Decimal, Is.EqualTo(13.4m));
        Assert.That(list.ToArray()[2].Decimal, Is.EqualTo(84.3m));
    }

    private class TestDbContext : DbContext
    {
        public TestDbContext(DbContextOptions options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<TestDecimalPropertyClass>();
            base.OnModelCreating(modelBuilder);
        }
    }

    private DbContext GetDbContext()
    {
        DbContextOptions<TestDbContext> options = new DbContextOptionsBuilder<TestDbContext>()
            .UseSqlite(SqliteConnection)
            .EnableSensitiveDataLogging()
            .Options;

        TestDbContext dbContext = new TestDbContext(options);
        dbContext.Database.EnsureCreated();
        return dbContext;
    }
}
[TestFixture]
公共类泛型排序测试
{
专用SqliteConnection SqliteConnection{get;set;}
[设置]
公共数据库设置()
{
SqliteConnectionStringBuilder SqliteConnectionStringBuilder=新SqliteConnectionStringBuilder
{
Mode=SqliteOpenMode.Memory,
Cache=SqliteCacheMode.Private
};
SqliteConnection=newsqliteconnection(sqliteConnectionStringBuilder.ToString());
SqliteConnection.Open();
}
[撕裂]
public void DbTearDown()
{
SqliteConnection.Close();
}
[测试]
公共异步任务指定了指定的属性\u WhenISortByColumn\u ThenItSorts()
{
小数[]位小数=新[]位{7m,84.3m,13.4m};
使用(DbContext=GetDbContext())
{
TestDecimalPropertyClassRepository testRepository=新的TestDecimalPropertyClassRepository(dbContext);
foreach(十进制@十进制小数)
{
TestDecimalPropertyClass实体=新的TestDecimalPropertyClass(@decimal);
等待testRepository.SaveAsync(实体);
}
}
可数实体;
使用(DbContext=GetDbContext())
{
TestDecimalPropertyClassRepository testRepository=新的TestDecimalPropertyClassRepository(dbContext);
entities=await testRepository.GetAllAsync(新排序)
{
SortableEntities=new[]
{
新可排序实体
{
下降=假,
Name=“decimal”,
订单=0
}
}
});
}
List=entities.ToList();
Assert.That(list.Count(),Is.EqualTo(decimals.Length));
Assert.That(list.ToArray()[0].Decimal,Is.EqualTo(7m));
Assert.That(list.ToArray()[1].Decimal,Is.EqualTo(13.4m));
Assert.That(list.ToArray()[2].Decimal,Is.EqualTo(84.3m));
}
私有类TestDbContext:DbContext
{
公共TestDbContext(DbContextOptions选项):基本(选项)
{
}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
modelBuilder.Entity();
基于模型创建(modelBuilder);
}
}
专用数据库
public class TestDecimalPropertyClassRepository
{
    private readonly IList<TestDecimalPropertyClass> list;

    public TestDecimalPropertyClassRepository(IEnumerable<TestDecimalPropertyClass> repo)
    {
        list = repo.ToList();
    }

    public IEnumerable<TestDecimalPropertyClass> GetAll(Sorting sorting)
    {
        List<TestDecimalPropertyClass> entities = list
            .AsQueryable()
            .SortBy(sorting)
            .ToList();

        return entities;
    }

    public void Save(TestDecimalPropertyClass testDecimalPropertyClass)
    {
        list.Add(testDecimalPropertyClass);            

    }
}
[Test]
public void GivenADecimalProperty_WhenISortByColumn_ThenItSorts()
{
    decimal[] decimals = new[] { 7m, 84.3m, 13.4m };
    var repo = decimals.Select(x => new TestDecimalPropertyClass(x));

    TestDecimalPropertyClassRepository testRepository = new TestDecimalPropertyClassRepository(repo);

    var entities = testRepository.GetAll(new Sorting
    {
        SortableEntities = new[]
            {
            new SortableEntity
            {
                Descending = false,
                Name = "decimal",
                Order = 0
            }
        }
    });

    List<TestDecimalPropertyClass> list = entities.ToList();
    Assert.That(list.Count(), Is.EqualTo(decimals.Length));
    Assert.That(list.ToArray()[0].Decimal, Is.EqualTo(7m));
    Assert.That(list.ToArray()[1].Decimal, Is.EqualTo(13.4m));
    Assert.That(list.ToArray()[2].Decimal, Is.EqualTo(84.3m));
}