Skip to content

Commit

Permalink
support for SmartEnum Model binding in razor pages, fixes #70 (#210)
Browse files Browse the repository at this point in the history
Co-authored-by: Steve Smith <steve@kentsmiths.com>
  • Loading branch information
nagasudhirpulla and ardalis authored Feb 2, 2022
1 parent 7838112 commit ab7d6a0
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
15 changes: 15 additions & 0 deletions SmartEnum.ModelBinding/SmartEnum.ModelBinding.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\SmartEnum\SmartEnum.csproj" />
</ItemGroup>

</Project>
20 changes: 20 additions & 0 deletions SmartEnum.ModelBinding/SmartEnumBinderProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using System;

namespace Ardalis.SmartEnum.ModelBinding
{
public class SmartEnumBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));

if (TypeUtil.IsDerived(context.Metadata.ModelType, typeof(SmartEnum<,>)))
return new BinderTypeModelBinder(typeof(SmartEnumModelBinder));

return null;
}
}
}
54 changes: 54 additions & 0 deletions SmartEnum.ModelBinding/SmartEnumModelBinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Reflection;
using System.Threading.Tasks;

namespace Ardalis.SmartEnum.ModelBinding
{
public class SmartEnumModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));

Type modelType = bindingContext.ModelMetadata.ModelType;

if (!TypeUtil.IsDerived(modelType, typeof(SmartEnum<,>)))
throw new ArgumentException($"{modelType} is not a SmartEnum");

string propertyName = bindingContext.ModelName;
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(propertyName);
if (valueProviderResult == ValueProviderResult.None)
return Task.CompletedTask;

bindingContext.ModelState.SetModelValue(propertyName, valueProviderResult);

string enumKeyName = valueProviderResult.FirstValue;

if (string.IsNullOrEmpty(enumKeyName))
return Task.CompletedTask;

// Create smart enum instance from enum key name by calling the FromName static method on the SmartEnum Class
Type baseSmartEnumType = TypeUtil.GetTypeFromGenericType(modelType, typeof(SmartEnum<,>));
foreach (MethodInfo methodInfo in baseSmartEnumType.GetMethods())
{
if (methodInfo.Name == "FromName")
{
ParameterInfo[] methodsParams = methodInfo.GetParameters();
if (methodsParams.Length == 2)
{
if (methodsParams[0].ParameterType == typeof(string) && methodsParams[1].ParameterType == typeof(bool))
{
var enumObj = methodInfo.Invoke(null, new object[] { enumKeyName, true });
bindingContext.Result = ModelBindingResult.Success(enumObj);
return Task.CompletedTask;
}
}
}
}
bindingContext.ModelState.TryAddModelError(propertyName, $"unable to call FromName on the SmartEnum of type {modelType}");
return Task.CompletedTask;
}
}
}
47 changes: 47 additions & 0 deletions SmartEnum.ModelBinding/TypeUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;

namespace Ardalis.SmartEnum.ModelBinding
{
internal static class TypeUtil
{
public static bool IsDerived(Type objectType, Type mainType)
{
Type currentType = objectType.BaseType;

if (currentType == null)
{
return false;
}

while (currentType != typeof(object))
{
if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == mainType)
return true;

currentType = currentType.BaseType;
}

return false;
}

public static Type GetTypeFromGenericType(Type objectType, Type mainType)
{
Type currentType = objectType.BaseType;

if (currentType == null)
{
return null;
}

while (currentType != typeof(object))
{
if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == mainType)
return currentType;

currentType = currentType.BaseType;
}

return null;
}
}
}
7 changes: 7 additions & 0 deletions SmartEnum.sln
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartEnum.EFCore.Integratio
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IntegrationTests", "IntegrationTests", "{EF5634F4-4667-4481-934C-D1CFA042AD0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartEnum.ModelBinding", "SmartEnum.ModelBinding\SmartEnum.ModelBinding.csproj", "{299BCE09-6CA5-4E07-AD55-F3C8AAB30155}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -134,6 +136,10 @@ Global
{DF5E3B19-AE8E-48A4-B023-4FA42D6EA8FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF5E3B19-AE8E-48A4-B023-4FA42D6EA8FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF5E3B19-AE8E-48A4-B023-4FA42D6EA8FF}.Release|Any CPU.Build.0 = Release|Any CPU
{299BCE09-6CA5-4E07-AD55-F3C8AAB30155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{299BCE09-6CA5-4E07-AD55-F3C8AAB30155}.Debug|Any CPU.Build.0 = Debug|Any CPU
{299BCE09-6CA5-4E07-AD55-F3C8AAB30155}.Release|Any CPU.ActiveCfg = Release|Any CPU
{299BCE09-6CA5-4E07-AD55-F3C8AAB30155}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -157,6 +163,7 @@ Global
{CDA8EF6E-F678-4FD3-80B0-5C61FC76EE51} = {FA199ECB-5F29-442A-AAC6-91DBCB7A5A04}
{DF5E3B19-AE8E-48A4-B023-4FA42D6EA8FF} = {EF5634F4-4667-4481-934C-D1CFA042AD0B}
{EF5634F4-4667-4481-934C-D1CFA042AD0B} = {79268877-BBEF-4DE2-B8D9-697F21933159}
{299BCE09-6CA5-4E07-AD55-F3C8AAB30155} = {FA199ECB-5F29-442A-AAC6-91DBCB7A5A04}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46896DE3-41B8-442F-A6FB-6AC9F11CCBCE}
Expand Down

0 comments on commit ab7d6a0

Please sign in to comment.