mirror of https://github.com/abpframework/abp.git
13 changed files with 808 additions and 1 deletions
@ -0,0 +1,78 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Aspects |
|||
{ |
|||
internal static class AbpCrossCuttingConcerns |
|||
{ |
|||
public const string Auditing = "AbpAuditing"; |
|||
public const string Validation = "AbpValidation"; |
|||
public const string UnitOfWork = "AbpUnitOfWork"; |
|||
public const string Authorization = "AbpAuthorization"; |
|||
|
|||
public static void AddApplied(object obj, params string[] concerns) |
|||
{ |
|||
if (concerns.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(concerns), $"{nameof(concerns)} should be provided!"); |
|||
} |
|||
|
|||
(obj as IAvoidDuplicateCrossCuttingConcerns)?.AppliedCrossCuttingConcerns.AddRange(concerns); |
|||
} |
|||
|
|||
public static void RemoveApplied(object obj, params string[] concerns) |
|||
{ |
|||
if (concerns.IsNullOrEmpty()) |
|||
{ |
|||
throw new ArgumentNullException(nameof(concerns), $"{nameof(concerns)} should be provided!"); |
|||
} |
|||
|
|||
var crossCuttingEnabledObj = obj as IAvoidDuplicateCrossCuttingConcerns; |
|||
if (crossCuttingEnabledObj == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var concern in concerns) |
|||
{ |
|||
crossCuttingEnabledObj.AppliedCrossCuttingConcerns.RemoveAll(c => c == concern); |
|||
} |
|||
} |
|||
|
|||
public static bool IsApplied([NotNull] object obj, [NotNull] string concern) |
|||
{ |
|||
if (obj == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(obj)); |
|||
} |
|||
|
|||
if (concern == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(concern)); |
|||
} |
|||
|
|||
return (obj as IAvoidDuplicateCrossCuttingConcerns)?.AppliedCrossCuttingConcerns.Contains(concern) ?? false; |
|||
} |
|||
|
|||
public static IDisposable Applying(object obj, params string[] concerns) |
|||
{ |
|||
AddApplied(obj, concerns); |
|||
return new DisposeAction(() => |
|||
{ |
|||
RemoveApplied(obj, concerns); |
|||
}); |
|||
} |
|||
|
|||
public static string[] GetApplieds(object obj) |
|||
{ |
|||
var crossCuttingEnabledObj = obj as IAvoidDuplicateCrossCuttingConcerns; |
|||
if (crossCuttingEnabledObj == null) |
|||
{ |
|||
return new string[0]; |
|||
} |
|||
|
|||
return crossCuttingEnabledObj.AppliedCrossCuttingConcerns.ToArray(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Aspects |
|||
{ |
|||
public interface IAvoidDuplicateCrossCuttingConcerns |
|||
{ |
|||
List<string> AppliedCrossCuttingConcerns { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
/// <summary>
|
|||
/// Can be added to a method to disable auto validation.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property)] |
|||
public class DisableValidationAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
/// <summary>
|
|||
/// Can be added to a method to enable auto validation if validation is disabled for it's class.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Method)] |
|||
public class EnableValidationAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
/// <summary>
|
|||
/// This interface is used to normalize inputs before method execution.
|
|||
/// </summary>
|
|||
public interface IShouldNormalize |
|||
{ |
|||
/// <summary>
|
|||
/// This method is called lastly before method execution (after validation if exists).
|
|||
/// </summary>
|
|||
void Normalize(); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
public class AbpValidationOptions |
|||
{ |
|||
public List<Type> IgnoredTypes { get; } |
|||
|
|||
public AbpValidationOptions() |
|||
{ |
|||
IgnoredTypes = new List<Type>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,250 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Reflection; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
/// <summary>
|
|||
/// This class is used to validate a method call (invocation) for method arguments.
|
|||
/// </summary>
|
|||
public class MethodInvocationValidator : ITransientDependency |
|||
{ |
|||
private const int MaxRecursiveParameterValidationDepth = 8; |
|||
|
|||
protected MethodInfo Method { get; private set; } |
|||
protected object[] ParameterValues { get; private set; } |
|||
protected ParameterInfo[] Parameters { get; private set; } |
|||
protected List<ValidationResult> ValidationErrors { get; } |
|||
protected List<IShouldNormalize> ObjectsToBeNormalized { get; } |
|||
|
|||
private readonly AbpValidationOptions _options; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new <see cref="MethodInvocationValidator"/> instance.
|
|||
/// </summary>
|
|||
public MethodInvocationValidator(IOptions<AbpValidationOptions> options) |
|||
{ |
|||
_options = options.Value; |
|||
|
|||
ValidationErrors = new List<ValidationResult>(); |
|||
ObjectsToBeNormalized = new List<IShouldNormalize>(); |
|||
} |
|||
|
|||
/// <param name="method">Method to be validated</param>
|
|||
/// <param name="parameterValues">List of arguments those are used to call the <paramref name="method"/>.</param>
|
|||
public virtual void Initialize(MethodInfo method, object[] parameterValues) |
|||
{ |
|||
Check.NotNull(method, nameof(method)); |
|||
Check.NotNull(parameterValues, nameof(parameterValues)); |
|||
|
|||
Method = method; |
|||
ParameterValues = parameterValues; |
|||
Parameters = method.GetParameters(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Validates the method invocation.
|
|||
/// </summary>
|
|||
public void Validate() |
|||
{ |
|||
CheckInitialized(); |
|||
|
|||
if (Parameters.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (!Method.IsPublic) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (IsValidationDisabled()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (Parameters.Length != ParameterValues.Length) |
|||
{ |
|||
throw new Exception("Method parameter count does not match with argument count!"); |
|||
} |
|||
|
|||
if (ValidationErrors.Any() && HasSingleNullArgument()) |
|||
{ |
|||
ThrowValidationError(); |
|||
} |
|||
|
|||
for (var i = 0; i < Parameters.Length; i++) |
|||
{ |
|||
ValidateMethodParameter(Parameters[i], ParameterValues[i]); |
|||
} |
|||
|
|||
if (ValidationErrors.Any()) |
|||
{ |
|||
ThrowValidationError(); |
|||
} |
|||
|
|||
foreach (var objectToBeNormalized in ObjectsToBeNormalized) |
|||
{ |
|||
objectToBeNormalized.Normalize(); |
|||
} |
|||
} |
|||
|
|||
protected virtual void CheckInitialized() |
|||
{ |
|||
if (Method == null) |
|||
{ |
|||
throw new AbpException("This object has not been initialized. Call Initialize method first."); |
|||
} |
|||
} |
|||
|
|||
protected virtual bool IsValidationDisabled() |
|||
{ |
|||
if (Method.IsDefined(typeof(EnableValidationAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(Method) != null; |
|||
} |
|||
|
|||
protected virtual bool HasSingleNullArgument() |
|||
{ |
|||
return Parameters.Length == 1 && ParameterValues[0] == null; |
|||
} |
|||
|
|||
protected virtual void ThrowValidationError() |
|||
{ |
|||
throw new AbpValidationException( |
|||
"Method arguments are not valid! See ValidationErrors for details.", |
|||
ValidationErrors |
|||
); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Validates given parameter for given value.
|
|||
/// </summary>
|
|||
/// <param name="parameterInfo">Parameter of the method to validate</param>
|
|||
/// <param name="parameterValue">Value to validate</param>
|
|||
protected virtual void ValidateMethodParameter(ParameterInfo parameterInfo, object parameterValue) |
|||
{ |
|||
if (parameterValue == null) |
|||
{ |
|||
if (!parameterInfo.IsOptional && |
|||
!parameterInfo.IsOut && |
|||
!TypeHelper.IsPrimitiveExtendedIncludingNullable(parameterInfo.ParameterType, includeEnums: true)) |
|||
{ |
|||
ValidationErrors.Add(new ValidationResult(parameterInfo.Name + " is null!", new[] { parameterInfo.Name })); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
ValidateObjectRecursively(parameterValue, 1); |
|||
} |
|||
|
|||
protected virtual void ValidateObjectRecursively(object validatingObject, int currentDepth) |
|||
{ |
|||
if (currentDepth > MaxRecursiveParameterValidationDepth) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (validatingObject == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
SetDataAnnotationAttributeErrors(validatingObject); |
|||
|
|||
//Validate items of enumerable
|
|||
if (validatingObject is IEnumerable && !(validatingObject is IQueryable)) |
|||
{ |
|||
foreach (var item in (validatingObject as IEnumerable)) |
|||
{ |
|||
ValidateObjectRecursively(item, currentDepth + 1); |
|||
} |
|||
} |
|||
|
|||
//Add list to be normalized later
|
|||
if (validatingObject is IShouldNormalize) |
|||
{ |
|||
ObjectsToBeNormalized.Add(validatingObject as IShouldNormalize); |
|||
} |
|||
|
|||
//Do not recursively validate for enumerable objects
|
|||
if (validatingObject is IEnumerable) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var validatingObjectType = validatingObject.GetType(); |
|||
|
|||
//Do not recursively validate for primitive objects
|
|||
if (TypeHelper.IsPrimitiveExtendedIncludingNullable(validatingObjectType)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (_options.IgnoredTypes.Any(t => t.IsInstanceOfType(validatingObject))) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>(); |
|||
foreach (var property in properties) |
|||
{ |
|||
if (property.Attributes.OfType<DisableValidationAttribute>().Any()) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
ValidateObjectRecursively(property.GetValue(validatingObject), currentDepth + 1); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks all properties for DataAnnotations attributes.
|
|||
/// </summary>
|
|||
protected virtual void SetDataAnnotationAttributeErrors(object validatingObject) |
|||
{ |
|||
var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>(); |
|||
foreach (var property in properties) |
|||
{ |
|||
var validationAttributes = property.Attributes.OfType<ValidationAttribute>().ToArray(); |
|||
if (validationAttributes.IsNullOrEmpty()) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var validationContext = new ValidationContext(validatingObject) |
|||
{ |
|||
DisplayName = property.DisplayName, |
|||
MemberName = property.Name |
|||
}; |
|||
|
|||
foreach (var attribute in validationAttributes) |
|||
{ |
|||
var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext); |
|||
if (result != null) |
|||
{ |
|||
ValidationErrors.Add(result); |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (validatingObject is IValidatableObject) |
|||
{ |
|||
var results = (validatingObject as IValidatableObject).Validate(new ValidationContext(validatingObject)); |
|||
ValidationErrors.AddRange(results); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Aspects; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.DynamicProxy; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
public class ValidationInterceptor : AbpInterceptor, ITransientDependency |
|||
{ |
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
public ValidationInterceptor(IServiceProvider serviceProvider) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public override void Intercept(IAbpMethodInvocation invocation) |
|||
{ |
|||
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Validation)) |
|||
{ |
|||
invocation.Proceed(); |
|||
return; |
|||
} |
|||
|
|||
using (var scope = _serviceProvider.CreateScope()) |
|||
{ |
|||
var validator = scope.ServiceProvider.GetRequiredService<MethodInvocationValidator>(); |
|||
validator.Initialize(invocation.Method, invocation.Arguments); |
|||
validator.Validate(); |
|||
} |
|||
|
|||
invocation.Proceed(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
public static class ValidationInterceptorRegistrar |
|||
{ |
|||
public static void RegisterIfNeeded(IOnServiceRegistredContext context) |
|||
{ |
|||
if (typeof(IApplicationService).IsAssignableFrom(context.ImplementationType)) |
|||
{ |
|||
//TODO: Notice that it may add the interceptor more than one for every exposed service type!?
|
|||
context.Interceptors.Add<ValidationInterceptor>(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,352 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.TestBase; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Validation |
|||
{ |
|||
public class ApplicationService_Validation_Tests : AbpIntegratedTest<ApplicationService_Validation_Tests.ValidationTestModule> |
|||
{ |
|||
private readonly IMyAppService _myAppService; |
|||
|
|||
public ApplicationService_Validation_Tests() |
|||
{ |
|||
_myAppService = ServiceProvider.GetRequiredService<IMyAppService>(); |
|||
} |
|||
|
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Work_Proper_With_Right_Inputs() |
|||
{ |
|||
var output = _myAppService.MyMethod(new MyMethodInput { MyStringValue = "test" }); |
|||
output.Result.ShouldBe(42); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Work_With_Wrong_Inputs() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => _myAppService.MyMethod(new MyMethodInput())); //MyStringValue is not supplied!
|
|||
Assert.Throws<AbpValidationException>(() => _myAppService.MyMethod(new MyMethodInput { MyStringValue = "a" })); //MyStringValue's min length should be 3!
|
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Work_With_Right_Nesned_Inputs() |
|||
{ |
|||
var output = _myAppService.MyMethod2(new MyMethod2Input |
|||
{ |
|||
MyStringValue2 = "test 1", |
|||
Input1 = new MyMethodInput { MyStringValue = "test 2" }, |
|||
DateTimeValue = DateTime.Now |
|||
}); |
|||
output.Result.ShouldBe(42); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Work_With_Wrong_Nesned_Inputs_1() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => |
|||
_myAppService.MyMethod2(new MyMethod2Input |
|||
{ |
|||
MyStringValue2 = "test 1", |
|||
Input1 = new MyMethodInput() //MyStringValue is not set
|
|||
})); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Work_With_Wrong_Nesned_Inputs_2() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => |
|||
_myAppService.MyMethod2(new MyMethod2Input //Input1 is not set
|
|||
{ |
|||
MyStringValue2 = "test 1" |
|||
})); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Work_With_Wrong_List_Input_1() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => |
|||
_myAppService.MyMethod3( |
|||
new MyMethod3Input |
|||
{ |
|||
MyStringValue2 = "test 1", |
|||
ListItems = new List<MyClassInList> |
|||
{ |
|||
new MyClassInList {ValueInList = null} |
|||
} |
|||
})); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Work_With_Wrong_Array_Input_1() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => |
|||
_myAppService.MyMethod3( |
|||
new MyMethod3Input |
|||
{ |
|||
MyStringValue2 = "test 1", |
|||
ArrayItems = new[] |
|||
{ |
|||
new MyClassInList {ValueInList = null} |
|||
} |
|||
})); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Work_If_Array_Is_Null() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => |
|||
_myAppService.MyMethod4(new MyMethod4Input()) //ArrayItems is null!
|
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Work_If_Array_Is_Null_But_DisabledValidation_For_Method() |
|||
{ |
|||
_myAppService.MyMethod4_2(new MyMethod4Input()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Work_If_Array_Is_Null_But_DisabledValidation_For_Property() |
|||
{ |
|||
_myAppService.MyMethod5(new MyMethod5Input()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Use_IValidatableObject() |
|||
{ |
|||
Assert.Throws<AbpValidationException>(() => |
|||
{ |
|||
_myAppService.MyMethod6(new MyMethod6Input |
|||
{ |
|||
MyStringValue = "test value" //MyIntValue has not set!
|
|||
}); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Normalize_Nested_Dtos() |
|||
{ |
|||
var input = new MyMethod7Input |
|||
{ |
|||
Inner = new MyMethod7Input.MyMethod7InputInner |
|||
{ |
|||
Value = 10 |
|||
} |
|||
}; |
|||
|
|||
_myAppService.MyMethod7(input); |
|||
|
|||
input.Inner.Value.ShouldBe(12); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Stop_Recursive_Validation_In_A_Constant_Depth() |
|||
{ |
|||
_myAppService.MyMethod8(new MyClassWithRecursiveReference { Value = "42" }).Result.ShouldBe(42); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Allow_Null_For_Nullable_Enums() |
|||
{ |
|||
_myAppService.MyMethodWithNullableEnum(null); |
|||
} |
|||
|
|||
[DependsOn(typeof(AbpAutofacModule))] |
|||
public class ValidationTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddType<MyAppService>(); |
|||
} |
|||
} |
|||
|
|||
public interface IMyAppService : IApplicationService |
|||
{ |
|||
MyMethodOutput MyMethod(MyMethodInput input); |
|||
MyMethodOutput MyMethod2(MyMethod2Input input); |
|||
MyMethodOutput MyMethod3(MyMethod3Input input); |
|||
MyMethodOutput MyMethod4(MyMethod4Input input); |
|||
MyMethodOutput MyMethod4_2(MyMethod4Input input); |
|||
MyMethodOutput MyMethod5(MyMethod5Input input); |
|||
MyMethodOutput MyMethod6(MyMethod6Input input); |
|||
MyMethodOutput MyMethod7(MyMethod7Input input); |
|||
MyMethodOutput MyMethod8(MyClassWithRecursiveReference input); |
|||
void MyMethodWithNullableEnum(MyEnum? value); |
|||
} |
|||
|
|||
public class MyAppService : ApplicationService, IMyAppService |
|||
{ |
|||
public MyMethodOutput MyMethod(MyMethodInput input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod2(MyMethod2Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod3(MyMethod3Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod4(MyMethod4Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
[DisableValidation] |
|||
public MyMethodOutput MyMethod4_2(MyMethod4Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod5(MyMethod5Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod6(MyMethod6Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod7(MyMethod7Input input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public MyMethodOutput MyMethod8(MyClassWithRecursiveReference input) |
|||
{ |
|||
return new MyMethodOutput { Result = 42 }; |
|||
} |
|||
|
|||
public void MyMethodWithNullableEnum(MyEnum? value) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
public class MyMethodInput |
|||
{ |
|||
[Required] |
|||
[MinLength(3)] |
|||
public string MyStringValue { get; set; } |
|||
} |
|||
|
|||
public class MyMethod2Input |
|||
{ |
|||
[Required] |
|||
[MinLength(2)] |
|||
public string MyStringValue2 { get; set; } |
|||
|
|||
public DateTime DateTimeValue { get; set; } |
|||
|
|||
[Required] |
|||
public MyMethodInput Input1 { get; set; } |
|||
} |
|||
|
|||
public class MyMethod3Input |
|||
{ |
|||
[Required] |
|||
[MinLength(2)] |
|||
public string MyStringValue2 { get; set; } |
|||
|
|||
public List<MyClassInList> ListItems { get; set; } |
|||
|
|||
public MyClassInList[] ArrayItems { get; set; } |
|||
} |
|||
|
|||
public class MyMethod4Input |
|||
{ |
|||
[Required] |
|||
public MyClassInList[] ArrayItems { get; set; } |
|||
} |
|||
|
|||
public class MyMethod5Input |
|||
{ |
|||
[DisableValidation] |
|||
public MyClassInList[] ArrayItems { get; set; } |
|||
} |
|||
|
|||
public class MyMethod6Input : IValidatableObject |
|||
{ |
|||
[Required] |
|||
[MinLength(2)] |
|||
public string MyStringValue { get; set; } |
|||
|
|||
public int MyIntValue { get; set; } |
|||
|
|||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) |
|||
{ |
|||
if (MyIntValue < 18) |
|||
{ |
|||
yield return new ValidationResult("MyIntValue must be greather than or equal to 18"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class MyMethod7Input : IShouldNormalize |
|||
{ |
|||
public MyMethod7InputInner Inner { get; set; } |
|||
|
|||
public void Normalize() |
|||
{ |
|||
Inner.Value++; |
|||
} |
|||
|
|||
public class MyMethod7InputInner : IShouldNormalize |
|||
{ |
|||
public int Value { get; set; } |
|||
|
|||
public void Normalize() |
|||
{ |
|||
Value++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class MyClassInList |
|||
{ |
|||
[Required] |
|||
[MinLength(3)] |
|||
public string ValueInList { get; set; } |
|||
} |
|||
|
|||
public class MyMethodOutput |
|||
{ |
|||
public int Result { get; set; } |
|||
} |
|||
|
|||
public class MyClassWithRecursiveReference |
|||
{ |
|||
public MyClassWithRecursiveReference Reference { get; } |
|||
|
|||
[Required] |
|||
public string Value { get; set; } |
|||
|
|||
public MyClassWithRecursiveReference() |
|||
{ |
|||
Reference = this; |
|||
} |
|||
} |
|||
|
|||
public enum MyEnum |
|||
{ |
|||
Value1, |
|||
Value2 |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue