Skip to content

Commit 730f32b

Browse files
authored
Support asp.net core 3.1 (#178)
1 parent 6e77906 commit 730f32b

File tree

4 files changed

+200
-6
lines changed

4 files changed

+200
-6
lines changed

Mvc.JQuery.DataTables.AspNetCore/DataTablesModelBinder.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Microsoft.AspNetCore.Mvc.ModelBinding;
2-
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
32
using System;
43
using System.Threading.Tasks;
54

Mvc.JQuery.DataTables.AspNetCore/Extensions.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
using System.Reflection;
66
using Microsoft.AspNetCore.Mvc;
77
using Mvc.JQuery.DataTables;
8+
#if NETCOREAPP3_1
9+
using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
10+
#endif
811

912
namespace Microsoft.Extensions.DependencyInjection
1013
{
@@ -20,7 +23,12 @@ public static IServiceCollection AddMvcJQueryDataTables(this IServiceCollection
2023
dataTablesViewModelType.Namespace + ".Common"),
2124
};
2225
services.AddSingleton(settings);
26+
#if NETCOREAPP3_1
27+
services.Configure<MvcRazorRuntimeCompilationOptions>(s => s.FileProviders.Add(settings.FileProvider));
28+
#elif NETSTANDARD2_0
2329
services.Configure<RazorViewEngineOptions>(s => s.FileProviders.Add(settings.FileProvider));
30+
#endif
31+
2432
services.AddMvc(options => { options.UseHtmlEncodeModelBinding(); });
2533

2634
return services;
@@ -46,7 +54,7 @@ public static class MvcJQueryDataTablesExtensions
4654
public static IApplicationBuilder UseMvcJQueryDataTables(this IApplicationBuilder app)
4755
{
4856
var settings = app.ApplicationServices.GetService<global::Mvc.JQuery.DataTables.Settings>();
49-
if(settings == null)
57+
if (settings == null)
5058
{
5159
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.");
5260
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections;
6+
using System.ComponentModel;
7+
using System.Globalization;
8+
using System.Reflection;
9+
using System.Runtime.ExceptionServices;
10+
11+
namespace Mvc.JQuery.DataTables
12+
{
13+
internal static class ModelBindingHelper
14+
{
15+
/// <summary>
16+
/// Converts the provided <paramref name="value"/> to a value of <see cref="Type"/> <typeparamref name="T"/>.
17+
/// </summary>
18+
/// <typeparam name="T">The <see cref="Type"/> for conversion.</typeparam>
19+
/// <param name="value">The value to convert."/></param>
20+
/// <param name="culture">The <see cref="CultureInfo"/> for conversion.</param>
21+
/// <returns>
22+
/// The converted value or the default value of <typeparamref name="T"/> if the value could not be converted.
23+
/// </returns>
24+
public static T ConvertTo<T>(object value, CultureInfo culture)
25+
{
26+
var converted = ConvertTo(value, typeof(T), culture);
27+
return converted == null ? default(T) : (T)converted;
28+
}
29+
30+
/// <summary>
31+
/// Converts the provided <paramref name="value"/> to a value of <see cref="Type"/> <paramref name="type"/>.
32+
/// </summary>
33+
/// <param name="value">The value to convert."/></param>
34+
/// <param name="type">The <see cref="Type"/> for conversion.</param>
35+
/// <param name="culture">The <see cref="CultureInfo"/> for conversion.</param>
36+
/// <returns>
37+
/// The converted value or <c>null</c> if the value could not be converted.
38+
/// </returns>
39+
public static object ConvertTo(object value, Type type, CultureInfo culture)
40+
{
41+
if (type == null)
42+
{
43+
throw new ArgumentNullException(nameof(type));
44+
}
45+
46+
if (value == null)
47+
{
48+
// For value types, treat null values as though they were the default value for the type.
49+
return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null;
50+
}
51+
52+
if (type.IsAssignableFrom(value.GetType()))
53+
{
54+
return value;
55+
}
56+
57+
var cultureToUse = culture ?? CultureInfo.InvariantCulture;
58+
return UnwrapPossibleArrayType(value, type, cultureToUse);
59+
}
60+
61+
private static object UnwrapPossibleArrayType(object value, Type destinationType, CultureInfo culture)
62+
{
63+
// array conversion results in four cases, as below
64+
var valueAsArray = value as Array;
65+
if (destinationType.IsArray)
66+
{
67+
var destinationElementType = destinationType.GetElementType();
68+
if (valueAsArray != null)
69+
{
70+
// case 1: both destination + source type are arrays, so convert each element
71+
var converted = (IList)Array.CreateInstance(destinationElementType, valueAsArray.Length);
72+
for (var i = 0; i < valueAsArray.Length; i++)
73+
{
74+
converted[i] = ConvertSimpleType(valueAsArray.GetValue(i), destinationElementType, culture);
75+
}
76+
return converted;
77+
}
78+
else
79+
{
80+
// case 2: destination type is array but source is single element, so wrap element in
81+
// array + convert
82+
var element = ConvertSimpleType(value, destinationElementType, culture);
83+
var converted = (IList)Array.CreateInstance(destinationElementType, 1);
84+
converted[0] = element;
85+
return converted;
86+
}
87+
}
88+
else if (valueAsArray != null)
89+
{
90+
// case 3: destination type is single element but source is array, so extract first element + convert
91+
if (valueAsArray.Length > 0)
92+
{
93+
value = valueAsArray.GetValue(0);
94+
return ConvertSimpleType(value, destinationType, culture);
95+
}
96+
else
97+
{
98+
// case 3(a): source is empty array, so can't perform conversion
99+
return null;
100+
}
101+
}
102+
103+
// case 4: both destination + source type are single elements, so convert
104+
return ConvertSimpleType(value, destinationType, culture);
105+
}
106+
107+
private static object ConvertSimpleType(object value, Type destinationType, CultureInfo culture)
108+
{
109+
if (value == null || destinationType.IsAssignableFrom(value.GetType()))
110+
{
111+
return value;
112+
}
113+
114+
// In case of a Nullable object, we try again with its underlying type.
115+
destinationType = UnwrapNullableType(destinationType);
116+
117+
// if this is a user-input value but the user didn't type anything, return no value
118+
if (value is string valueAsString && string.IsNullOrWhiteSpace(valueAsString))
119+
{
120+
return null;
121+
}
122+
123+
var converter = TypeDescriptor.GetConverter(destinationType);
124+
var canConvertFrom = converter.CanConvertFrom(value.GetType());
125+
if (!canConvertFrom)
126+
{
127+
converter = TypeDescriptor.GetConverter(value.GetType());
128+
}
129+
if (!(canConvertFrom || converter.CanConvertTo(destinationType)))
130+
{
131+
// EnumConverter cannot convert integer, so we verify manually
132+
if (destinationType.GetTypeInfo().IsEnum &&
133+
(value is int ||
134+
value is uint ||
135+
value is long ||
136+
value is ulong ||
137+
value is short ||
138+
value is ushort ||
139+
value is byte ||
140+
value is sbyte))
141+
{
142+
return Enum.ToObject(destinationType, value);
143+
}
144+
145+
throw new InvalidOperationException($"NoConverterExists: {value.GetType()} -> {destinationType}");
146+
}
147+
148+
try
149+
{
150+
return canConvertFrom
151+
? converter.ConvertFrom(null, culture, value)
152+
: converter.ConvertTo(null, culture, value, destinationType);
153+
}
154+
catch (FormatException)
155+
{
156+
throw;
157+
}
158+
catch (Exception ex)
159+
{
160+
if (ex.InnerException == null)
161+
{
162+
throw;
163+
}
164+
else
165+
{
166+
// TypeConverter throws System.Exception wrapping the FormatException,
167+
// so we throw the inner exception.
168+
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
169+
170+
// This code is never reached because the previous line will always throw.
171+
throw;
172+
}
173+
}
174+
}
175+
176+
private static Type UnwrapNullableType(Type destinationType)
177+
{
178+
return Nullable.GetUnderlyingType(destinationType) ?? destinationType;
179+
}
180+
}
181+
}

Mvc.JQuery.DataTables.AspNetCore/Mvc.JQuery.DataTables.AspNetCore.csproj

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0</TargetFrameworks>
4+
<TargetFrameworks>netcoreapp3.1;netstandard2.0</TargetFrameworks>
55
<RootNamespace>Mvc.JQuery.DataTables</RootNamespace>
66
<AssemblyName>Mvc.JQuery.DataTables.AspNetCore</AssemblyName>
77
<PackageId>Mvc.JQuery.DataTables.AspNetCore</PackageId>
88

9-
<PackageVersion>1.0.0</PackageVersion>
9+
<PackageVersion>2.0.0</PackageVersion>
1010
<Authors>Harry McIntyre</Authors>
1111
<Description>Popular lib for using DataTables.net with IQueryable. Install this package to use with AspNetCore</Description>
1212
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
1313
<PackageProjectUrl>https://github.com/mcintyre321/mvc.jquery.datatables</PackageProjectUrl>
1414
<Copyright>Harry McIntyre</Copyright>
1515
<PackageTags>jquery datatables iqueryable razor asp mvc mvc5</PackageTags>
16-
<PackageLicenseUrl>https://github.com/mcintyre321/mvc.jquery.datatables/blob/master/License.txt</PackageLicenseUrl> </PropertyGroup>
16+
<PackageLicenseUrl>https://github.com/mcintyre321/mvc.jquery.datatables/blob/master/License.txt</PackageLicenseUrl>
17+
</PropertyGroup>
1718

18-
<ItemGroup>
19+
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
20+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
21+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.*" />
22+
</ItemGroup>
23+
24+
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
1925
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.*" />
2026
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.*" />
2127
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="2.0.*" />

0 commit comments

Comments
 (0)