【发布时间】:2017-11-09 10:35:54
【问题描述】:
我正在尝试使 C# 应用程序和 C# Web API 的排序更容易。我正在使用 Entity Framework Core 进行持久性和测试。
在我的应用程序或 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();
}
}
这是我为它写的一个测试:
[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;
}
}
我希望它将项目按以下顺序排序:7m、13.4m、84.3m,但它会将其排序为 13.4m、7m、84.3m
谁能帮我理解为什么会这样,以便我修复它?
谢谢, 克里斯
【问题讨论】:
-
它的输出顺序与输入顺序相同,这表明没有进行排序。
-
我刚刚将十进制数组重新排序为
{7m, 84.3m, 13.4m},它仍然以{13.4m, 7m, 84.3m}出现 -
它适用于十进制以外的类型?
-
这里有很多代码。你能用最少的代码举例说明你的问题吗? stackoverflow.com/help/mcve
-
包含所有代码以重现问题是一件好事,但如果您尝试从头开始并一次包含一小段代码,我怀疑您将获得更少的代码重现问题,也许你会意识到问题出在哪里。
标签: c# linq sorting lambda entity-framework-core