diff --git a/src/MAOToolkit.Web/Utilities/ModelBinders/FlagsEnumModelBinder.cs b/src/MAOToolkit.Web/Utilities/ModelBinders/FlagsEnumModelBinder.cs new file mode 100644 index 0000000..4f2fb4c --- /dev/null +++ b/src/MAOToolkit.Web/Utilities/ModelBinders/FlagsEnumModelBinder.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace MAOToolkit.Utilities.ModelBinders +{ + public class FlagsEnumModelBinder : IModelBinder + { + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext is null) + { + throw new ArgumentNullException(nameof(bindingContext)); + } + + // Only accept enum values. + if (!bindingContext.ModelMetadata.IsFlagsEnum) + return Task.CompletedTask; + + var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); + + if (valueProviderResult == ValueProviderResult.None) + { + return Task.CompletedTask; + } + + // Get the real enum type. + var enumType = bindingContext.ModelType; + enumType = Nullable.GetUnderlyingType(enumType) ?? enumType; + + // Each value self may contains a series of actual values, split it with comma. + var strs = valueProviderResult.Values.SelectMany(s => s?.Split(',') ?? Array.Empty()); + + // Convert all items into enum items. + var actualValues = strs.Select(valueString => Enum.Parse(enumType, valueString)); + + // Merge to final result. + var result = actualValues.Aggregate(0, (current, value) => current | Convert.ToInt32(value)); + + // Convert to Enum object. + var realResult = Enum.ToObject(enumType, result); + + // Result + bindingContext.Result = ModelBindingResult.Success(realResult); + return Task.CompletedTask; + } + } + + /// + /// + /// An used to provider instances. + /// + public class FlagsEnumModelBinderProvider : IModelBinderProvider + { + /// + /// + /// Creates a based on . + /// + /// The . + /// An . + public IModelBinder? GetBinder(ModelBinderProviderContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return context.Metadata.IsFlagsEnum ? new FlagsEnumModelBinder() : null; + } + } + + public static class FlagsEnumModelBinderExtensions + { + public static void AddFlagsEnumProvider(this MvcOptions option) + { + option.ModelBinderProviders.Insert(0, new FlagsEnumModelBinderProvider()); + } + } +} \ No newline at end of file diff --git a/src/MAOToolkit/Extensions/QueryableExtensions.cs b/src/MAOToolkit/Extensions/QueryableExtensions.cs index 9b8da32..808fec9 100644 --- a/src/MAOToolkit/Extensions/QueryableExtensions.cs +++ b/src/MAOToolkit/Extensions/QueryableExtensions.cs @@ -1,9 +1,24 @@ using System.Linq.Expressions; - +using System.Reflection; + namespace MAOToolkit.Extensions { public static class QueryableExtensions - { + { + public static IOrderedQueryable OrderBy(this IQueryable queryable, string orderByProperty, bool desc = false) + { + string command = desc ? "OrderByDescending" : "OrderBy"; + var type = typeof(T); + var property = type.GetProperty(orderByProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + ArgumentNullException.ThrowIfNull(property); + + var parameter = Expression.Parameter(type, "p"); + var propertyAccess = Expression.MakeMemberAccess(parameter, property); + var orderByExpression = Expression.Lambda(propertyAccess, parameter); + var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, queryable.Expression, Expression.Quote(orderByExpression)); + return (IOrderedQueryable)queryable.Provider.CreateQuery(resultExpression); + } + public static IQueryable WhereAny(this IQueryable queryable, params Expression>[] predicates) { var parameter = Expression.Parameter(typeof(T));