diff --git a/Mvc.JQuery.DataTables.AspNetCore.Example/Mvc.JQuery.DataTables.AspNetCore.Example.csproj b/Mvc.JQuery.DataTables.AspNetCore.Example/Mvc.JQuery.DataTables.AspNetCore.Example.csproj index 001d716..c2ad9ea 100644 --- a/Mvc.JQuery.DataTables.AspNetCore.Example/Mvc.JQuery.DataTables.AspNetCore.Example.csproj +++ b/Mvc.JQuery.DataTables.AspNetCore.Example/Mvc.JQuery.DataTables.AspNetCore.Example.csproj @@ -1,94 +1,18 @@  - netcoreapp1.0 + netcoreapp2.0 - $(PackageTargetFallback);portable-net45+win8+wp8+wpa81; Mvc.JQuery.DataTables.Example - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs b/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs index 7cb2650..81bae70 100644 --- a/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs +++ b/Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs @@ -99,9 +99,42 @@ private DataTablesParam BindLegacyModel(IValueProvider valueProvider, int column private static T GetValue(IValueProvider valueProvider, string key) { ValueProviderResult valueResult = valueProvider.GetValue(key); - return (valueResult==null) + return (valueResult == null) ? default(T) - : (T)valueResult.ConvertTo(typeof(T)); + : ConvertTo(valueResult); + } + + /// + /// Attempts to convert the values in to the specified type. + /// + /// The for conversion. + /// The . + /// + /// The converted value, or the default value of if the value could not be converted. + /// + /// + /// Copyright (c) .NET Foundation and Contributors + /// All rights reserved. + /// Licensed under the Apache License, Version 2.0 (the "License"); you may not use + /// this file except in compliance with the License.You may obtain a copy of the license at: + /// http://www.apache.org/licenses/LICENSE-2.0 + /// Unless required by applicable law or agreed to in writing, software distributed + /// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + /// CONDITIONS OF ANY KIND, either express or implied. See the License for the + /// specific language governing permissions and limitations under the License. + /// + private static T ConvertTo(ValueProviderResult result) + { + object valueToConvert = null; + if (result.Values.Count == 1) + { + valueToConvert = result.Values[0]; + } + else if (result.Values.Count > 1) + { + valueToConvert = result.Values.ToArray(); + } + return ModelBindingHelper.ConvertTo(valueToConvert, result.Culture); } } 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 5cbede3..523ea5d 100644 --- a/Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj +++ b/Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj @@ -1,24 +1,30 @@  - netstandard1.6;net451 + 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 + - - - - + + + + + + + + + diff --git a/Mvc.JQuery.DataTables.Common/DataTablesFiltering.cs b/Mvc.JQuery.DataTables.Common/DataTablesFiltering.cs index 1a9cb4f..0d2ce61 100644 --- a/Mvc.JQuery.DataTables.Common/DataTablesFiltering.cs +++ b/Mvc.JQuery.DataTables.Common/DataTablesFiltering.cs @@ -7,7 +7,7 @@ namespace Mvc.JQuery.DataTables { - internal class DataTablesFiltering + public class DataTablesFiltering { public IQueryable ApplyFiltersAndSort(DataTablesParam dtParameters, IQueryable data, DataTablesPropertyInfo[] columns) { @@ -30,7 +30,11 @@ public IQueryable ApplyFiltersAndSort(DataTablesParam dtParameters, IQuery } } var values = parts.Where(p => p != null); - data = data.Where(string.Join(" or ", values), parameters.ToArray()); + var filterClause = string.Join(" or ", values); + if (string.IsNullOrWhiteSpace(filterClause) == false) + { + data = data.Where(filterClause, parameters.ToArray()); + } } for (int i = 0; i < dtParameters.sSearchValues.Count; i++) { @@ -114,7 +118,7 @@ private static ReturnedFilteredQueryForType Guard(Func(GuardedFilter filter) { - Filters.Add(Guard(arg => arg is T, filter)); + Filters.Add(Guard(arg => arg.Type == typeof(T), filter)); } private static string GetFilterClause(string query, DataTablesPropertyInfo column, List parametersForLinqQuery) diff --git a/Mvc.JQuery.DataTables.Common/Mvc.JQuery.DataTables.Common.csproj b/Mvc.JQuery.DataTables.Common/Mvc.JQuery.DataTables.Common.csproj index 81bfe7a..4e1cb47 100644 --- a/Mvc.JQuery.DataTables.Common/Mvc.JQuery.DataTables.Common.csproj +++ b/Mvc.JQuery.DataTables.Common/Mvc.JQuery.DataTables.Common.csproj @@ -24,10 +24,10 @@ - - - - + + + + diff --git a/Mvc.JQuery.DataTables.Common/Processing/TypeFilters.cs b/Mvc.JQuery.DataTables.Common/Processing/TypeFilters.cs index c230ff7..0ba2111 100644 --- a/Mvc.JQuery.DataTables.Common/Processing/TypeFilters.cs +++ b/Mvc.JQuery.DataTables.Common/Processing/TypeFilters.cs @@ -111,12 +111,14 @@ public static string DateTimeOffsetFilter(string query, string columnname, DataT DateTimeOffset start, end; if (DateTimeOffset.TryParse(parts[0] ?? "", out start)) { + start = start.ToUniversalTime(); filterString = columnname + " >= @" + parametersForLinqQuery.Count; parametersForLinqQuery.Add(start); } if (DateTimeOffset.TryParse(parts[1] ?? "", out end)) { + end = end.ToUniversalTime(); filterString = (filterString == null ? null : filterString + " and ") + columnname + " <= @" + parametersForLinqQuery.Count; parametersForLinqQuery.Add(end); } @@ -161,12 +163,14 @@ public static string DateTimeFilter(string query, string columnname, DataTablesP DateTime start, end; if (DateTime.TryParse(parts[0] ?? "", out start)) { + start = start.ToUniversalTime(); filterString = columnname + " >= @" + parametersForLinqQuery.Count; parametersForLinqQuery.Add(start); } if (DateTime.TryParse(parts[1] ?? "", out end)) { + end = end.ToUniversalTime(); filterString = (filterString == null ? null : filterString + " and ") + columnname + " <= @" + parametersForLinqQuery.Count; parametersForLinqQuery.Add(end); } @@ -247,11 +251,17 @@ public static string StringFilter(string q, string columnname, DataTablesPropert public static string EnumFilter(string q, string columnname, DataTablesPropertyInfo propertyInfo, List parametersForLinqQuery) { - - if (q.StartsWith("^")) q = q.Substring(1); - if (q.EndsWith("$")) q = q.Substring(0, q.Length - 1); - parametersForLinqQuery.Add(ParseValue(q, propertyInfo.Type)); - return columnname + " == @" + (parametersForLinqQuery.Count - 1); + try + { + if (q.StartsWith("^")) q = q.Substring(1); + if (q.EndsWith("$")) q = q.Substring(0, q.Length - 1); + parametersForLinqQuery.Add(ParseValue(q, propertyInfo.Type)); + return columnname + " == @" + (parametersForLinqQuery.Count - 1); + } + catch (Exception) + { + return null; + } } } -} \ No newline at end of file +} diff --git a/Mvc.JQuery.Datatables.Templates/Mvc.JQuery.Datatables.Templates.csproj b/Mvc.JQuery.Datatables.Templates/Mvc.JQuery.Datatables.Templates.csproj index a8183bc..3bc7a4c 100644 --- a/Mvc.JQuery.Datatables.Templates/Mvc.JQuery.Datatables.Templates.csproj +++ b/Mvc.JQuery.Datatables.Templates/Mvc.JQuery.Datatables.Templates.csproj @@ -12,10 +12,10 @@ - - - - + + + +