Asp.Net Core Model Validation *Multiple* Attributes - c#

I have a Asp.Net Core REST service and I'm using the built in validation. I needed some additional functionality, so I found some examples of validation attributes that I needed, so here is a small part of my model:
[RequiredIfEmpty("B")]
[RequiredIfEmpty("C")]
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
So, pretty obvious what I'm going for. I want to validate that A is specified if B or C is empty.
When I send a JSON request that will fail validation, I only get:
"A is required when B is empty."
I'm expecting to get:
"A is required when B is empty."
"A is required when C is empty."
So, it seems like the validation code does a distinct on the attributes based on type because it ignores the 2nd one. This is further proven if I do:
[RequiredIfEmpty("B")]
[RequiredIfEmpty2("C")]
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
RequiredIfEmpty2 is just derived from RequiredIfEmpty, no additional code. Now I get the expected:
"A is required when B is empty."
"A is required when C is empty."
In this example, I only have 2 dependent properties, so no biggie to create a 2 version, but its very hacky and I don't like it.
I thought about changing the RequiredIfEmpty attribute to take a string[] of properties, but it doesn't appear like the MVC infrastructure would allow multiple error strings returned by a single attribute.
I did report it to Microsoft, but wondering if anybody else can think of a work-around besides having a 2 version?

.Net Core + .Net Framework
You can override the Equals and GetHashCode methods in the attribute to create distinctions between AllowMultiple attributes.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public sealed class RequiredIfEmptyAttribute : RequiredAttribute
{
private object _instance = new object();
public override bool Equals(object obj) => _instance.Equals(obj);
public override int GetHashCode() => _instance.GetHashCode();
// all the rest of the code
}
.Net Framework Only
If you're only targeting the .Net Framework, you can use the TypeId property to be a unique identifier between two attributes of the same type. (MSDN documentation)
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public sealed class RequiredIfEmptyAttribute : RequiredAttribute
{
public override object TypeId { get; } = new object();
// all the rest of the code
}
I thought about changing the RequiredIfEmpty attribute to take a string[] of properties, but it doesn't appear like the MVC infrastructure would allow multiple error strings returned by a single attribute.
Correct. You cannot return more than one message per attribute. You could implement the IValidatableObject interface to achieve this, however.

Related

Why does the JSON2CSharp online converter decorate each property with a [JsonProperty("...")] attribute?

I have this JSON response from an API:
{
"arguments": {
"Configuration": {
"Building_Configuration.Parameters_SP.fixtureStrategy_SP": "ETA",
"Building_Configuration.Parameters_SP.dimensionSelection_SP": "Imperial",
"Building_Configuration.Parameters_SP.controllerRobotic_SP": false,
"Building_Configuration.Parameters_SP.controllerBACNet_SP": false
}
}
}
I have this Root.cs Model file that I used the JSON to C# Converter to make that corresponds to the JSON above in my solution in C# Visual Studio 2019:
public class Root
{
public Arguments arguments { get; set; }
}
public class Arguments
{
public Configuration Configuration { get; set; }
}
public class Configuration
{
[JsonProperty("Building_Configuration.Parameters_SP.fixtureStrategy_SP")]
public string BuildingConfigurationParametersSPFixtureStrategySP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.dimensionSelection_SP")]
public string BuildingConfigurationParametersSPDimensionSelectionSP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.controllerRobotic_SP")]
public bool BuildingConfigurationParametersSPControllerRoboticSP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.controllerBACNet_SP")]
public bool BuildingConfigurationParametersSPControllerBACNetSP { get; set; }
}
I'm trying to access and return the value of BuildingConfigurationParametersSPFixtureStrategySP (the first property in the Configuration class above) in this manner:
public string CreateExecPost()
{
/*...everthing here works fine down through the end of this method. I can set
breakpoints and step through the following code and look at the values of all the
following variables and all is well
....*/
var payload = new StringContent(newPost, Encoding.UTF8, "application/json");
var result = client.PostAsync(endpoint, payload).Result.Content.ReadAsStringAsync().Result;
Root MyObject = JsonConvert.DeserializeObject<Root>(result);
return MyObject.arguments.configuration.BuildingConfigurationParametersSPFixtureStrategySP;
} //The correct value for BuildingConfigurationParametersSPFixtureStrategySP is returned and all is well!
So, the question is why does the converter generate an attribute like...
[JsonProperty("Building_Configuration.Parameters_SP.fixtureStrategy_SP")]
...above each of the { get; set; } statements? I've researched and read about JsonProperty and JsonPropertyAttribute, but I'm still not fully clear on it. I'm not seeing what the tool uses to signal it to generate the attribute or why it does it.
This tool generates code with the Json.net library by default, and the official documentation doesn’t explain much on this class: https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_JsonProperty.htm
There are other similar questions on how to use this, for example:
What [JsonProperty] used for in c#?
The general usage of this class is when you want to, or need to, rename a property. And the last one is relevant here.
The json parser normally tries to match the property names 1:1 with your class properties.
But whenever the json property name contains reserved keywords, language syntax, or otherwise illegal property names, you need to rename it.
In your example the name Building_Configuration.Parameters_SP.fixtureStrategy_SP contains periods. If you would try to name your get;set property like this, the code would not compile.
The site that generates the code knows this, and will add the required JsonProperty to map the full json property name to the class field that was renamed to BuildingConfigurationParametersSPFixtureStrategySP (the illegal characters were removed)
See for valid property names: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names
And for reference: Accessing properties with a dot in their name

