From 730f32be0c5a22611351f7e04abce12fe152d640 Mon Sep 17 00:00:00 2001 From: Raymen Scholten Date: Tue, 31 Mar 2020 15:00:16 +0200 Subject: [PATCH] Support asp.net core 3.1 (#178) --- .../DataTablesModelBinder.cs | 1 - .../Extensions.cs | 10 +- .../ModelBindingHelper.cs | 181 ++++++++++++++++++ .../Mvc.JQuery.DataTables.AspNetCore.csproj | 14 +- 4 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 Mvc.JQuery.DataTables.AspNetCore/ModelBindingHelper.cs diff --git a/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs b/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs index 8a4ab8b..81bae70 100644 --- a/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs +++ b/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Internal; using System; using System.Threading.Tasks; diff --git a/Mvc.JQuery.DataTables.AspNetCore/Extensions.cs b/Mvc.JQuery.DataTables.AspNetCore/Extensions.cs index 073eb43..e9ef853 100644 --- a/Mvc.JQuery.DataTables.AspNetCore/Extensions.cs +++ b/Mvc.JQuery.DataTables.AspNetCore/Extensions.cs @@ -5,6 +5,9 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc; using Mvc.JQuery.DataTables; +#if NETCOREAPP3_1 +using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; +#endif namespace Microsoft.Extensions.DependencyInjection { @@ -20,7 +23,12 @@ public static IServiceCollection AddMvcJQueryDataTables(this IServiceCollection dataTablesViewModelType.Namespace + ".Common"), }; services.AddSingleton(settings); +#if NETCOREAPP3_1 + services.Configure(s => s.FileProviders.Add(settings.FileProvider)); +#elif NETSTANDARD2_0 services.Configure(s => s.FileProviders.Add(settings.FileProvider)); +#endif + services.AddMvc(options => { options.UseHtmlEncodeModelBinding(); }); return services; @@ -46,7 +54,7 @@ public static class MvcJQueryDataTablesExtensions public static IApplicationBuilder UseMvcJQueryDataTables(this IApplicationBuilder app) { var settings = app.ApplicationServices.GetService(); - if(settings == null) + if (settings == null) { throw new InvalidOperationException("Unable to find the required services. Please add all the required services by calling 'IServiceCollection.{}' inside the call to 'ConfigureServices(...)' in the application startup code."); } diff --git a/Mvc.JQuery.DataTables.AspNetCore/ModelBindingHelper.cs b/Mvc.JQuery.DataTables.AspNetCore/ModelBindingHelper.cs new file mode 100644 index 0000000..aa4637d --- /dev/null +++ b/Mvc.JQuery.DataTables.AspNetCore/ModelBindingHelper.cs @@ -0,0 +1,181 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Runtime.ExceptionServices; + +namespace Mvc.JQuery.DataTables +{ + internal static class ModelBindingHelper + { + /// + /// Converts the provided to a value of . + /// + /// The for conversion. + /// The value to convert."/> + /// The for conversion. + /// + /// The converted value or the default value of if the value could not be converted. + /// + public static T ConvertTo(object value, CultureInfo culture) + { + var converted = ConvertTo(value, typeof(T), culture); + return converted == null ? default(T) : (T)converted; + } + + /// + /// Converts the provided to a value of . + /// + /// The value to convert."/> + /// The for conversion. + /// The for conversion. + /// + /// The converted value or null if the value could not be converted. + /// + public static object ConvertTo(object value, Type type, CultureInfo culture) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (value == null) + { + // For value types, treat null values as though they were the default value for the type. + return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; + } + + if (type.IsAssignableFrom(value.GetType())) + { + return value; + } + + var cultureToUse = culture ?? CultureInfo.InvariantCulture; + return UnwrapPossibleArrayType(value, type, cultureToUse); + } + + private static object UnwrapPossibleArrayType(object value, Type destinationType, CultureInfo culture) + { + // array conversion results in four cases, as below + var valueAsArray = value as Array; + if (destinationType.IsArray) + { + var destinationElementType = destinationType.GetElementType(); + if (valueAsArray != null) + { + // case 1: both destination + source type are arrays, so convert each element + var converted = (IList)Array.CreateInstance(destinationElementType, valueAsArray.Length); + for (var i = 0; i < valueAsArray.Length; i++) + { + converted[i] = ConvertSimpleType(valueAsArray.GetValue(i), destinationElementType, culture); + } + return converted; + } + else + { + // case 2: destination type is array but source is single element, so wrap element in + // array + convert + var element = ConvertSimpleType(value, destinationElementType, culture); + var converted = (IList)Array.CreateInstance(destinationElementType, 1); + converted[0] = element; + return converted; + } + } + else if (valueAsArray != null) + { + // case 3: destination type is single element but source is array, so extract first element + convert + if (valueAsArray.Length > 0) + { + value = valueAsArray.GetValue(0); + return ConvertSimpleType(value, destinationType, culture); + } + else + { + // case 3(a): source is empty array, so can't perform conversion + return null; + } + } + + // case 4: both destination + source type are single elements, so convert + return ConvertSimpleType(value, destinationType, culture); + } + + private static object ConvertSimpleType(object value, Type destinationType, CultureInfo culture) + { + if (value == null || destinationType.IsAssignableFrom(value.GetType())) + { + return value; + } + + // In case of a Nullable object, we try again with its underlying type. + destinationType = UnwrapNullableType(destinationType); + + // if this is a user-input value but the user didn't type anything, return no value + if (value is string valueAsString && string.IsNullOrWhiteSpace(valueAsString)) + { + return null; + } + + var converter = TypeDescriptor.GetConverter(destinationType); + var canConvertFrom = converter.CanConvertFrom(value.GetType()); + if (!canConvertFrom) + { + converter = TypeDescriptor.GetConverter(value.GetType()); + } + if (!(canConvertFrom || converter.CanConvertTo(destinationType))) + { + // EnumConverter cannot convert integer, so we verify manually + if (destinationType.GetTypeInfo().IsEnum && + (value is int || + value is uint || + value is long || + value is ulong || + value is short || + value is ushort || + value is byte || + value is sbyte)) + { + return Enum.ToObject(destinationType, value); + } + + throw new InvalidOperationException($"NoConverterExists: {value.GetType()} -> {destinationType}"); + } + + try + { + return canConvertFrom + ? converter.ConvertFrom(null, culture, value) + : converter.ConvertTo(null, culture, value, destinationType); + } + catch (FormatException) + { + throw; + } + catch (Exception ex) + { + if (ex.InnerException == null) + { + throw; + } + else + { + // TypeConverter throws System.Exception wrapping the FormatException, + // so we throw the inner exception. + ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); + + // This code is never reached because the previous line will always throw. + throw; + } + } + } + + private static Type UnwrapNullableType(Type destinationType) + { + return Nullable.GetUnderlyingType(destinationType) ?? destinationType; + } + } +} \ No newline at end of file diff --git a/Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj b/Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj index f741182..523ea5d 100644 --- a/Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj +++ b/Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj @@ -1,21 +1,27 @@  - netstandard2.0 + netcoreapp3.1;netstandard2.0 Mvc.JQuery.DataTables Mvc.JQuery.DataTables.AspNetCore Mvc.JQuery.DataTables.AspNetCore - 1.0.0 + 2.0.0 Harry McIntyre Popular lib for using DataTables.net with IQueryable. Install this package to use with AspNetCore false https://github.com/mcintyre321/mvc.jquery.datatables Harry McIntyre jquery datatables iqueryable razor asp mvc mvc5 - https://github.com/mcintyre321/mvc.jquery.datatables/blob/master/License.txt + https://github.com/mcintyre321/mvc.jquery.datatables/blob/master/License.txt + - + + + + + +