Skip to content

Commit

Permalink
Reverted ValidateField to use FieldIdentifier.Model (#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrissainty authored May 9, 2022
1 parent 583b903 commit f8eff24
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 83 deletions.
8 changes: 4 additions & 4 deletions samples/BlazorServer/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<hr class="mb-5" />

<EditForm Model="@Person" OnValidSubmit="@SubmitValidForm">
<FluentValidationValidator @ref="fluentValidationValidator" DisableAssemblyScanning="@true" />
<FluentValidationValidator @ref="_fluentValidationValidator" DisableAssemblyScanning="@true" />
<ValidationSummary />

<p>
Expand Down Expand Up @@ -70,9 +70,9 @@
<button @onclick="PartialValidate">Partial Validation</button>

@code {
Person Person { get; set; } = new Person();
Person Person { get; set; } = new();

private FluentValidationValidator fluentValidationValidator;
private FluentValidationValidator? _fluentValidationValidator;

void SubmitValidForm()
{
Expand All @@ -81,6 +81,6 @@

void PartialValidate()
{
Console.WriteLine($"Partial validation result : {fluentValidationValidator.Validate(options => options.IncludeRuleSets("Names"))}");
Console.WriteLine($"Partial validation result : {_fluentValidationValidator?.Validate(options => options.IncludeRuleSets("Names"))}");
}
}
1 change: 1 addition & 0 deletions samples/BlazorServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
builder.Services.AddServerSideBlazor(c => c.DetailedErrors = true);

builder.Services.AddTransient<IValidator<Person>, PersonValidator>();
builder.Services.AddTransient<IValidator<Address>, AddressValidator>();

var app = builder.Build();

Expand Down
6 changes: 3 additions & 3 deletions samples/BlazorServer/Shared/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
</div>

@code {
bool collapseNavMenu = true;
bool _collapseNavMenu = true;

string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
string? NavMenuCssClass => _collapseNavMenu ? "collapse" : null;

void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
_collapseNavMenu = !_collapseNavMenu;
}
}
4 changes: 2 additions & 2 deletions samples/BlazorWebAssembly/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
@code {
Person Person { get; set; } = new Person();

private FluentValidationValidator _fluentValidationValidator;
private FluentValidationValidator? _fluentValidationValidator;

void SubmitValidForm()
{
Expand All @@ -81,6 +81,6 @@

void PartialValidate()
{
Console.WriteLine($"Partial validation result : {_fluentValidationValidator.Validate(options => options.IncludeRuleSets("Names"))}");
Console.WriteLine($"Partial validation result : {_fluentValidationValidator?.Validate(options => options.IncludeRuleSets("Names"))}");
}
}
10 changes: 5 additions & 5 deletions samples/Shared/SharedModels/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ namespace SharedModels
{
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
public string? Line1 { get; set; }
public string? Line2 { get; set; }
public string? Town { get; set; }
public string? County { get; set; }
public string? Postcode { get; set; }
}

public class AddressValidator : AbstractValidator<Address>
Expand Down
13 changes: 6 additions & 7 deletions samples/Shared/SharedModels/Person.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using FluentValidation;
using System.Threading.Tasks;

namespace SharedModels
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int? Age { get; set; }
public string EmailAddress { get; set; }
public Address Address { get; set; } = new Address();
public string? EmailAddress { get; set; }
public Address Address { get; set; } = new();
}

public class PersonValidator : AbstractValidator<Person>
Expand All @@ -35,12 +34,12 @@ public PersonValidator()
RuleFor(p => p.EmailAddress)
.NotEmpty().WithMessage("You must enter a email address")
.EmailAddress().WithMessage("You must provide a valid email address")
.MustAsync(async (email, cancellationToken) => await IsUniqueAsync(email)).WithMessage("Email address must be unique").When(p => !string.IsNullOrEmpty(p.EmailAddress));
.MustAsync(async (email, _) => await IsUniqueAsync(email)).WithMessage("Email address must be unique").When(p => !string.IsNullOrEmpty(p.EmailAddress));

RuleFor(p => p.Address).SetValidator(new AddressValidator());
}

private async Task<bool> IsUniqueAsync(string email)
private static async Task<bool> IsUniqueAsync(string email)
{
await Task.Delay(300);
return email.ToLower() != "mail@my.com";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,28 @@
using FluentValidation.Internal;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using static FluentValidation.AssemblyScanner;

namespace Blazored.FluentValidation
{
public static class EditContextFluentValidationExtensions
{
private static readonly char[] Separators = { '.', '[' };
private static readonly List<string> ScannedAssembly = new List<string>();
private static readonly List<AssemblyScanResult> AssemblyScanResults = new List<AssemblyScanResult>();
private static readonly List<string> ScannedAssembly = new();
private static readonly List<AssemblyScanResult> AssemblyScanResults = new();

public static async Task<EditContext> AddFluentValidation(this EditContext editContext, IServiceProvider serviceProvider, bool disableAssemblyScanning, IValidator validator, FluentValidationValidator fluentValidationValidator)
public static async Task<EditContext> AddFluentValidation(this EditContext editContext, IServiceProvider serviceProvider, bool disableAssemblyScanning, IValidator? validator, FluentValidationValidator fluentValidationValidator)

{
ArgumentNullException.ThrowIfNull(editContext, nameof(editContext));

var messages = new ValidationMessageStore(editContext);

editContext.OnValidationRequested +=
async (sender, eventArgs) => await ValidateModel((EditContext)sender, messages, serviceProvider, disableAssemblyScanning, fluentValidationValidator, validator);
async (sender, _) => await ValidateModel((EditContext)sender!, messages, serviceProvider, disableAssemblyScanning, fluentValidationValidator, validator);

editContext.OnFieldChanged +=
async (sender, eventArgs) => await ValidateField(editContext, messages, eventArgs.FieldIdentifier, serviceProvider, disableAssemblyScanning, validator);
async (_, eventArgs) => await ValidateField(editContext, messages, eventArgs.FieldIdentifier, serviceProvider, disableAssemblyScanning, validator);

return await Task.FromResult(editContext);
}
Expand All @@ -36,11 +33,11 @@ private static async Task ValidateModel(EditContext editContext,
IServiceProvider serviceProvider,
bool disableAssemblyScanning,
FluentValidationValidator fluentValidationValidator,
IValidator validator = null)
IValidator? validator = null)
{
validator ??= GetValidatorForModel(serviceProvider, editContext.Model, disableAssemblyScanning);

if (validator is object)
if (validator is not null)
{
var context = ValidationContext<object>.CreateWithOptions(editContext.Model, fluentValidationValidator.Options ?? (opt => opt.IncludeAllRuleSets()));

Expand All @@ -62,14 +59,14 @@ private static async Task ValidateField(EditContext editContext,
FieldIdentifier fieldIdentifier,
IServiceProvider serviceProvider,
bool disableAssemblyScanning,
IValidator validator = null)
IValidator? validator = null)
{
var properties = new[] { fieldIdentifier.FieldName };
var context = new ValidationContext<object>(editContext.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));

validator ??= GetValidatorForModel(serviceProvider, editContext.Model, disableAssemblyScanning);
var context = new ValidationContext<object>(fieldIdentifier.Model, new PropertyChain(), new MemberNameValidatorSelector(properties));
validator ??= GetValidatorForModel(serviceProvider, fieldIdentifier.Model, disableAssemblyScanning);

if (validator is object)
if (validator is not null)
{
var validationResults = await validator.ValidateAsync(context);

Expand All @@ -80,47 +77,45 @@ private static async Task ValidateField(EditContext editContext,
}
}

private static IValidator GetValidatorForModel(IServiceProvider serviceProvider, object model, bool disableAssemblyScanning)
private static IValidator? GetValidatorForModel(IServiceProvider serviceProvider, object model, bool disableAssemblyScanning)
{
var validatorType = typeof(IValidator<>).MakeGenericType(model.GetType());
if (serviceProvider != null)
try
{
try
{
if (serviceProvider.GetService(validatorType) is IValidator validator)
{
return validator;
}
}
catch (Exception)
if (serviceProvider.GetService(validatorType) is IValidator validator)
{
return validator;
}
}
catch (Exception)
{
// ignored
}

if (disableAssemblyScanning)
{
return null;
}

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(i => !ScannedAssembly.Contains(i.FullName)))
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(i => i.FullName is not null && !ScannedAssembly.Contains(i.FullName)))
{
try
{
AssemblyScanResults.AddRange(FindValidatorsInAssembly(assembly));
}
catch (Exception)
{
// ignored
}

ScannedAssembly.Add(assembly.FullName);
ScannedAssembly.Add(assembly.FullName!);
}


var interfaceValidatorType = typeof(IValidator<>).MakeGenericType(model.GetType());
var modelValidatorType = AssemblyScanResults.FirstOrDefault(i => interfaceValidatorType.IsAssignableFrom(i.InterfaceType))?.ValidatorType;

Type modelValidatorType = AssemblyScanResults.FirstOrDefault(i => interfaceValidatorType.IsAssignableFrom(i.InterfaceType))?.ValidatorType;

if (modelValidatorType == null)
if (modelValidatorType is null)
{
return null;
}
Expand Down Expand Up @@ -154,20 +149,21 @@ private static FieldIdentifier ToFieldIdentifier(in EditContext editContext, in
var nextToken = propertyPathAsSpan.Slice(0, nextTokenEnd);
propertyPathAsSpan = propertyPathAsSpan.Slice(nextTokenEnd + 1);

object newObj;
object? newObj;
if (nextToken.EndsWith("]"))
{
// It's an indexer
// This code assumes C# conventions (one indexer named Item with one param)
nextToken = nextToken.Slice(0, nextToken.Length - 1);
var prop = obj.GetType().GetProperty("Item");

if (prop is object)
if (prop is not null)
{
// we've got an Item property
var indexerType = prop.GetIndexParameters()[0].ParameterType;
var indexerValue = Convert.ChangeType(nextToken.ToString(), indexerType);
newObj = prop.GetValue(obj, new object[] { indexerValue });

newObj = prop.GetValue(obj, new [] { indexerValue });
}
else
{
Expand Down Expand Up @@ -210,30 +206,5 @@ private static FieldIdentifier ToFieldIdentifier(in EditContext editContext, in
}
}
}

/// <summary>
/// Runs an function that returns a value and ignores any Exceptions that occur.
/// Returns true or falls depending on whether catch was
/// triggered
/// </summary>
/// <param name="operation">parameterless lamda that returns a value of T</param>
/// <param name="defaultValue">Default value returned if operation fails</param>
public static T IgnoreErrors<T>(Func<T> operation, T defaultValue = default(T))
{
if (operation == null)
return defaultValue;

T result;
try
{
result = operation.Invoke();
}
catch
{
result = defaultValue;
}

return result;
}
}
}
13 changes: 9 additions & 4 deletions src/Blazored.FluentValidation/FluentValidationsValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ namespace Blazored.FluentValidation
{
public class FluentValidationValidator : ComponentBase
{
[Inject] private IServiceProvider ServiceProvider { get; set; }
[Inject] private IServiceProvider ServiceProvider { get; set; } = default!;

[CascadingParameter] private EditContext CurrentEditContext { get; set; }
[CascadingParameter] private EditContext? CurrentEditContext { get; set; }

[Parameter] public IValidator Validator { get; set; }
[Parameter] public IValidator? Validator { get; set; }
[Parameter] public bool DisableAssemblyScanning { get; set; }

internal Action<ValidationStrategy<object>> Options;
internal Action<ValidationStrategy<object>>? Options;

public bool Validate(Action<ValidationStrategy<object>> options)
{
if (CurrentEditContext is null)
{
throw new NullReferenceException(nameof(CurrentEditContext));
}

Options = options;

try
Expand Down

0 comments on commit f8eff24

Please sign in to comment.