I have a the following operation:
public void Save (Customer c, IEnumerable <Product> products)
{
// Validate that you have entered at least one product.
if (!produtos.Any())
throw new ArgumentOutOfRangeException("products");
}
Inline, without using inheritance (eg AbstractValidator ), as would this same operation using the FluentValidation library?
This is not supported yet:
public void DoOperation(List<string> strings)
{
var validator = new InlineValidator<List<string>>();
validator.RuleFor(l => l).Must(l => l.Any()).WithMessage("No one");
validator.ValidateAndThrow(strings)
}
On this case, we have to throw ValidationException manually.
like:
public void DoOperation(List<string> strings)
{
if (!strings.Any())
{
var failures = new List<ValidationFailure>();
failures.Add(new ValidationFailure("strings", "Must have at less one."));
throw new ValidationException(failures);
}
}
See:
https://fluentvalidation.codeplex.com/discussions/579227
var validator = new InlineValidator<Person>();
validator.RuleSet("SomeRuleset", ()=>{
validator.RuleFor(x=>x.Name)...etc
});
https://github.com/FluentValidation/FluentValidation/issues/563
I think this kind of validation is impossible, if you had an object which had a property of type IEnumerable<Product> you could using FluentValidation to check if the object has at least one product.
for example
public class ProductList
{
IEnumerable<Product> Products {get;set;}
var Validator = new ProductListValidator();
public bool IsValid
{
get
{
var res = Validator.Validate(this);
return res.IsValid;
}
}
public IList<ValidationFailure> ValidationResult
{
get
{
var res = Validator.Validate(this);
return res.Errors;
}
}
}
public class ProductListValidator : AbstractValidator<ProductList>
{
public ProductListValidator()
{
RuleFor(i => i.Products).Must(i => i.HasAny()).WithMessage("Your Error Meesage");
}
}
then
public void Save (Customer c, ProductList products)
{
// Validate that you have entered at least one product.
if (!ProductList.IsValid)
{
ReturnErrorSummary(ProductList.ValidationResult);
}
}
Related
I have 2 errors because of my little knowledge of PostSharp (last version). My project's three aspect classes, but I only get 2 errors. When the getall method runs in my mvc project, I want log information to be generated in my database and C:/Log/Log.txt. But no logs are created. Here is what I want from you. No matter how I write the code block, my problem solves? I have some validation and Transaction processes, but I don't think it has anything to do with the error I'm getting, so there's no need for details. Firstly, I've gotten the following warning.
enter image description here
To solve this, I followed the procedure below. LogAspect and FluentValidationAspect classes have been giving error.
[LogAspect(AspectPriority = 1), FluentValidationAspect(AspectPriority = 2), TransactionScopeAspect(AspectPriority = 3)]
And again, I got the following error. (CS 7036)
enter image description here
I want to do some operations in my BookManager class. (Log, Validation, Transaction). Here are the codes;
[LogAspect(AspectPriority = 1), FluentValidationAspect(AspectPriority = 2), TransactionScopeAspect(AspectPriority = 3)]
public class BookManager : IBookService
{
private IBookDal _bookDal;
private IPersonDal _personDal;
/* private readonly IQueryableRepository<Book> _queryable; */
public BookManager(IBookDal bookDal, IPersonDal personDal/*IQueryableRepository<Book> queryable */)
{
_personDal = personDal;
/*_queryable = queryable; */
_bookDal = bookDal;
}
[FluentValidationAspect(typeof(BookValidator))]
public Book Add(Book book)
{
return _bookDal.Add(book);
}
public void Delete(Book book)
{
_bookDal.Delete(book);
}
[LogAspect(typeof(DatabaseLogger))]
public List<Book> GetAll()
{
return _bookDal.GetList();
}
[TransactionScopeAspect]
public void TransactionalOperation(Person person, Book book)
{
_personDal.Delete(person);
// Business Codes
_bookDal.Add(book);
}
public Book GetById(int bookId)
{
return _bookDal.Get(p=>p.BookId==bookId);
}
[FluentValidationAspect(typeof(BookValidator))]
public Book Update(Book book)
{
return _bookDal.Update(book);
}
}
[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Instance)]
public class LogAspect : OnMethodBoundaryAspect
{
private Type _loggerType;
[NonSerialized]
private LoggerService _loggerService;
public LogAspect(Type loggerType)
{
_loggerType = loggerType;
}
public override void RuntimeInitialize(MethodBase method)
{
if (_loggerType.BaseType != typeof(LoggerService))
{
throw new Exception("Wrong logger type.");
}
_loggerService = (LoggerService)Activator.CreateInstance(_loggerType);
base.RuntimeInitialize(method);
}
public override void OnEntry(MethodExecutionArgs args)
{
if (!_loggerService.IsInfoEnabled)
{
return;
}
try
{
var logParameters = args.Method.GetParameters().Select((t, i) => new LogParameter
{
Name = t.Name,
Type = t.ParameterType.Name,
Value = args.Arguments.GetArgument(i)
}).ToList();
var logDetail = new LogDetail
{
FullName = args.Method.DeclaringType == null ? null : args.Method.DeclaringType.Name,
MethodName = args.Method.Name,
Parameters = logParameters
};
_loggerService.Info(logDetail);
}
catch (Exception)
{
}
}
}
[Serializable]
public class FluentValidationAspect : OnMethodBoundaryAspect
{
Type _validatorType;
public FluentValidationAspect(Type validatorType)
{
_validatorType = validatorType;
}
public override void OnEntry(MethodExecutionArgs args)
{
var validator = (IValidator)Activator.CreateInstance(_validatorType);
var entityType = _validatorType.BaseType.GetGenericArguments()[0];
var entities = args.Arguments.Where(t => t.GetType() == entityType);
foreach (var entity in entities)
{
ValidatorTool.FluentValidate(validator, entity);
}
}
}
I want to tell you something that you have to consider. Also, I did assembly level logging. This is the code.
[assembly: LogAspect(typeof(JsonFileLogger), AttributeTargetTypes = "ELibrary.Library.Business.Managers.BookManager*")]
Finally I want to add, can you explain the solution of this error by rewriting the wrong block?
Ordering aspects is documented here. You are getting the warning because PostSharp does not have information on the ordering of some transformations. In your case I'd add the following attribute on LogAspect:
[AspectTypeDependency(AspectDependencyAction.Order, AspectDependencyPosition.Before, typeof(FluentValidationAspect))]
And then on FluentValidationAspect:
[AspectTypeDependency(AspectDependencyAction.Order, AspectDependencyPosition.Before, typeof(TransactionScopeAspect))]
This should totally order those three aspects and you should get rid of warnings.
Afterwards, you were getting C# errors because your aspect constructors simply have a parameter that you did not specify. AspectPriority needs to be specified on individual attributes.
I have a property on a class which is implemented by an interface. Now I want to get all attributes from a specific type declared on that property and their interface Pendants.
In order to regard multi implementation with implicit and explicit members I wrote an test-class (with xUnit).
[DebuggerDisplay("{Tooltip}")]
[AttributeUsage(AttributeTargets.Property)]
public class TooltipAttribute : Attribute
{
public TooltipAttribute(string tooltip)
{
Tooltip = tooltip;
}
public string Tooltip { get; set; }
}
public interface IAmGood
{
[Tooltip("GOOD: I am a very generic description.")]
int Length { get; }
}
public interface IAmBad
{
[Tooltip("BAD: This description is not wanted to be shown.")]
int Length { get; }
}
public class DemoClassImplicit : IAmGood, IAmBad
{
[Tooltip("GOOD: Implicit")]
public int Length => throw new NotImplementedException();
[Tooltip("BAD: Explicit")]
int IAmBad.Length => throw new NotImplementedException();
}
public class DemoClassExplicit : IAmGood, IAmBad
{
[Tooltip("GOOD: Explicit")]
int IAmGood.Length => throw new NotImplementedException();
[Tooltip("BAD: Implicit")]
public int Length => throw new NotImplementedException();
}
public class DemoClassImplicitForBoth : IAmGood, IAmBad
{
[Tooltip("I am GOOD and BAD")]
public int Length => throw new NotImplementedException();
}
public class TestClass
{
[Fact]
public void GetTooltipFromImplicit()
{
var demoClassImplicit = new DemoClassImplicit();
var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperty("Length");
var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);
Assert.Equal(2, tooltips.Count());
Assert.All(tooltips, o => Assert.Contains("GOOD", o.Tooltip));
}
[Fact]
public void GetTooltipFromExplicit()
{
var demoClassImplicit = new DemoClassExplicit();
var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperties().First(o => o.Name.EndsWith(".Length"));
var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);
Assert.Equal(2, tooltips.Count());
Assert.All(tooltips, o => Assert.Contains("GOOD", o.Tooltip));
}
[Fact]
public void GetTooltipFromImplicitForBoth()
{
var demoClassImplicit = new DemoClassImplicitForBoth();
var propertyInfo = demoClassImplicit.GetType().GetRuntimeProperty("Length");
var tooltips = GetTooltipAttribute<TooltipAttribute>(propertyInfo);
Assert.Equal(3, tooltips.Count());
}
/// <summary>
/// The core method.
/// </summary>
public IEnumerable<T_Attribute> GetTooltipAttribute<T_Attribute>(PropertyInfo propInfo)
where T_Attribute : Attribute
{
var result = new List<T_Attribute>(propInfo.GetCustomAttributes<T_Attribute>());
var declaringType = propInfo.DeclaringType;
// The get method is required for comparing without use the prop name.
var getMethodFromGivenProp = propInfo.GetGetMethod(true);
// Check for each interface if the given property is declared there
// (it is not a naming check!).
foreach (var interfaceType in declaringType.GetInterfaces())
{
var map = declaringType.GetInterfaceMap(interfaceType);
// Check if the current interface has an member for given props get method.
// Attend that compare by naming would be cause an invalid result here!
var targetMethod = map.TargetMethods.FirstOrDefault(o => o.Equals(getMethodFromGivenProp));
if (targetMethod != null)
{
// Get the equivalent get method on interface side.
// ERROR: The error line!
var interfaceMethod = map.InterfaceMethods.FirstOrDefault(o => o.Name == targetMethod.Name);
if (interfaceMethod != null)
{
// The get method does not help to get the attribute so the property is required.
// In order to get the property we must look which one has the found get method.
var property = interfaceType.GetProperties().FirstOrDefault(o => o.GetGetMethod() == interfaceMethod);
if (property != null)
{
var attributes = property.GetCustomAttributes<T_Attribute>();
if (attributes != null)
{
result.AddRange(attributes);
}
}
}
}
}
return result;
}
}
The test method 'GetTooltipFromExplicit' fails because in the core method is a comparison by name. I marked the line above with // ERROR: The error line!.
I have no idea how to find the method-pendant inside of 'InterfaceMapping'-class.
The solution was to know that the order of the two collections in InterfaceMapping are mirrorred.
So replace the line below is the solution:
// ERROR: The error line!
var interfaceMethod = map.InterfaceMethods.FirstOrDefault(o => o.Name == targetMethod.Name);
// SOLUTION: The working line:
var interfaceMethod = map.InterfaceMethods[Array.IndexOf(map.TargetMethods, targetMethod)];
This detailed was explained on official member documentation (but not on the class itself). See:
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.interfacemapping.interfacemethods?view=netcore-3.1#remarks
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.interfacemapping.targetmethods?view=netcore-3.1#remarks
Context:
I consume a ERP WebService exposing N methods like:
FunctionNameResponse FunctionName(FunctionNameQuery query)
I made a functional wrapper in order to:
Get rid off wrapper object FunctionNameResponse and FunctionNameQuery, that every method has.
One instance of the WebService for all the program.
Investigate and log error in the wrapper.
Investigate Slow running and Soap envelope with IClientMessageInspector
Duplicated code:
For each of the methods of the WebService I end up with around thirty lines of code with only 3 distinct words. Type response, type query, method name.
public FooResponse Foo(FooQuery query)
{
// CheckWebServiceState();
FooResponse result = null;
try
{
result =
WSClient
.Foo(query)
.response;
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
I would like to reduce those repetition. In order to :
Make it easier to add a Method;
Avoid copy pasting programming with no need to understand what you are doing.
Easier to add specific catch and new test without the need to copy past in every method.
The following code work and exist only in the imaginary realm. It's a not functional sketch of my solution using my limited understanding.
public class Demo
{
public enum WS_Method
{
Foo,Bar,FooBar
}
public class temp
{
public Type Query { get; set; }
public Type Response { get; set; }
public WS_Method MethodName { get; set; }
}
public static IEnumerable<temp> TestFunctions =>
new List<temp>
{
new temp{Query=typeof(FooQuery), Response=typeof(FooResponse), MethodName=WS_Method.Foo },
new temp{Query=typeof(BarQuery), Response=typeof(BarResponse), MethodName=WS_Method.Bar },
new temp{Query=typeof(FooBarQuery), Response=typeof(FooBarResponse), MethodName=WS_Method.FooBar },
};
public static void Run()
{ // Exemple of consuming the method
var input = new BarQuery { Bar_Label = "user input", Bar_Ig = 42 };
BarResponse result = Execute<BarQuery, BarResponse>(input);
}
public static T2 Execute<T1,T2>(T1 param) {
//Get temp line where Query type match Param Type.
var temp = TestFunctions.Single(x => x.Query == typeof(T1));
var method = typeof(DemoWrapper).GetMethod(temp.MethodName.ToString(), new Type[] { typeof(T1) });
var wsClient = new DemoWrapper();
T2 result = default(T2);
try
{
result =
method
.Invoke(wsClient, new object[] { param })
.response;
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
}
I know the reflection is heavy and perhaps it's not the right way to achieve this refactoring. So the question is:
How do I refactor those function?
attachment : Live demo https://dotnetfiddle.net/aUfqNp.
In this scenario:
You have a larger block of code which is mostly repeated
The only difference is a smaller unit of code that's called inside the larger block
You can refactor this by passing the smaller unit of code as a Func or Action as a parameter to the larger function.
In that case your larger function looks like this:
public TResponse GetResponse<TResponse>(Func<TResponse> responseFunction)
{
var result = default(TResponse);
try
{
result = responseFunction();
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
The individual functions which call it look like this, without all the repeated code:
public FooResponse Foo(FooQuery query)
{
return GetResponse(() => WSClient.Foo(query));
}
Here's another approach where you keep the methods but have them all call a method that handles the duplication.
public class Demo
{
private _wsClient = new DemoWrapper();
public static void Run()
{ // Exemple of consuming the method
var input = new BarQuery { Bar_Label = "user input", Bar_Ig = 42 };
BarResponse result = Bar(input);
}
public FooResponse Foo(FooQuery foo) =>
Execute(foo, query => _wsClient.Foo(query));
public BarResponse Bar(BarQuery bar) =>
Execute(bar, query => _wsClient.Bar(query));
public FooBarResponse FooBar(FooBarQuery fooBar) =>
Execute(fooBar, query => _wsClient.FooBar(query));
private static TResponse Execute<TQuery ,TResponse>(
TQuery param, Func<TQuery, TResponse> getResponse)
{
//Get temp line where Query type match Param Type.
var result = default(TResponse);
try
{
result = getResponse(query);
}
catch (Exception e)
{
// SimpleTrace();
// SoapEnvelopeInterceptorTrace();
// TimeWatch_PerformanceIEndpointBehaviorTrace();
}
return result;
}
}
I have an MVC application that has many AbstractValidator<T> per view model. This is by design because I use SimpleInjector to inject queries to hit our database and I don't want to have constructor over injection in one validator. I created the class below that is the validator for every view model, but it does not call my When and other custom validation rules, only the simple rules are called. Any help will be appreciated. Thank you.
public class CompositeValidator<T> : AbstractValidator<T>
{
private ICollection<IValidator> _validators = new List<IValidator>();
public CompositeValidator(IEnumerable<IValidator<T>> validators = null)
{
if (validators == null) return;
foreach (var validator in validators)
{
_validators.Add(validator);
var enumerator = validator.GetEnumerator();
while (enumerator.MoveNext())
{
AddRule(enumerator.Current);
}
}
}
public override ValidationResult Validate(ValidationContext<T> context)
{
var errorsFromOtherValidators = _validators.SelectMany(x => x.Validate(context).Errors);
return new ValidationResult(errorsFromOtherValidators );
}
}
Here is an example of two validators
public class PersonValidator : AbstractValidator<PersonVm>
{
public PersonValidator()
{
RuleFor(model => model.FirstName).NotEmpty();
RuleFor(model => model.LastName).NotEmpty();
}
}
public class PersonMustBeRegisteredValidator : AbstractValidator<PersonVm>
{
private readonly IQuery<PersonExists> _query;
public PersonMustBeRegisteredValidator(IQuery<ReturnPerson> query)
{
_query = query;
Custom(model =>
{
var person = _query.Select(new { model.Id });
if (person == null) return new ValidationFailure("Id", "Person does not exist");
return null;
});
}
}}
this actually works. I found a validation interceptor I forgot about that was not firing my validation.
In lua, one can very easily create a table and add functions to it.
How do I do that in C#? My first idea was to use a List of delegates and fill the list with anonymous functions, but I can't get the syntax right.
public class PlanItem
{
public string Name { get; set; }
public List<BusinessRule> Rules;
}
public delegate bool BusinessRule(PlanItem item);
[Test]
public void TestIt()
{
PlanItem item = new PlanItem();
item.Rules.Add(
BusinessRule(PlanItem item)
{
return !string.IsNullOrEmpty(item.Name);
}
);
foreach(BusinessRule rule in item.Rules)
{
if(!rule(item))
// write uh-oh
}
}
This should do the trick:
item.Rules.Add((planItem) =>
{
return !string.IsNullOrEmpty(planItem.Name);
});
public void TestIt()
{
PlanItem item = new PlanItem();
item.Rules.Add( (item) => { !string.IsNullOrEmpty(item.Name); });
item.Rules.ForEach( (rule) => { if(!rule(item) uhOh(item, outputStream);} );
}
In C# 2.0 without anonymous delegates:
BusinessRule rule = delegate(PlanItem planitem) {
return !string.IsNullOrEmpty(planitem.Name);
};
item.Rules.Add(rule);