C# GRPC Reference existing object

Im completely new in using GRPC. I have and question regarding setting up the .proto file.
In my existing solution i have forexample this class:
public class Car
{
public int Id { get; set; }
public string Brand { get; set; }
public string? Type {get; set;}
}
The Car class is placed in a Core project, since it's used by other logic around the solution. But if i like to return in my GRPC Server, is it really necessary to define it in the .proto file again ala this:
message CarReply {
int32 Id = 1;
string Brand = 2;
string Type = 3;
}
What i liked was an reference to my Car() class. Is this not possible?
If you want to use vanilla "contract first" protobuf, then yes: you need to use a schema and the generated type - it is the generated type that knows how to perform the serialization.
However! There may be an alternative; protobuf-net (and protobuf-net.Grpc) provide an independent implementation of protobuf that supports "code first" usage, which allows you to use your existing type model.
The easiest way to do this with protobuf-net is to annotate your model (using either protobuf-net's attributes, or the framework data-contract attributes - the first option giving more control); for example:
[ProtoContract]
public class Car
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Brand { get; set; }
[ProtoMember(3)]
public string? Type {get; set;}
}
It is also possible to configure everything at runtime, but that's a bit harder.
This can also be used fully with gRPC, using interfaces to define the service contract; full guidance is here

FileHelpers error: The field: 'k__BackingField' has the type: XXX that is not a system type, so this field need a CustomConverter

I need to read a CSV file with FileHelpers based on type, automatically generated by my MVC model. The model looks like this:
public partial class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
The last field is obviously generated by a foreign key in database, referring to table MerchantCategories.
Then I attempt to create an instance of FileHelperEngine with this type:
var engine = new FileHelperEngine<Merchant>();
And get the following exception:
The field: 'k__BackingField' has the type: MerchantCategory that is not a system type, so this field need a CustomConverter ( Please Check the docs for more Info).
Actually I don't need this field at all for my import, so I tried to ignore it in derived class:
[DelimitedRecord(",")]
public class MerchantForImport : Merchant {
[FieldHidden]
new public MerchantCategory MerchantCategory;
}
var engine = new FileHelperEngine<MerchantForImport>();
And still the same error. I don't need this field at all, I don't want to implement any FieldConverter for it, I never asked for this k__BackingField and it's nowhere to be found in my code!
I can't call FileHelperEngine.Options.RemoveField() because the exception is thrown by the constructor.
Where does that come from? How do I get rid of it?
From a design perspective, I think you are going about it the wrong way. You are trying to use the Merchant class for two incompatible uses. Instead you should have two separate classes.
FileHelpers is a library for describing csv files so that you can import them easily. You should have a MerchantFileSpec for describing your file. It's really not a proper C# class - it may have: dummy fields to represent unused columns; lots of attributes [FieldNullValue], [FieldQuoted], [FieldConverter]; etc. It works best with public fields (a FileHelpers limitation which is not C# best practice), etc. It is a convenience syntax for describing the import file. It should not include any business logic or special constructors, or backing fields. Keep it as simple as possible.
Then you can have your MVC-generated Merchant class which is separate. Its purpose is to describe the merchant as required by the MVC framework, with foreign keys, ids, whatever.
Then you use a FileHelperEngine<MerchantFileSpec> to read the records into an array and map it to an enumerable of Merchant (via Linq or a library like AutoMapper).
Something like:
/// Your MVC-generated class. Add methods, getters, setters, whatever.
/// FileHelpers doesn't use this class.
class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
/// This is the class FileHelpers will use
/// This class describes the CSV file only. Stick to whatever
/// syntax conventions are required by FileHelpers.
[DelimitedRecord(";")]
class ProductMerchantFileSpec
{
[FieldQuoted(QuoteMode.OptionalForRead)]
public long Id;
[FieldQuoted(QuoteMode.OptionalForRead)]
public string Name;
[FieldQuoted(QuoteMode.OptionalForRead)]
// Handle non-US formats such as , decimal points
// convert from inches to centimetres?
// you get the idea...
[FieldConverter(MyCustomizedCategoryConverter)] // you get the idea
public int Category;
}
class Program
{
static void Main(string[] args)
{
var engine = new FileHelperEngine<ProductMerchantFileSpec>();
var productMerchantRecords = engine.ReadFile(filePath);
var productMerchants = productMerchantRecords
.Select(x => new Merchant() { Id = x.Id, Name = x.Name, Category = x.Category });
}
}
I received this error specifically because my object (i.e. Merchant) was missing a column that existed in the source file. I was able to work around the issue prior to realizing the missing column by adding a new property to my object class public string[] MyProperty { get; set; }. This work-around help me realize a column was missing.
i.e..
public partial class Merchant
{
public long id { get; set; }
..
..
..
public string[] MyProperty { get; set; }
}

