FluentValidation Validator using arguments - c#

I have a FluentValidation validator that I want to use to validate a booking. On a booking you must choose a room type that exists as an available room type on the tour that you are choosing. I need to get the available room types from a service, passing in the code for the tour. What is the best way to handle getting the tour code where it needs to be?
What I've got so far:
public class BookingValidator : AbstractValidator<Booking>, IBookingValidator
public BookingValidator()
{
RuleFor(booking => booking.Rooms).SetValidator(new RoomValidator())
}
public class RoomValidator : AbstractValidator<Room>
public RoomValidator()
{
//validate that room.Type (eg. TWIN) exists in availableRoomTypes (eg List<string> {'SINGLE','TWIN'}
}
Some hack at the problem:
public class BookingValidator : AbstractValidator<Booking>
//should/can i pass in arguments here when IoC container is wiring up IBookingValidator to BookingValidator? Seems awkward
public BookingValidator(string tourCode)
{
//if so, use argument to get available room types, pass to RoomValidator
var availableRooms = RoomTypeService.GetAvailableRoomTypesForTour(tourCode);
RuleFor(booking => booking.Rooms).SetValidator(new RoomValidator(availableRooms))
//alternatively, tourCode is available from booking - is there some way to pass it to RoomValidator?
RuleFor(booking => booking.Rooms).SetValidator(new RoomValidator(),booking => booking.TourCode);
//Or is there some way I should be using .Must() or Custom()??
}
So the main problem is how or where to get tour code into the validator...?

I would suggest creating a service that has dependencies on IRoomTypeService and IBookingValidator. It gets the available room types from the IRoomTypeService dependency and passes them to the validator via a property. See the following code by way of example:
public class BookingValidationService : IBookingValidationService
{
public IRoomTypeService RoomTypeService { get; set; }
public IBookingValidator BookingValidator { get; set; }
public ValidationResult ValidateBooking(Booking booking, string tourCode)
{
BookingValidator.AvailableRooms = RoomTypeService.GetAvailableRoomTypesForTour(tourCode);
return BookingValidator.Validate(booking);
}
}

Related

WebAPI deserialization of a protected property is null

My solution has a WebAPI project (.net core 3.1, Microsoft.AspNetCore.Mvc) and a (.Net Standard 2.1) class library that defines the data structures.
My Controller takes a post with a single parameter that deserializes mostly correctly
public class apiRequest
{
public RequestData TheData { get; set; }
public Options Options { get; set; }
public apiRequest() { }
}
The RequestData and child objects are defined i a .Net Standard 2.1 class library and added via a nuget package
public class RequestData : IRequestData
{
public int Datum{ get; set; }
...
public List<ComplexItem> ComplexItems { get; set; }
...
}
public class ComplexItem: ItemBase, IComplexItem
{
public ComplexItem() : base() { }
public ComplexItem(Pricing defaultPricing) : base(defaultPricing) { }
[JsonConstructor]
public ComplexItem(Pricing defaultPricing, Pricing selectedPricing) : base(defaultPricing, selectedPricing) { }
}
The problem I am running into is with the defaultPricing is always null when it gets to the controller
public class ItemBase : IItemBase
{
public ItemBase () { }
public ItemBase (Pricing defaultPricing)
{
DefaultPricing = defaultPricing;
}
[JsonConstructor]
public ItemBase (Pricing defaultPricing, Pricing selectedPricing)
{
DefaultPricing = defaultPricing;
SelectedPricing = selectedPricing;
}
#region Pricing
[JsonProperty]
protected Pricing DefaultPricing { get; set; }
public Pricing SelectedPricing { get; set; }
[JsonIgnore]
protected Pricing CurrentPricing
{
get { return SelectedPricing ?? DefaultPricing; }
set { SelectedPricing = value; }
}
[JsonIgnore]
public decimal Cost { get => CurrentPricing?.Cost ?? 0; }
[JsonIgnore]
public decimal Price { get => CurrentPricing?.Price ?? 0; }
#endregion
}
I've tried using [DataContract] and [DataMember] attributes, JsonObject, JsonConstructor, JsonProperty attributes and [Serializable] attribute. (Is there a current best practice on what to use?)
If I read the Json from a file and use Newtonsoft.Json.JsonConvert.DeserializeObject it deserializes correctly with the Json attributes added, but still null in the controller.
It also deserializes in the API properly if I make it public, so it doesn't seem like a problem in the Pricing class itself
After posting I found this Question about making Newtonsoft the default and using MikeBeaton's accepted solution there with Microsoft.AspNetCore.Mvc.NewtonsoftJson package worked so I'll put this as one potential answer for anyone else with this issue. Would still like to know if there is a more correct solution available.
System.Text.Json Serializes Public Properties
As the documentation implies (emphasis mine):
By default, all (read: only) public properties are serialized. You can specify properties to exclude.
I would guess that this was the design chosen because serializing an object is allowing that object to cross barriers of scope and the public scope is the only one that can reliably be assumed.
If you think about it, it makes sense. Lets say, you define a protected property and serialize the object. Then a client picks it up and deserializates that text representation into a public property. What you have designed to be an implementation detail of/to derived types is now accessible outside the scope defined by the modifier.
Apart from simply pointing you to your own answer where Newtonsoft allows this protected property to be serialized, I would suggest you look more intently at your design and why those properties are protected in the first place. It makes sense within the context of your API implementation, but the client can't (shouldn't) be assumed to follow your same inheritance structure (or support inheritance at all). It seems like you might want to define a true DTO to act as the "shape" of your API response and find the right place to transition from your internal types using protected scope to control access and the DTO that can cross the border of the API.

