using XXX.Infrastructure.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Dynamic.Core;
namespace XXX.Infrastructure.Extensions
{
public static class QueryableExtensions
{
public static IQueryable<T> ApplySort<T>(this IQueryable<T> source, string orderBy, IPropertyMapping propertyMapping)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (propertyMapping == null)
{
throw new ArgumentNullException(nameof(propertyMapping));
}
var mappingDictionary = propertyMapping.MappingDictionary;
if (mappingDictionary == null)
{
throw new ArgumentNullException(nameof(mappingDictionary));
}
if (string.IsNullOrWhiteSpace(orderBy))
{
return source;
}
var orderByAfterSplit = orderBy.Split(',');
foreach (var orderByClause in orderByAfterSplit.Reverse())
{
var trimmedOrderByClause = orderByClause.Trim();
var orderDescending = trimmedOrderByClause.EndsWith(" desc");
var indexOfFirstSpace = trimmedOrderByClause.IndexOf(" ", StringComparison.Ordinal);
var propertyName = indexOfFirstSpace == -1 ?
trimmedOrderByClause : trimmedOrderByClause.Remove(indexOfFirstSpace);
if (string.IsNullOrEmpty(propertyName))
{
continue;
}
if (!mappingDictionary.TryGetValue(propertyName, out List<MappedProperty> mappedProperties))
{
throw new ArgumentException($"Key mapping for {propertyName} is missing");
}
if (mappedProperties == null)
{
throw new ArgumentNullException(propertyName);
}
mappedProperties.Reverse();
foreach (var destinationProperty in mappedProperties)
{
if (destinationProperty.Revert)
{
orderDescending = !orderDescending;
}
source = source.OrderBy(destinationProperty.Name + (orderDescending ? " descending" : " ascending"));
}
}
return source;
}
public static IQueryable<object> ToDynamicQueryable<TSource>
(this IQueryable<TSource> source, string fields, Dictionary<string, List<MappedProperty>> mappingDictionary)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (mappingDictionary == null)
{
throw new ArgumentNullException(nameof(mappingDictionary));
}
if (string.IsNullOrWhiteSpace(fields))
{
return (IQueryable<object>)source;
}
fields = fields.ToLower();
var fieldsAfterSplit = fields.Split(',').ToList();
if (!fieldsAfterSplit.Contains("id", StringComparer.InvariantCultureIgnoreCase))
{
fieldsAfterSplit.Add("id");
}
var selectClause = "new (";
foreach (var field in fieldsAfterSplit)
{
var propertyName = field.Trim();
if (string.IsNullOrEmpty(propertyName))
{
continue;
}
var key = mappingDictionary.Keys.SingleOrDefault(k => String.CompareOrdinal(k.ToLower(), propertyName.ToLower()) == 0);
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"Key mapping for {propertyName} is missing");
}
var mappedProperties = mappingDictionary[key];
if (mappedProperties == null)
{
throw new ArgumentNullException(key);
}
foreach (var destinationProperty in mappedProperties)
{
selectClause += $" {destinationProperty.Name},";
}
}
selectClause = selectClause.Substring(0, selectClause.Length - 1) + ")";
return (IQueryable<object>)source.Select(selectClause);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace XXX.Infrastructure.Services
{
public interface IPropertyMapping
{
Dictionary<string, List<MappedProperty>> MappingDictionary { get; }
}
}
using XXX.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
namespace XXX.Infrastructure.Services
{
public abstract class PropertyMapping<TSource, TDestination> : IPropertyMapping
where TDestination : IEntity
{
public Dictionary<string, List<MappedProperty>> MappingDictionary { get; }
protected PropertyMapping(Dictionary<string, List<MappedProperty>> mappingDictionary)
{
MappingDictionary = mappingDictionary;
MappingDictionary[nameof(IEntity.Id)] = new List<MappedProperty>
{
new MappedProperty { Name = nameof(IEntity.Id), Revert = false}
};
}
}
}
query = query.ApplySort(postParameters.OrderBy, _propertyMappingContainer.Resolve<PostResource, Post>());
public interface IPropertyMappingContainer
{
void Register<T>() where T : IPropertyMapping, new();
IPropertyMapping Resolve<TSource, TDestination>() where TDestination : IEntity;
bool ValidateMappingExistsFor<TSource, TDestination>(string fields) where TDestination : IEntity;
}
Startup.cs
var propertyMappingContainer = new PropertyMappingContainer();
propertyMappingContainer.Register<PostPropertyMapping>();
propertyMappingContainer.Register<ArticlePropertyMapping>();
services.AddSingleton<IPropertyMappingContainer>(propertyMappingContainer);