I have an ASP.NET Core Web API.
I an endpoint which accepts a model called Search. It has property called Query of type Expression. This Expression object has sub classes.
public class Search {
public Expression Query{get;set;}
}
Public class Expression {
}
public class AndExpression {
public IList<Expression> Expressions {get;set;}
}
public class MatchesExpression {
public string FieldId {get;set;}
public string Value {get;set;}
public string Operator {get;set;}
}
I post the following JSON to my endpoint (content-type of application/json)
{
"query":
{
"fieldId": "body",
"value": "cake",
"operator": "matches"
}
}
Firstly, the query parameter is just the base Expression - A polymorphic issue!
So... I thought bespoke Model Binder.
I can set up a model binder against the Search object, but you'll note that the AndExpression can contain other Expression objects, so instead I'd like to write a binder that can be bound to "Query" on the Search Model and to Expressions on the AndExpression Model etc etc
I attempted this:
public class Search
{
[ModelBinder(BinderType = typeof(ExpressionBinder))]
public Expression Query { get; set; }
}
public class ExpressionBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
throw new NotImplementedException();
}
}
public class ExpressionBinderProvider : IModelBinderProvider {
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(Expression))
{
return new BinderTypeModelBinder(typeof(ExpressionBinder));
}
return null;
}
}
Ive wired this binder up in the configureServices method of my Startup Class.
I have a break point in the ExpressionBinder and it doesn't hit!
What am I doing wrong?
Also, can I use the [ModelBinder(BinderType = typeof(ExpressionBinder))] attribute against a list of Expressions?
So this is very explicit https://github.com/aspnet/Mvc/issues/4553
If a FromBody attribute is applied the ModelBinder attribute wont work!
Related
I have a class with a collection that needs validation. The generic on the collection takes an interface and different types can be added to the collection.
What is the cleanest path forward to creating a FluentValidation validator that supports polymorphism?
public interface IWizardStep {}
public class WizardOne : IWizardStep
{
public string Model { get; set; }
}
public class WizardTwo : IWizardStep
{
public string FirstName { get; set; }
}
public class Wizard
{
public Wizard()
{
var w1 = new WizardOne();
var w2 = new WizardTwo();
Steps = new List<IWizardStep>
{
w1,
w2
};
}
public IList<IWizardStep> Steps { get; set; }
}
public class WizardValidator : AbstractValidator<Wizard>
{
public WizardValidator()
{
RuleFor(x => x.Steps)
// Steps First where is WizardOne
// Model.NotEmpty()
// Steps First where is WizardTwo
// FirstName.NotEmpty()
}
FluentValidation doesn't support polymorphism for child collections like this out of the box, but you can add this behaviour by using a custom property validator, or by using OfType in your rule definitions.
I've written about both approaches before here:
Step 1: Create a validator for each implementor
Start by creating a validator for WizardOne and WizardTwo:
public class WizardOneValidator : AbstractValidator<WizardOne> {
public WizardOneValidator() {
RuleFor(x => x.Model).NotEmpty();
}
}
public class WizardTwoValidator : AbstractValidator<WizardTwo> {
public WizardTwoValidator() {
RuleFor(x => x.FirstName).NotEmpty();
}
}
Step 2: Create the parent validator
You have two options for defining the parent validator. The simplest approach is to use OfType, but this is less performant. The more complex option is to use a custom property validator.
Option 1: Using OfType
public WizardValidator : AbstractValidator<Wizard> {
public WizardValidator() {
RuleForEach(x => x.Steps.OfType<WizardOne>()).SetValidator(new WizardOneValidator());
RuleForEach(x => x.Steps.OfType<WizardTwo>()).SetValidator(new WizardTwoValidator());
}
}
This is the simplest approach, but calling OfType inside the call RuleFor will end up bypassing FluentValidation's expression cache, which is a potential performance hit. It also iterates the collection multiple. This may or may not be an issue for you - you'll need to decide if this has any real-world impact on your application.
Option 2: Using a custom PropertyValidator.
This uses a custom custom validator which can differentiate the underlying type at runtime:
public WizardValidator : AbstractValidator<Wizard> {
public WizardValidator() {
RuleForEach(x => x.Steps).SetValidator(new PolymorphicValidator<Wizard, IWizardStep>()
.Add<WizardOne>(new WizardOneValidator())
.Add<WizardTwo>(new WizardTwoValidator())
);
}
}
Syntactically, this isn't quite as nice, but doesn't bypass the expression cache and doesn't iterate the collection multiple times. This is the code for the PolymorphicValidator:
public class PolymorphicValidator<T, TInterface> : ChildValidatorAdaptor<T, TInterface> {
readonly Dictionary<Type, IValidator> _derivedValidators = new Dictionary<Type, IValidator>();
// Need the base constructor call, even though we're just passing null.
public PolymorphicValidator() : base((IValidator<TInterface>)null, typeof(IValidator<TInterface>)) {
}
public PolymorphicValidator<T, TInterface> Add<TDerived>(IValidator<TDerived> derivedValidator) where TDerived : TInterface {
_derivedValidators[typeof(TDerived)] = derivedValidator;
return this;
}
public override IValidator<TInterface> GetValidator(PropertyValidatorContext context) {
// bail out if the current item is null
if (context.PropertyValue == null) return null;
if (_derivedValidators.TryGetValue(context.PropertyValue.GetType(), out var derivedValidator)) {
return new ValidatorWrapper(derivedValidator);
}
return null;
}
private class ValidatorWrapper : AbstractValidator<TInterface> {
private IValidator _innerValidator;
public ValidatorWrapper(IValidator innerValidator) {
_innerValidator = innerValidator;
}
public override ValidationResult Validate(ValidationContext<TInterface> context) {
return _innerValidator.Validate(context);
}
public override Task<ValidationResult> ValidateAsync(ValidationContext<TInterface> context, CancellationToken cancellation = new CancellationToken()) {
return _innerValidator.ValidateAsync(context, cancellation);
}
public override IValidatorDescriptor CreateDescriptor() {
return _innerValidator.CreateDescriptor();
}
}
}
This will probably be implemented in the library as a first class feature at some point in the future - you can track its development here if you're interested.
I have this API
public ActionResult AddDocument([FromBody]AddDocumentRequestModel documentRequestModel)
{
AddDocumentStatus documentState = _documentService.AddDocument(documentRequestModel, DocumentType.OutgoingPosShipment);
if (documentState.IsSuccess)
return Ok();
return BadRequest();
}
And this is my request model
public class AddDocumentRequestModel
{
public AddDocumentRequestModel(int partnerId, List<ProductRequestModel> products)
{
PartnerId = partnerId;
Products = products;
}
[Range(1, int.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int PartnerId { get; private set; }
[Required, MustHaveOneElement(ErrorMessage = "At least one product is required")]
public List<ProductRequestModel> Products { get; private set; }
}
so when I'm trying to hit the API with this body
{
"partnerId": 101,
"products": [{
"productId": 100,
"unitOfMeasureId": 102,
"quantity":5
}
]
}
this is the request : System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'Alati.Commerce.Sync.Api.Controllers.AddDocumentRequestModel'
I don't need parameterless constructor,because it doesn't read the body parameters.Is there any other way for deserialization?
You can achieve your desired result. You need to switch to NewtonsoftJson serialization (from package Microsoft.AspNetCore.Mvc.NewtonsoftJson)
Call this in Startup.cs in the ConfigureServices method:
services.AddControllers().AddNewtonsoftJson();
After this, your constructor will be called by deserialization.
Extra info: I am using ASP Net Core 3.1
Later Edit: I wanted to give more info on this, as it seems that this can also be achieved by using System.Text.Json, although custom implementation is necessary. The answer from jawa states that Deserializing to immutable classes and structs can be achieved with System.Text.Json, by creating a custom converter (inherit from JsonConverter) and registering it to the converters collection (JsonSerializerOptions.Converters) like so:
public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
...
}
and then...
var serializeOptions = new JsonSerializerOptions();
serializeOptions.Converters.Add(new ImmutablePointConverter());
serializeOptions.WriteIndented = true;
Just in case someone have the same issue I had, I was using abstract class, once removed the abstract key word, it all worked just fine.
Just Add [JsonConstructor] before your constructor
like this
public class Person
{
public string Name { get; set; }
public int LuckyNumber { get; private set; }
[JsonConstructor]
public Person(int luckyNumber)
{
LuckyNumber = luckyNumber;
}
public Person() { }
}
There are still some limitations using System.Text.Json - have a look here: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson
Deserialization without parameterless constructor using a parameterized constructor is not supported yet (but it's on their plan). You can implement your custom JsonConverter (like in this example: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#deserialize-to-immutable-classes-and-structs) or - like Adrian Nasul above suggested: use Newtonsoft.Json and then you can use the [JsonConstructor] attribute
In my case I had set a class as internal and when I made it public it worked. The error message was really of little help with this specific circumstance.
Old (actual class name changed to ClassName in the example
internal class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = className?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
New:
public class Rootobject
{
[JsonConstructor]
public Rootobject(ClassName className)
{
ClassName = branding ?? throw new ArgumentNullException(nameof(className));
}
public ClassName ClassName { get; set; }
}
In my case error, caused was inside InnerException. There is my class had a field with a custom class type that did not have a parameterless constructor. I've added a parameterless constructor to the inner class and the problem has gone away.
I am aware about the problem when creating custom collections that inherits from List<T> or ICollection<T> with additional custom properties:
public class MyCollection: List<int>
{
public string MyCustomProperty { get; set; }
}
As I know, such collection will passed throw WCF as ArrayOfInt and WCF will not serialize my custom property. The solution is to create the wrapper class that will manage collection inside and will have a custom property.
I want to make a nicer workaround for my needs...does IEnumerable<T> will have the same problem?
public class MyCollection: IEnumerable<int>
{
/**************/
/* Code that implements IEnumerable<int> and manages the internal List<T> */
/* I know I will not able to cast it to List<T>, but I don't need it. */
/* If I will need it, I will implement cast operators later */
/**************/
public string MyCustomProperty { get; set; }
}
Will the class above pass throw WCF include MyCustomProperty value?
Thanks
I tried it and it does not serialize custom property.
I just returned the entire class object from the service method. Result is still ArrayOfInt (i used List as container)
public class MyExtension: IEnumerable<int>
{
public string CustomString { get; set; }
private List<int> lst = new List<int>();
public void Add(int i)
{
lst.Add(i);
}
public IEnumerator<int> GetEnumerator()
{
return lst.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return lst.GetEnumerator();
}
}
I had to mark it as DataContract and every member as DataMember to have all the properties serialized.
<MyExtension xmlns="http://schemas.datacontract.org/2004/07/GetRequestTest" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CustomString>sunny</CustomString>
<lst xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:int>1</a:int>
</lst>
</MyExtension>
I am using ValueInjecter to map properties from a Domain model to a DTO served up via a Service Layer. The service in question also accepts updates... so an updated DTO is passed in and this is then injected to the domain object and saved.
// Domain
public class Member
{
public Country Country { get; set; }
}
public class Country
{
public string Code { get; set; }
public string Name { get; set; }
}
//Dto
public class MemberDto
{
public string CountryCode { get; set; }
}
//Transformation Method attempt 1
public Member InjectFromDto (MemberDto dto, Member source)
{
source = source.InjectFrom<UnflatLoopValueInjection>(dto);
return source;
}
Now all this above code does is updates the Property Member.Country.Code which is obviously not what I need it to do.
So from the docs, I figured I needed to create an override and got this:
public class CountryLookup: UnflatLoopValueInjection<string, Country>
{
protected override Country SetValue(string sourcePropertyValue)
{
return countryService.LookupCode(sourcePropertyValue);
}
}
//revised transformation call
//Transformation Method attempt 2
public Member InjectFromDto (MemberDto dto, Member source)
{
source = source.InjectFrom<UnflatLoopValueInjection>(dto)
.InjectFrom<CountryLookup>(dto);
return source;
}
My problem is during debugging, CountryLookup never gets called.
Possible reasons I can think of:
Nhibernate Proxy classes causing value injecter to not match the Country type? Tho this doesnt make sense because it works during the flattening.
Perhaps the unflattening isn't firing for some reason. I.e Dto is CountryCode and Domain is Country.Code
I need to use the CountryCode property on the Dto to call a countryService.LookupCode to return the correct object to use during the update injection.
unflattening would be to do this:
entity.Country.Code <- dto.CountryCode
what you need is:
entity.Country <- dto.CountryCode
so the solution for you would be to inherit an ExactValueInjection where you would go from CountryCode to Country.
what I recommend you to do is do the same that I did in the live demo of another project of mine http://awesome.codeplex.com
where I have something like this:
public class Entity
{
public int Id{get;set;}
}
public class Member : Entity
{
public Country Country{get;set;}
}
public class MemberDto : DtoWithId
{
public int? Country {get;set;}
}
and use these injections to go from entity to dto and back
public class NullIntToEntity : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType == typeof(int?) && targetType.IsSubclassOf(typeof(Entity));
}
protected override object SetValue(object sourcePropertyValue)
{
if (sourcePropertyValue == null) return null;
var id = ((int?) sourcePropertyValue).Value;
dynamic repo = IoC.Resolve(typeof(IRepo<>).MakeGenericType(TargetPropType));
return repo.Get(id);
}
}
//(you also need to have a generic repository, notice IRepo<>)
public class EntityToNullInt : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType.IsSubclassOf(typeof (Entity)) && targetType == typeof (int?);
}
protected override object SetValue(object o)
{
if (o == null) return null;
return (o as Entity).Id;
}
}
these injections will handle not just going from int? to Country and back but also any other type which inherits Entity
Using the suggestion/reference from Omu this was the specific code to the problem.
public class CountryLookup : ExactValueInjection
{
private ICountryService countryservice;
public CountryLookup(ICountryService countryService)
{
this.countryService = countryService;
}
protected override bool TypesMatch(Type s, Type t)
{
return (s == typeof(string)) && (t == typeof (Country));
}
protected override Object SetValue(object v)
{
if (v == null)
return null;
var country = countryService.LookupCode((string) v);
return country;
}
public override string SourceName()
{
return "CountryCode";
}
public override string TargetName()
{
return "Country";
}
}
public Member InjectFromDto (MemberDto dto, Member source)
{
source = source.InjectFrom<UnflatLoopValueInjection>(dto)
.InjectFrom<CountryLookup>(dto);
return source;
}
Is a framework calling the setter method? In most DI frameworks, the standard is lowercase 's' in the setMethod(). Just a first-thought recommendation.
I know this question has been raised in similar form multiple times, but none of the threads could give me the concrete answer to my question.
I use Fluent NHibernate and Fluent`s auto-mapping to map my domain entities. Right now, I use this convention class to set all properties NOT NULL:
public class NotNullColumnConvention : IPropertyConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
The big question is:
What do I need to do, to allow single properties of my entity classes to be NULL?
Here is one of my entity classes:
public class Employee : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
I´d be really pleased, if someone can finally help me out! All possible search string I have entered into Google return pages, marked as already visited...
Thanks,
Arne
EDIT: Changed title ... Want to allow NULL for single properties
Create an attribute :
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CanBeNullAttribute : Attribute
{
}
And a convention :
public class CanBeNullPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(
x => !this.IsNullableProperty(x)
|| x.Property.MemberInfo.GetCustomAttributes(typeof(CanBeNullAttribute), true).Length > 0);
}
public void Apply(IPropertyInstance instance)
{
instance.Nullable();
}
private bool IsNullableProperty(IExposedThroughPropertyInspector target)
{
var type = target.Property.PropertyType;
return type.Equals(typeof(string)) || (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
}
Drop the attribute on top of your properties.