How check other property use custom rule in fluent validation - c#

Let's say there is a class and I need to check the value of property 1 against the value of property 2 so that it can be reused. I don't understand how you can pass the value of 2 properties to a custom validation rule of 1 property using fluent validation
I have class
public class MyClass
{
public string Property1 { get; set; }
public string Property2 {get; set; }
public string Property3 { get; set; }
}
and validato class
public class MyClassValidator : AbstractValidator<MyClass>
{
public MyClassValidator ()
{
RuleFor(мyClass => мyClass.Property1).Property1();
}
}
and custom rule
public static IRuleBuilderOptions<T, string>
Property1<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.NotNull()
.NotEmpty()
.Length(12)
}
I want the rule
public static IRuleBuilderOptions<T, string>
Property1<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.Custom((x, c) => {
if(x.Property2== "qwerty")
if(x.Property1=="q")
context.AddFailure("")
});
}
How can i pass property value of Property2 to Property1 property check?
Edit1
I want this check to be used on other classes
I just want to thank you for your reply.
So I decided not only to look at the answers, but also to ask my first question

You need a custom validator. To quote an example from the docs...
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Pets).Custom((list, context) => {
if(list.Count > 10) {
context.AddFailure("The list must contain 10 items or fewer");
}
});
}
}
The context gives you access to the whole object being validated, so you can get at any other properties you want.

Related

Fluent Validation, different validation for each item in a list in Asp.NET Core