Reflection in WinRt getting parameter of an inherited class

I got two classes, and i want to use reflection.
public Class A
{
public string aa { get; set; }
public string bb { get; set; }
...
}
public Class B: A {}
when i try to get the Property of an B object i got no properties;
TypeInfo b = typeof(B).GetTypeInfo();
IEnumerable<PropertyInfo> pList = b.DeclaredProperties;
pList is always null, maybe because i used "DeclaredProperties" instead of GetProproperties(), but in winRt i can't use it.
i've read this solution How to get properties of a class in WinRT but i can't use var properties = this.GetType().GetTypeInfo().GetRuntimeProperties(); because GetRuntimeProperties() is not recognized
Solution Found, doubts still remain
To get the property of an inherited class i need to get the RuntimeProperties in this way
IEnumerable<PropertyInfo> pList = typeof(B).GetRuntimeProperties();
ignoring the PropertyInfo, and it works too if i try to get the property of an A object
What are the differences between getType().GetTypeInfo() and getType().GetRuntimeProperties() while i'm reading the property of an A object?
public string aa;
public string bb;
These are not properties. Properties are defined such as:
public string Aa
{
get;
set;
}
For more information, look up the official documentation on MSDN.
Once you've made the corrections to your classes A and B, you will be able to use :
var classProperties = typeof(B).GetTypeInfo().DeclaredProperties;
For properties defined within the class B, and :
var allProperties = typeof(B).GetRuntimeProperties();
For properties that are defined within the class and in its inheritance tree; i.e. properties that are actually accessible at runtime (therefore the name of the method).
If you don't want to change the public fields to properties (but you definitely should), use the GetRuntimeFields method on typeof(B) and DeclaredMembers on typeof(B).GetTypeInfo() for similar behaviour.

Check if instance of class is annotated with attribute

I have a class (Application) that has multiple properties of the type of another custom class (Employment). I would like to validate that Employment class conditionally based on whether the property of the Application class is marked with [Required].
From what I've found, I think I should be utilizing the IValidatableObject interface for Employment. The problem is that I'm not sure how to use reflection (or something else maybe) to check if this instance of the class is annotated with the [Required] attribute to determine whether to validate it or not.
Maybe this isn't even possible. I initially set up two classes for the Employment class: Employment and EmploymentRequired. Only the latter had the validation attributes on its properties. It works, but I'd like to just have one class to use if possible.
public class Application
{
[Required]
public Employment Employer1 { get; set; }
public Employment Employer2 { get; set; }
}
public class Employment : IValidatableObject
{
[Required]
public string EmployerName { get; set; }
[Required]
public string JobTitle { get; set; }
public string Phone { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
var t = this.GetType();
//var pi = t.GetProperty("Id");
//var isRequired = Attribute.IsDefined(pi, typeof(RequiredAttribute));
//how can I get the attributes of this property in Application class?
if (isRequired)
{
Validator.TryValidateProperty(this.EmployerName,
new ValidationContext(this, null, null) { MemberName = "EmployerName" }, results);
Validator.TryValidateProperty(this.JobTitle,
new ValidationContext(this, null, null) { MemberName = "JobTitle" }, results);
}
return results;
}
}
You should be able to check for the required attribute using Attribute.IsDefined.
http://msdn.microsoft.com/en-us/library/system.attribute.isdefined.aspx
Seems like you can't do this, because using reflection you can't get parent object/class that references your current instance and all the more so reference property information.
EDIT: Maybe you can make Employment type Generic with required and non required validation modes?
I think you are searching for the Attribute.IsDefined method. You would have to first obtain the reference to the field itself, and then validate the presence of the attribute. Like the following (adapted from the example at MSDN):
// Get the class type (you can also get it directly from an instance)
Type clsType = typeof(Application);
// Get the FieldInfo object
FieldInfo fInfo = clsType.GetField("Employer1");
// See if the Required attribute is defined for the field
bool isRequired = Attribute.IsDefined(fInfo , typeof(RequiredAttribute));
Since what I'm trying to do doesn't seem to be possible exactly, I found a different way to do it, based on the suggestion of #user1578874. I added an IsRequired property to Employment and used MVC Foolproof Validation to mark those properties as [RequiredIf("IsRequired")]. Seems to be the cleanest solution.

Categories