Is there a way to make FluentValidation more dynamic?

We just received a phase 1 release from a vendor to translate an archaic PowerBuilder application into C# MVC with Angular 5 (we have an Angular guy that has already mostly rewritten the front end in 7 so the security concerns from 5 are a nonissue). Since the statement of work only required them to reproduce the application there are next to zero validations on input because there wasn't much, if any, on the original application.
I have recently done some research into FluentValidation and like it for its reusability later in applications that will use the same overall data. However, looking at this code the models in the MVC are not normalized like they probably should be and so we have dozens of models that likely could be normalized out so that there would be less overlap in data fields such as First Name, Last Name, Address, Business Address etc.
I have basic experience with generics and reflection and have supported a few more advanced examples in the past. So I was trying to find some way to utilze these two concepts to make the validators more dynamic.
I was unable to find much in the way of more advanced FluentValidation examples other than the basic hard connection to a given named model. I have tried to use the generic T in place of the model but was unable to bridge the gap and access the object being passed into the validation.
public class FormValidator : AbstractValidator<ModelExample>
{
public FormValidation()
{
}
}
//tried to do something like this but wasn't able to access the .HasProperties. Although I was able to access the GetProperties,
//having trouble implementing it into the RuleFor however.
public class FormValidation<T> : AbstractValidator<T>
{
RuleFor(x => x.GetType().GetProperty(nameof({something if it exists}).{check stuff is valid}
{
public class ModelExample
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class OtherModelExample
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
My end goal would be to be able to pass related objects into a given validator and it would be able to determine if the properties existed and act accordingly.
This may be an issue where I don't really know how to ask the question in Google, I tend to have issue wording things in a way where it brings up what I would expect.
This also may not even be possible but if it could save me from writing a series of hard coupled validators that I might have to rewrite later if we ever are allowed to normalize the data flow it would be of great help.
Any articles or documentation with more advanced examples than the simple ones I find would be of great use even beyond this project. Most of the tutorials I find are very basic examples and I sometimes have a hard time picturing them in "real" code application.
Thanks
Instead of creating generic validators for a whole model, have you considered the reverse and creating them for each property?
If you use custom property validators you can specify the validator logic once, and then simply create a validator class per view model.
eg:
class Program
{
static void Main(string[] args)
{
var person = new Person
{
Name = "Ada",
NickName = "A"
};
var validator = new PersonValidator();
var result = validator.Validate(person);
//Should be a problem with the NickName
}
}
class Person
{
public string Name { get; set; }
public string NickName { get; set; }
}
class PersonValidator : AbstractValidator<Person>
{
public PersonValidator()
{
RuleFor(x => x.Name).SetValidator(new NameValidator());
RuleFor(x => x.NickName).SetValidator(new NameValidator());
}
}
public class NameValidator : AbstractValidator<string>
{
public NameValidator()
{
RuleFor(x => x).Must(x => x.Length > 1)
.WithMessage("The name is not long enough");
}
}
This is probably a safer option too, as it's opt in rather than implicit.

FluentValidation not working on collection of outer model objects

I am having trouble getting FluentValidation to work with a collection of objects. My controller POST action takes in an IEnumerable of objects like below. When I post to an action that takes a single EventInputDto, with an incorrectly formatted Url property, my validation occurs successfully. When I post to a collection of EventInputDto, it does not work and does no validation.
If I use regular MVC Attributes (i.e. required / email), they work with collections as well as single objects. How do I get this to work with FluentValidation? I am not working with inner collections so I'm not sure why it does not work as intended.
public async Task<IActionResult> CreateEventCollection([FromBody] IEnumerable<EventInputDto> events)
{
if (!ModelState.IsValid)
{
return UnprocessableEntity(ModelState); //does not work
}
}
My validators are setup using generics because I am using separate models for inputs and updates.
public class EventManipulationValidator<T> : AbstractValidator<T> where T : EventManipulationDto
{
public EventManipulationValidator()
{
RuleFor(manipulationDto => manipulationDto.Title).NotNull().WithMessage("Title cannot be blank")
.Length(1, 50);
RuleFor(manipulationDto => manipulationDto.Message).NotNull().WithMessage("Message cannot be blank")
.Length(1, 1000);
RuleFor(manipulationDto => manipulationDto.ScheduledTime).NotNull().WithMessage("Scheduled Time cannot be blank");
RuleFor(inputDto => inputDto.Url).Matches(#"https://.*windows\.net.*").WithMessage("The url must be valid and stored on Azure");
}
}
As my CreateEventCollection action takes in an IEnumerable of EventInputDto, my validator for EventInputDto is setup as below:
public class EventInputValidator : EventManipulationValidator<EventInputDto>
{
public EventInputValidator()
{
//all property validators are inherited from EventManipulationValidator
}
}
public class EventInputCollectionValidator : AbstractValidator<IEnumerable<EventInputDto>>
{
public EventInputCollectionValidator()
{
RuleForEach(p => p).SetValidator(new EventManipulationValidator<EventInputDto>());
}
}
Below are my models for reference:
EventManipulationDto
public abstract class EventManipulationDto
{
public string Title { get; set; }
public string Message { get; set; }
public string Url { get; set; }
public DateTime? ScheduledTime { get; set; }
}
EventInputDto
public class EventInputDto : EventManipulationDto
{
//all properties inherited from base class
}
After going through the list of open/closed issues on the project GitHub, it seems that not all of my approach is required. There is no need for my `EventInputCollectionValidator. FluentValidation no longer requires explicitly defining an IEnumerable validator like I defined above.
It's enough to define a base AbstractValidator or as in my case an inherited validator from a parent class.
The only change needed to get it to work was in my startup.cs when registering fluentvalidation. I needed to explicitly add ImplicitlyValidateChildProperties = true. Didn't realize this was required as I thought this was for validating child property collections and not the parent collection objects. Works perfectly now.
.AddFluentValidation(fv => {
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = true;
fv.RegisterValidatorsFromAssemblyContaining<Startup>();
fv.ImplicitlyValidateChildProperties = true;
});

Hide model classmember in controller in C# Web API?

I am trying to learn and understand C# Web API and MVC.
I understand the simple tutorials where one has a simple Product or Person class as a Model and then makes a CRUD Controller to make use of the model.
But I need it to be a bit more complex and can't figure it out.
I have following Model:
public class PersonModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public DateTime LastUpdated { get; set; }
}
Same as the table in my database. The LastUpdated column has a default constraint: (GETUTCDATE())
I am not interrested in exposing LastUpdated in my POST methods in PersonsController:
public void PostPerson(PersonModel person)
{
// Upload person to database
}
Because then one could insert an invalid datetime in LastUpdated - or I have to manuel set LastUpdated in my business logic, but why not just let my SQL server do it?
Anyway to hide LastUpdated in PostPerson?
As a sidenote I would like to be able to show LastUpdated in my GetPerson method.
How is that possible?
When you implement a property in a class, you can specify different access modifiers for the get vs. set accessors.
This is true whether you are implementing the property yourself, or using an automatic property.
Different combinations of access modifiers include:
get/set both public – client can read/write property value
get/set both private – client has no access to the property
get public, set private – property is read-only
get private, set public – property is write-only
// get/set both public
public string Name { get; set; }
// get/set both private
private string SecretName { get; set; }
// public get => read-only
public string CalcName { get; private set; }
// public set => write-only
public string WriteOnlyName { private get; set; }
You could create a custom DTO as a view model for the POST operation on this controller. This would be additionally handy because you probably also don't want the client to supply the Id value either (I assume). Something like this:
public class PersonDTO
{
public string Name { get; set; }
public string Title { get; set; }
}
This would be the input for the controller action:
public void PostPerson(PersonDTO person)
{
// Upload person to database
}
Then in the code you'd create a new PersonModel to add to the data context. Something like:
using (var db = new MyDataContext())
{
var newPerson = new PersonModel
{
Name = person.Name,
Title = person.Title
};
db.Persons.Add(newPerson);
db.SaveChanges();
}
(Or perhaps create a kind of translation method on the DTO which returns an instance of the model, acting as a sort of factory method and putting the logic in the object rather than in the controller.) This way the client isn't providing an entire PersonModel instance, just an object which describes the creation of that instance. The GET operation can still return the full PersonModel.
When building an API (using WebAPI, for example) it can often be really useful to fine-tune the inputs and outputs like this. And such custom DTOs/ViewModels really come in handy, albeit at the cost of slightly more code by creating essentially a translation layer to the backing models.
One tool I've found particularly handy in determining where in the API I need to tweak things is when using Swagger to generate my API docs. Looking through the generated docs, I may notice something which I don't want to be exposed. This is an indicator that I need to customize that API endpoint a little more so that the resulting docs are a little cleaner.
Try adding the exclude attribute above the property
[Exclude]
public DateTime LastUpdated {get; set(}

NHibernate + ASP.Net MVC + User Activity Feed

I am looking for the most appropriate way of dealing with a user activity feed on my social networking site. At the moment i have several activities which can appear on the news feed such as:
Users joins the site
User comments on a post
User adds a post to their favorites
User adds a new post to the site
Here is a simplified version of my domain objects at the moment:
public abstract class NewsItem : Entity, ITenantSpecific
{
public virtual Account Account { get; set; }
public virtual DateTime DateTime { get; set; }
// returns formatted news html string which gets
// overridden by inherted classes
public abstract string GetNewsHtml();
}
public class NewsItemJoiner : NewsItem
{
public virtual Account AccountJoined { get; set; }
public override string GetNewsHtml()
{
return "XXX has just joined our music network";
}
}
As you can see at the moment I have a property which must be overridden on each activity called GetNewsHtml. This isn't ideal as I don't believe my domain should be responsible for generating my HTML.
I have thought about using a partial view for each activity type and pass into it the NewsItem base class downcasted into the correct type.
However I am open to suggestions.
I have a similar issue but with different order types. I decided to define rendering at the view layer (web/controllers), not domain. You can do it this way:
public interface IRenderer<T> where T: NewsItem
{
string Render(T item);
}
public class NewsItemJoinerRenderer: IRenderer<NewsItemJoiner>
{
public string Render(T item)
{
return "XXX has just joined our music network";
}
}
public class NewsRendererFactory
{
public IRenderer<T> GetRenderer<T>()
{
return ServiceLocator.GetInstance<IRenderer<T>>();
}
}
Then you can pass NewsRendererFactory to controller. Perhaps there's a way to avoid ServiceLocator but for now I cannot tell.
Notice that this makes your architecture both configurable and pluggable if needed.
You can define additional render-related interfaces, add more properties to the IRenderer - for example, PartialName, etc, or have lambda filters on IRenderer that Factory uses to decide if this interface implementation is applicable for the passed (to GetRenderer("some-condition")) condition. A lot of things are possible.
If you don't want IoC containers (ServiceLocator), you can have its job done with simple switch() statement inside NewsRendererFactory.GetRenderer. This will isolate the logic inside single factory method, and you'll be able to replace it easily with true IoC once you are ready.
Update: how to get renderers.
If you don't use IoC, you do something like
typeof(IRenderer<>).Assembly.GetTypes().Where(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IRenderer<>) &&
x.GetGenericArguments().FirstOrDefault() == requestedTypeArguments)
Then you can select SingleOrDefault() or ToList() if you can handle multiple renderers.

Categories

Resources