The system I'm developing uses FluentValidation (v5.0.0.1).
What I want to do is create several validators that partially validate an object, which I can then combine in other validators depending on what is required at the time.
For example, say my class has name and address. (This can't be split into a separate class like in the examples).
For scenario 1, I want to validate the name only, so I write a name validator class.
For scenario 2, I only want to validate the address, so I write an address validator class.
For scenario 3, I want to validate both the name and the address, so I want to write a new validator class that calls the name validator and then the address validator.
I don't want to repeat the code in different places, which is why I want them separate. I also don't want to use the When operator as there is no way to determine the when from the contents of the object.
I know I can call SetValidator, but how do I write the call?
RuleFor(j=>j).SetValidator(new NameValidator());
RuleFor(j=>j).SetValidator(new AddressValidator());
doesn't work.
I will explain the solution with this example. I'm going to validate this Contact entity:
public class Contact
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
The requirement is validate FirstName and LastName then Address1, Address2, City, PostalCode and have the posibility to reuse our validators in other entities.
Create interfaces to define what an specific entity is.
public interface IAmPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
public interface IHaveAddress
{
string Address1 { get; set; }
string Address2 { get; set; }
string City { get; set; }
string PostalCode { get; set; }
}
Now Contact entity has to implement both interfaces:
public class Contact : IAmPerson, IHaveAddress
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
Then, create the first validator for an IAmPerson entity
public class PersonValidator : AbstractValidator<IAmPerson>
{
public PersonValidator()
{
RuleFor(data => data.FirstName).Length(3, 50).WithMessage("Invalid firstName");
RuleFor(data => data.LastName).Length(3, 50).WithMessage("Invalid LastName");
}
}
The second one for IHaveAddress entity
public class AddressValidator : AbstractValidator<IHaveAddress>
{
public AddressValidator()
{
RuleFor(data => data.Address1).NotNull().NotEmpty().WithMessage("Invalid address1");
RuleFor(data => data.Address2).NotNull().NotEmpty().WithMessage("Invalid address2");
RuleFor(data => data.City).NotNull().NotEmpty().WithMessage("Invalid City");
RuleFor(data => data.PostalCode).NotNull().NotEmpty().WithMessage("Invalid PostalCode");
}
}
Way to use your custom validators
public class ContactValidator: AbstractValidator<Contact>
{
public ContactValidator()
{
RuleFor(contact => contact).SetValidator(new PersonValidator());
RuleFor(contact => contact).SetValidator(new AddressValidator());
}
}
Now you can use your validators to validate person data or address data in any other entity. The unique thing you have to do is implement specific interfaces in the entities you are going to validate.
[UPDATE]
You can increase readability of your code by adding extension methods
public static class ValidatorExtensions
{
public static IRuleBuilderOptions<T, IHaveAddress> MustHaveAValidAddress<T>(this IRuleBuilder<T, IHaveAddress> ruleBuilder)
{
return ruleBuilder.SetValidator(new AddressValidator());
}
public static IRuleBuilderOptions<T, IAmPerson> MustBeAValidPerson<T>(this IRuleBuilder<T, IAmPerson> ruleBuilder)
{
return ruleBuilder.SetValidator(new PersonValidator());
}
}
This is the final result using the extension methods I have just added:
RuleFor(contact => contact).MustBeAValidPerson();
RuleFor(contact => contact).MustHaveAValidAddress();
If you set a validator on the same type as you have, it will only use the last validator set on the type (in your case the AddressValidator). You can create some methods to encapsulate the validation and use Must.
Note that you won't be able to re-use the same error code or error message across these different validations.
public static class Validations
{
public static bool ValidateName(string name)
{
return name != null; //Or any other validation
}
public static bool ValidateAddress(string address)
{
return !string.IsNullOrEmpty(address); //Or any other validation
}
}
Scenario 1
RuleFor(j=>j.Name).Must(Validations.ValidateName);
Scenario 2
RuleFor(j=>j.Address).Must(Validations.ValidateAddress);
Scenario 3
RuleFor(j=>j.Name).Must(Validations.ValidateName);
RuleFor(j=>j.Address).Must(Validations.ValidateAddress);
Related
I'm currently using AutoMapper on an Project running code-first Entity Framework.
Here just simple entities and DTO's:
// DTO Profile
public class CreateOrEditProfileDto
{
public string Code { get; set; }
public string Name { get; set; }
public List<RouteDto> Routes { get; set; }
}
// entity Profile
public class Profile
{
public virtual string Code { get; set; }
public virtual string Name { get; set; }
}
// DTO Route
public class RouteDto
{
public string DriverName { get; set; }
public string DriverSurname { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public int ProfileId { get; set; }
}
//entity Route
public class Route
{
public virtual string DriverName { get; set; }
public virtual string DriverSurname { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual int ProfileId { get; set; }
[ForeignKey("ProfileId")]
public Profile ProfileFk { get; set; }
}
//Mapper
configuration.CreateMap<RouteDto, Route>().ReverseMap();
// configuration.CreateMap<CreateOrEditProfileDto, Profile>().ReverseMap();
// this type of configuration give the error written below
configuration.CreateMap<CreateOrEditProfileDto, Profile>()
.ForMember(dest => dest, opt =>
opt.MapFrom(src => src.Routes.Select(x =>
new Route()
{
ProfileId = x.ProfileId,
DriverName = x.DriverName,
DriverSurname = x.DriverSurname,
Phone = x.Phone,
Email = x.Email,
}
)
)
);
I'm a little bit confusing, I'm trying to map one-to-many relationship between Profile and Route, Route has a foreign key to Profile. A single profile could have more routes. So, I want to create a profile and attach the list of routes, but when I compile the solution, I get this error:
AutoMapper.AutoMapperConfigurationException: 'Custom configuration for members is only supported for top-level individual members on a type.'
Does anyone know the best way to solve this mapping?
Regards
Because List<RouteDto> is mapped to Profile, the type does not match. You need to add a property in Profile.
public class Profile
{
public virtual string Code { get; set; }
public virtual string Name { get; set; }
public List<Route> Routes { get; set; }
}
The mapping attribute dest.Routes need to be specified. Then, the Routes will be automatically mapped.
CreateMap<CreateOrEditProfileDto, EntityProfile>()
.ForMember(dest => dest.Routes, opt =>
opt.MapFrom(src => src.Routes.Select(x=>
new Route()
{
ProfileId = x.ProfileId,
DriverName = x.DriverName,
DriverSurname = x.DriverSurname,
Phone = x.Phone,
Email = x.Email,
}
))
);
I have a project where I have a set of forms:
public class Form
{
public string Id { get; set; }
public string Name { get; set; }
public IList<string> FieldValueIds { get; set; }
public string UserId { get; set; } // the user who completed the form.
public string FormTemplateId { get; set; }
}
Which each "implement" a form template selected at creation of the form.
public class FormTemplate
{
public string Id { get; set; }
public string Name { get; set; }
public IList<string> FieldIds { get; set; }
}
Which defines which fields are present within the form. Each field
public class FormField
{
public string Id { get; set; }
public string Name { get; set; }
public string Caption { get; set; }
public ValueType DataType { get; set; } // Enum specifying the type of data this field accepts.
}
Stores information about the field such as a description and what type it is expecting. Each FormField can be present in multiple FormTemplates with the values for the form being stored as FieldValue objects related to the Form itself.
public class FieldValue
{
public string Id { get; set; }
public string FieldId { get; set; }
public string ValueAsJsonString { get; set; }
}
Other objects include the User Object:
public class User
{
public string Id { get; set; }
public string Username { get; set; }
public string GivenNames { get; set; }
public string Surname { get; set; }
}
I would like to be able to perform a query to find all Forms completed by a user with a specified name, or all Forms where a field with name X has value Y and so forth.
I have looked into usage of indexes as specified in the documentation Indexing related documents, however the implementation as presented in the documentation threw a NotSupportedException when I implemented the example as follows:
class FormTemplates_ByFieldAndName : AbstractIndexCreationTask<FormTemplate>
{
public class Result
{
public string Name { get; set; }
public IList<string> FieldNames { get; set; }
}
public FormTemplates_ByFieldAndName()
{
Map = FormTemplates => from FormTemplate in FormTemplates
select new
{
Name = FormTemplate.Name,
FieldNames = FormTemplate.FieldIds.Select(x => LoadDocument<FormField>(x).Name)
};
}
}
// in code:
IList<FormTemplate> TestResults = session.Query<FormTemplates_ByFieldAndName.Result, FormTemplates_ByFieldAndName>()
.Where(x => x.Name == "TemplateName" || x.FieldNames.Contains("FieldName"))
.OfType<FormTemplate>()
.ToList();
As best as I can tell this was implemented correctly, however I have seen a suggestion to replace the .Contains with a .Any implementation instead. In lieu of this I have been experimenting with a different approach by applying successive .Where arguments. Like so:
var pre = session.Query<FormTemplates_ByFieldAndName.Result, FormTemplates_ByFieldAndName>();
var pr2 = pre.Where(x => x.Name == "TypeTest25");
List<FormTemplate> TestResults = pr2
.Where(x => x.FieldNames.Any(a => a == "field25"))
.OfType<FormTemplate>()
.OrderByScoreDescending()
.ToList();
Modifying the system to perform in a more factory oriented approach by applying successive filters based on a supplied string in a pre-specified format.
Is this the way I should be going for this implementation and if not what should I be changing? In particular if I am to proceed with the Indexing option how would I apply this technique to the nested relationship between Forms and FormFields through FormTemplates.
You seems to be trying to do this in a way that is mostly relational, but you don't have to.
Instead of trying to have a set of independent documents that each has part of the data, just store it all in a single document.
public class Form
{
public string Id { get; set; }
public string Name { get; set; }
public IList<FieldValue> FieldValues { get; set; }
public string UserId { get; set; } // the user who completed the form.
public string FormTemplateId { get; set; }
}
public class FieldValue
{
public string Id { get; set; }
// can store the value directly!
//public string ValueAsJsonString { get; set; }
public object Value {get; set; }
}
This will generate documents that looks like this:
{
"Id": "forms/1234",
"Name": "Tom",
"FieldValues": [
{
"Id": "FromValues/SchoolDistrictName",
"Value": "ABi195"
}
],
"UserId": "users/tom",
"FormTemplateId": "FromTemplate/1234"
}
Which is a much more natural way to model things.
At that point, you can use RavenDB's ability to index dynamic data, see the docs here:
https://ravendb.net/docs/article-page/3.5/Csharp/indexes/using-dynamic-fields
I have a table in SqlServerDatabase. Table name is User(Id,Name,Paswd) and Im using automapper in Mvc4. Now i want only specific fields or 2 fields from the table instead of whole table, using automapper.how to do??
basically if the 2 objects have the same fields as in the little example
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Paswd { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Paswd { get; set; }
}
You just have to ignore the field
Mapper.CreateMap<User, UserDto>()
.ForMember(o => o.Paswd, m => m.Ignore());
You can find a lot of usefull example and features here
Automapepr Wiki
Forgive me if this question has been answered somewhere, I have been having a hard time finding a solution for this problem.
I am trying to set up EF Code First on an MVC4 Project. I have a User and Customer that both inherit from Person. I then have a Template object that has a Many-to-Many relationship with Customer and a One-to-Many relationship with User. Here is how I have it set up:
MODELS
public class Person
{
[Key]
public int PersonID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string FullName
{
get
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
public string Email { get; set; }
public virtual List<Template> Templates { get; set; }
}
public class User : Person
{
....
}
public class Customer : Person
{
....
}
public class Template
{
public int TemplateId { get; set; }
public string TemplateName { get; set; }
public virtual List<Customer> Customers { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public virtual User User { get; set; }
}
CONTEXT
public class ProjectContext : DbContext
{
public ProjectContext()
: base("name=ProjectDB")
{
}
public DbSet<Template> Templates { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Template>()
.HasMany(x => x.Customers)
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);
}
}
If I remove the Person DBSet out of the context this works fine but sets up TPT inheritance. I would like to use TPH inheritance, but when I enable migrations with the Person DBSet in the context it chokes:
NavigationProperty 'Templates' is not valid. Type 'MvcProject.Models.Customer' of FromRole 'Template_Customers_Target' in AssociationType 'MvcProject.Models.Template_Customers' must exactly match with the type 'MvcProject.Models.Person' on which this NavigationProperty is declared on.
Where am I going wrong here?
You cannot inherit navigation properties from a base entity. They always must be declared in the class the other end of the relationship is refering to.
Template.Customers is refering to Customer (not to Person), hence the inverse navigation property Templates must be declared in Customer (not in Person)
Template.User is refering to User (not to Person), hence the inverse navigation property Templates must be declared in User (not in Person)
So, basically you must move the Templates collection from Person into both derived classes:
public class Person
{
// no Templates collection here
}
public class User : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
public class Customer : Person
{
//...
public virtual List<Template> Templates { get; set; }
}
Then you can define the two relationships with Fluent API like so:
modelBuilder.Entity<Template>()
.HasMany(t => t.Customers)
.WithMany(c => c.Templates) // = Customer.Templates
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer"));
modelBuilder.Entity<Template>()
.HasRequired(t => t.User)
.WithMany(u => u.Templates) // = User.Templates
.HasForeignKey(t => t.UserId);
Change your HasMany selector to People:
modelBuilder.Entity<Template>()
.HasMany(x => x.People) // here
.WithMany(x => x.Templates)
.Map(x => x.MapLeftKey("TemplateId")
.MapRightKey("PersonId")
.ToTable("TemplateCustomer")
);
I'm using Sharp Architecture and have a number of situations where Value Objects are used in an Entity. Here is an obvious simple example:
public class Person : Entity
{
protected Person(){}
public Person(string personName)
{
this.PersonName = personName;
}
public virtual string PersonName { get; protected set;}
public virtual StreetAddress MailingAddress { get; set; }
}
public class StreetAddress : ValueObject
{
protected StreetAddress(){}
public StreetAddress(string address1, string address2, string city, string state, string postalCode, string country )
{
this.Address1 = address1;
this.Address2 = address2;
this.City = city;
this.State = state;
this.PostalCode = postalCode;
this.Country = country;
}
public virtual string Address1 { get; protected set; }
public virtual string Address2 { get; protected set; }
public virtual string City { get; protected set; }
public virtual string State { get; protected set; }
public virtual string PostalCode { get; protected set; }
public virtual string Country { get; protected set; }
}
This of course throws: An association from the table Person refers to an unmapped class: Project.Domain.StreetAddress
because the the AutoPersistenceModelGenerator only includes classes with type IEntityWithTypedId<>. Its not clear how Sharp Architecture expects this common condition to be implemented. Does this have to be handled with a bazillion overrides?
You could change the GetSetup() method in AutoPersistenceModelGenerator to something like:
private Action<AutoMappingExpressions> GetSetup()
{
return c =>
{
c.IsComponentType = type => type.BaseType == typeof (ValueObject);
};
}
I'll try to get the blogpost I saw covering this posted for credit.
You would want to map this as a component. You can use the mapping overrides in Fluent NHibernate to accomplish this.
I agree with Alec. I would map this as a component.
For more information on that, see this SO question:
AutoMapping a Composite Element in Fluent Nhibernate
There, you'll also find info on how to map a collection of composite elements.