I´ve been trying to find a way to validate items inside a list, each with different validation rules. I came upon Fluent validation which is a great library but I can´t seem to find a way to do validation for each item individually. I got a faint idea from this similar thread (Validate 2 list using fluent validation), but I´m not sure how to focus it how I want.
So I have this View Model:
public class EditPersonalInfoViewModel
{
public IList<Property> UserPropertyList { get; set; }
}
This contains a list of Active Directory properties. Each represented by this class:
public class Property
{
public string Name { get; set; }
public UserProperties Value { get; set; }
public string input { get; set; }
public bool Unmodifiable { get; set; }
public string Type { get; set; }
}
The point is that, each AD property has different constraints so I want to specify different rules for each property in the list in some way like this:
public class ADPropertiesValidator : AbstractValidator<EditPersonalInfoViewModel>
{
public ADPropertiesValidator()
{
RuleFor(p => p.UserPropetyList).Must((p,n) =>
{
for (int i = 0; i < n.Count; i++)
{
if ((n[i].Name.Equals("sAMAccountName"))
{
RuleFor(n.input ).NotEmpty()....
}
else if(...)
{
//More Rules
}
}
)
}
}
Any ideas how to approach this? thanks in advance.
You are approaching the validation from the wrong perspective. Instead of creating validation conditions inside your collection container class, just create another validator specific for your Property class, and then use that inside your ADPropertiesValidator:
public class ADPropertyValidator : AbstractValidator<Property>
{
public ADPropertyValidator()
{
When(p => p.Name.Equals("sAMAccountName"), () =>
{
RuleFor(p => p.input)
.NotEmpty()
.MyOtherValidationRule();
});
When(p => p.Name.Equals("anotherName"), () =>
{
RuleFor(p => p.input)
.NotEmpty()
.HereItIsAnotherValidationRule();
});
}
}
public class ADPropertiesValidator : AbstractValidator<EditPersonalInfoViewModel>
{
public ADPropertiesValidator()
{
RuleForEach(vm => vm.UserPropertyList)
.SetValidator(new ADPropertyValidator());
}
}

MongoDB - Class Map not applying

I have a class called Entity, it has an Id property which is of type Guid. Now by default MongoDb serializes this as binary which is not what I want.
I cannot for the life of me get the Id property to serialize as a string.
I have tried both a ConventionPack and a ClassMap.
Entity Definition:
public class Entity : Dictionary<string, object>
{
public Guid Id
{
get { return this.ContainsKey(nameof(Id)) ? Guid.Parse(this[nameof(Id)].ToString()) : Guid.Empty; }
set { this[nameof(Id)] = value; }
}
public string EntityName
{
get { return this.ContainsKey(nameof(EntityName)) ? this[nameof(EntityName)].ToString() : ""; }
set { this[nameof(EntityName)] = value; }
}
}
Class Map Definition:
public class EntityClassMap: BsonClassMap<Entity>
{
public EntityClassMap()
{
AutoMap();
GetMemberMap(x => x.Id).SetSerializer(new GuidSerializer(BsonType.String));
}
}
Class Map registration:
BsonClassMap.RegisterClassMap(new EntityClassMap());
Convention
public class GuidAsStringRepresentationConvention : ConventionBase, IMemberMapConvention
{
public void Apply (BsonMemberMap memberMap)
{
if (memberMap.MemberType == typeof(Guid))
{
memberMap.SetSerializer(new GuidSerializer(BsonType.String));
}
}
}
Convention Pack definition and registration:
var pack = new ConventionPack() {new GuidAsStringRepresentationConvention()};
ConventionRegistry.Register("GuidAsString", pack, it => true);
At a complete loss as to what I am doing wrong.
OK so apparently none of the class map or convention code was wrong the issue was that the Entity object inherited from Dictionary<string, object>
When I stopped inheriting from Dictionary<string, object> it all started working.
What I was trying to achieve was essentially allow both the statically defined properties and any extra properties to be saved.
It turns out, Mongo will automatically map so called "Extra Elements" to a property that implements BsonDocument or IDictionary<string, object> if it uses the convention of being called ExtraElements or is configured in a mapping with MapExtraElementsMember(c => c.Fields);
It also tidies up my entity class which can now use auto properties:
public class Entity
{
public Guid Id { get; set; }
public string EntityName { get; set; }
public Dictionary<string, object> Fields { get; set; }
}
Hope this helps someone as it has taken me about a day and a half to figure this out!

Implement interface and use code from existing implementation of the interface

I'm trying to implement the ITableEntity interface so that I can add [DataContract] attribute on it. But if I implement this interface myself, I'll have to give the ReadEntity and WriteEntity methods a body.
But there is a class that already implements the ITableEntity interface and gave ReadEntity and WriteEntity methods a body, which is the TableEntity.cs.
How can I make my implementation of the interface use the methods in the TableEntity class?
[Edit]
[DataContract]
public class SerializableTableEntity : ITableEntity
{
private TableEntity tableEntity;
public string ETag { get; set; }
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public DateTimeOffset Timestamp { get; set; }
public SerializableTableEntity()
{
tableEntity = new TableEntity();
}
public void ReadEntity(IDictionary<string, EntityProperty> properties, Microsoft.WindowsAzure.Storage.OperationContext operationContext)
{
tableEntity.ReadEntity(properties, operationContext);
}
public IDictionary<string, EntityProperty> WriteEntity(Microsoft.WindowsAzure.Storage.OperationContext operationContext)
{
return tableEntity.WriteEntity(operationContext);
}
}
The reason that every property in your stored table is blank is because WriteEntity and ReadEntity use the blank object to store and write the data.
You're delegating serialization of your object to 'tableEntity' but none of your properties are there.
Suggestion: you will need to implement all of your SerializableTableEntity's properties inside a class that derives from TableEntity, contain a variable of that type inside the SerializableTableEntity entity, and delegate every member's property get/set from SerializableTableEntity to this new object.
Does this make sense?
EDIT: Code sample as requested (you're not going to enjoy it though)
[DataContract]
public class SerializableTableEntity : ITableEntity
{
private CustomEntity tableEntity;
public string ETag {
{
get
{
return tableEntity.ETag;
}
set
{
tableEntity.Etag = value;
}
}
public string PartitionKey
{
get
{
return tableEntity.PartitionKey;
}
set
{
tableEntity.PartitionKey = value;
}
}
public string RowKey
{
get
{
return tableEntity.RowKey;
}
set
{
tableEntity.RowKey = value;
}
}
public DateTimeOffset Timestamp
{
get
{
return tableEntity.Timestamp;
}
set
{
tableEntity.Timestamp = value;
}
}
public string PropertyOne
{
get
{
return tableEntity.PropertyOne;
}
set
{
tableEntity.PropertyOne = value;
}
}
public SerializableTableEntity()
{
tableEntity = new CustomEntity();
}
public void ReadEntity(IDictionary<string, EntityProperty> properties, Microsoft.WindowsAzure.Storage.OperationContext operationContext)
{
tableEntity.ReadEntity(properties, operationContext);
}
public IDictionary<string, EntityProperty> WriteEntity(Microsoft.WindowsAzure.Storage.OperationContext operationContext)
{
return tableEntity.WriteEntity(operationContext);
}
}
public class CustomEntity : TableEntity
{
public string PropertyOne { get; set; }
}
I ended up creating exact copy of these classes and made them Serializable. But being able to do some complex queries seems to be a challenge as well. So we moved to SQL Database.
Either delegate the "uninteresting" methods (a more realistic example is here):
class YourClass : Interface {
public void ReadEntity()
{
delegateTo.ReadEntity();
}
TableEntity delegateTo = new TableEntity();
}
or just throw an exception inside them (like NotImplementedException) - the latter will only work for you if those methods are not called.
You can create a class that contains the implementation of the TableEntity class, but also adds the functionality that you want. This is similar to the Decorator Pattern.
[Attributes...]
public class MyTableEntity : ITableEntity {
private TableEntity decoratedTableEntity;
public void ReadEntity(args...) {
decoratedTableEntity.ReadEntity(args...);
}
}
To make the solution more generic, change decoratedTableEntity to be an ITableEntity.

Dynamic RegularExpression attribute

I have this form where there's a Postal Code field, in my ViewModel it looks something like this:
[RegularExpression(#"^\d{5}(-\d{4})?$")]
public string PostalCode { get; set; }
That regular expression accepts 5 digits postal codes, but now I need to support other countries where they use 8, 4 or 6 digits postal codes.
I have those custom regex in a database, but I can't pass non-static variables to an attribute in this way:
[RegularExpression(MyCustomRegex)]
public string PostalCode { get; set; }
What can I do? I tried creating a custom attribute, but in some point I needed to pass a non-static parameter, which is not possible.
Should I use reflection? Is there a cleaner way?
A better way may be to decouple the attribute from the regex.
public class PostalCodeAttribute : Attribute
{
public string Country { get; set; }
}
public interface IPostalCodeModel
{
string PostalCode { get; }
}
public class UsModel : IPostalCodeModel
{
[PostalCode(Country = "en-US")]
public string PostalCode { get; set; }
}
public class GbModel : IPostalCodeModel
{
[PostalCode(Country = "en-GB")]
public string PostalCode { get; set; }
}
Validator:
public class PostalCodeValidator
{
private readonly IRegularExpressionService _regularExpressionService;
public PostalCodeValidator(IRegularExpressionService regularExpressionService)
{
_regularExpressionService = regularExpressionService;
}
public bool IsValid(IPostalCodeModel model)
{
var postalCodeProperty = model.GetType().GetProperty("PostalCode");
var attribute = postalCodeProperty.GetCustomAttribute(typeof(PostalCodeAttribute)) as PostalCodeAttribute;
// Model doesn't implement PostalCodeAttribute
if(attribute == null) return true;
return ValidatePostalCode(_regularExpressionService, model, attribute.Country);
}
private static bool ValidatePostalCode(
IRegularExpressionService regularExpressionService,
IPostalCodeModel model,
string country
)
{
var regex = regularExpressionService.GetPostalCodeRegex(country);
return Regex.IsMatch(model.PostalCode, regex);
}
}
As indicated in several related questions (e.g. Pass instance of Class as parameter to Attribute constructor Lambda expression in attribute constructor) only compile time literals are allowed as arguments for an attribute.
I did think of a workaround that may or may not work. The idea is to create a custom attribute class that derives from the regular expression attribute and that performs a regex lookup on construction and passes the result to its base.
DISCLAIMER: I haven't actually tested it (and am not planning on doing so ;-).
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class PostalCodeAttribute : RegularExpressionAttribute
{
private static ConcurrentDictionary<string, Func<string, string>> _resolverDict = new ConcurrentDictionary<string, Func<string, string>>();
private static string Resolve(string source)
{
Func<string, string> resolver = null;
if (!_resolverDict.TryGetValue(source, out resolver))
throw new InvalidOperationException(string.Format("No resolver for {0}", source));
return resolver(source);
}
public static void RegisterResolver(string source, Func<string, string> resolver)
{
_resolverDict.AddOrUpdate(source, resolver, (s, c) => resolver);
}
static PostalCodeAttribute()
{
// necessary to enable client side validation
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PostalCodeAttribute), typeof(RegularExpressionAttributeAdapter));
}
public PostalCodeAttribute(string patternSource)
: base(Resolve(patternSource))
{
}
}
/// ...
public void SomeIntializer()
{
PostalCodeAttribute.RegisterResolver("db_source", (s) => PostalCodeRegularExpressions.LookupFromDatabase());
}
public class SomeClassWithDataValidation
{
[PostalCode("db_source")]
public string PostalCode { get; set; }
}
Note that this will only work, if registration of a matching resolver function is done before any of these attributes are instantiated.

nhibernate validator usage

Say I have a value object class, FullName, that is used as a property in an Employee entity class. The FullName may have a middle initial, nick name, etc; but from a domain perspective I would like to only enforce that both the FirstName and LastName properties of the FullName are valued.
I want to express this as part of an EmployeeValidator : ValidationDef{Employee} object, as opposed to an attribute.
Do I first need to make a class validator for FullName (ie, FirstAndLAstNameRequired) and then say that the FullName property in Employee is Valid (using some loquacious form of the ValidAttribute)?
As an aside, it seems that this documentation is still the best out there, but it does look dated at three years old. Is there anything newer that I missed?
Cheers,
Berryl
UPDATE
I haven't figured this out yet, but I have found what is likely the best source of NHib Validator info here: http://fabiomaulo.blogspot.com/search/label/Validator
Here is some psuedo code to express the question better too:
/// <summary>A person's name.</summary>
public class FullName
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string MiddleName { get; set; }
public virtual string NickName { get; set; }
}
public class EmployeeValidator : ValidationDef<Employee>
{
public EmployeeValidator()
{
Define(x => x.FullName).FirstAndLastNameRequired(); // how to get here!!
}
}
UPDATE FOR DAVID
public class FullNameValidator : ValidationDef<FullName>
{
public FullNameValidator() {
Define(n => n.FirstName).NotNullable().And.NotEmpty().And.MaxLength(25);
Define(n => n.LastName).NotNullable().And.NotEmpty().And.MaxLength(35);
// not really necessary but cool that you can do this
ValidateInstance
.By(
(name, context) => !name.FirstName.IsNullOrEmptyAfterTrim() && !name.LastName.IsNullOrEmptyAfterTrim())
.WithMessage("Both a First and Last Name are required");
}
}
public class EmployeeValidator : ValidationDef<Employee>
{
public EmployeeValidator()
{
Define(x => x.FullName).IsValid(); // *** doesn't compile !!!
}
}
To get the FullName validated when you validate the employee, I think you'd do something like the following:
public class EmployeeValidator : ValidationDef<Employee>
{
public EmployeeValidator()
{
Define(x => x.FullName).IsValid();
Define(x => x.FullName).NotNullable(); // Not sure if you need this
}
}
Then the FullName Validator would just be something like:
public class FullNameValidator : ValidationDef<FullName>
{
public EmployeeValidator()
{
Define(x => x.FirstName).NotNullable();
Define(x => x.LastName).NotNullable();
}
}
Alternatively I think you could do something like (haven't checked the syntax):
public class EmployeeValidator: ValidationDef<Employee>
{
public EmployeeValidator() {
ValidateInstance.By((employee, context) => {
bool isValid = true;
if (string.IsNullOrEmpty(employee.FullName.FirstName)) {
isValid = false;
context.AddInvalid<Employee, string>(
"Please enter a first name.", c => c.FullName.FirstName);
} // Similar for last name
return isValid;
});
}
}

Categories