Я написал логику сортировки для веб-приложения ASP.NET Core 2.2. Моя концепция - определить словарь, содержащий правила порядка сортировки. Словарь содержит string
ключей. Каждое правило связано с типом объекта. Также существует возможность получения правила порядка сортировки для определенного типа объекта с помощью клавиши string
.
Логика сортировки, которую я написал , не упорядочивает какой-либо коллекции, она только хранит и предоставляет информацию, необходимую для сортировки. Каждое правило порядка сортировки имеет:
- и
Expression<Func<T, object>>
;
- a
bool
флаг, описывающий способ сортировки (по возрастанию / по убыванию);
- a
bool
флаг, указывающий, является ли конкретное правило порядка сортировки правилом по умолчанию.
Я определил эти данные в ISortOrderRule<T>
интерфейсе:
public interface ISortOrderRule<T>
{
Expression<Func<T, object>> Expression { get; }
bool IsDescending { get; }
bool IsDefault { get; }
}
с реализацией по умолчанию в SortOrderRule<T>
классе:
public class SortOrderRule<T> : ISortOrderRule<T>
{
public Expression<Func<T, object>> Expression { get; set; }
public bool IsDefault { get; set; }
public bool IsDescending { get; set; }
}
Выражение можно использовать, например, в качестве аргумента для OrderBy()
метода LINQ. Флаг IsDefault
может использоваться в резервном механизме для порядка сортировки по умолчанию, если другие не найдены.
Теперь, чтобы создать правила порядка сортировки для конкретной сущности, я создал общий интерфейс ISortOrderCollection<T>
, где правила порядка сортировки могут храниться в нижележащем словаре:
public interface ISortOrderCollection<T> :
IReadOnlyDictionary<string, ISortOrderRule<T>>,
IReadOnlyCollection<KeyValuePair<string, ISortOrderRule<T>>>,
IEnumerable<KeyValuePair<string, ISortOrderRule<T>>>,
IEnumerable
{
}
Только для чтения, потому что я хотел, чтобы он был закрыт для внешнего мира, но открыт для классов, происходящих из SortOrderCollectionBase<T>
:
public abstract class SortOrderCollectionBase<T> : ISortOrderCollection<T>
{
private readonly IDictionary<string, ISortOrderRule<T>> _rules;
public SortOrderCollectionBase()
{
_rules = new Dictionary<string, ISortOrderRule<T>>();
}
protected void AddSortOrderRule(string key, ISortOrderRule<T> sortOrderRule)
{
// Tweak over the key, add prefix or suffix depending on sorting way
// So sort order rules for the same property but with opposite
// sorting way can be distinguished.
var sortRuleKey = BuildSortOrderRuleKey(key, sortOrderRule);
_rules.Add(sortRuleKey, sortOrderRule);
}
// Implementations of interface members removed for brevity.
}
Теперь я могу добавить некоторые правила порядка сортировки для Level
объекта:
public class LevelSortOrderCollection : SortOrderCollectionBase<Level>
{
public LevelSortOrderCollection()
{
AddSortOrderRule(nameof(Level.Position), new SortOrderRule<Level>
{
Expression = (level) => level.Position,
IsDefault = true,
});
AddSortOrderRule(nameof(Level.Position), new SortOrderRule<Level>
{
Expression = (level) => level.Position,
IsDescending = true,
});
}
}
Модель уровня:
public class Level
{
public int Id { get; set; }
public int Position { get; set; }
}
Типы, реализующие ISortOrderCollection<T>
, зарегистрированы в Startup
в ConfigureServices()
методе:
services.AddScoped<ISortOrderCollection<Level>, LevelSortOrderCollection>();
// ...
И, наконец, я могу использовать коллекцию порядка сортировки в контроллере:
public class LevelsController : Controller
{
private readonly IRepository<Level> _levelsRepository;
private readonly ISortOrderCollection<Level> _levelSortOrder;
public LevelsController(
IRepository<Level> levelsRepository,
ISortOrderCollection<Level> levelSortOrder)
{
_levelsRepository = levelsRepository;
_levelSortOrder = levelSortOrder;
}
public async Task<IActionResult> Index(string sort)
{
var sortOrder = _levelSortOrder[sort];
var result = await _levelsRepository.GetPageAsync(sortOrder.Expression);
return View(result);
}
}
GetPageAsync()
из IRepository<Level>
принимает выражение, которое позже используется для упорядочения записей с OrderBy()
.
Обратите внимание, что я намеренно вырезал некоторый код IMO, не принося ничего стоящего здесь, например, нулевых проверок, проверки, логики контроллера / репозитория, выбирая, вызывать ли OrderBy()
или OrderByDescending()
, и возвращался к порядку сортировки по умолчанию. Если вы чувствуете, что вам нужно увидеть больше, дайте мне знать в комментариях.
Вопрос
Как создать централизованный поставщик правил порядка сортировки с доступом ко многим коллекциям порядка сортировки для нескольких объектов? Вместо непосредственного внедрения коллекции порядка сортировки для определенного типа сущности внутри контроллера, я хотел бы иметь возможность внедрить поставщика порядка сортировки общего назначения, например так:
private readonly IRepository<Level> _levelsRepository;
private readonly ISortOrderProvider _sortOrderProvider;
public LevelsController(
IRepository<Level> levelsRepository,
ISortOrderProvider sortOrderProvider)
{
_levelsRepository = levelsRepository;
_sortOrderProvider = sortOrderProvider;
}
и тогда я бы вызвал какой-то метод с параметром типа:
var sortOrder = _provider.GetSortOrderRule<Level>("Position");
, который попытается найти правило порядка сортировки для типа объекта, переданного в параметре типа с соответствующим ключом string
.
Конечная нота
Я знаю, что все это довольно сложно, поэтому есть хороший шанс получить совершенно другой способ достичь того, что я пытаюсь сделать. Я не зациклен на таких ответах.