Compile Time Type Assignment - c#

I'm working on a custom validation framework for my WPF/C# application.
What I'm looking to do is to retrieve strings from the resource file where the viewmodel is declared, but in the actual validation code it self. This particular string is the same resource used by label on the editing UI Form.
My code works fine with the following syntax -
[Required(TypeRes = typeof(Resources))]
public string RequiredStringWithDesc { get; set; }
But what I"m looking for is something that is syntacticly cleaner looking. I was trying to use
const Type LocalRes = typeof(Resources);
[Required(TypeRes = LocalRes)]
public string RequiredStringWithDesc { get; set; }
Any suggestions on a simpler syntax? The old c++ DEFINE statement here would work well.
FYI: the reasons for going to this much work has to do with how we are doing localization and UI construction.
EDIT To answer a couple of questions about why are we doing this?
We are going to be using the same string from the resource file to -
On the edit screen, this is the label to identify the field.
In the datamodel, if there is a validation error, we are using this to correctly label the problem in the log file.
In the Viewmodel, we are reusing this label in the validation error message to reinforce where the problem is to the user.
This is part of a real time inspection system and some of the failure modes relate directly back to these data fields. So we can easily get the correctly localized label to apply to run-time fault messages
The general concept is that this simplifies presenting consistent messages to the user while only creating things once. With regards to validation attributes (and this question), we need to be able to get the Resource file type to load the correct message.

Create a new attribute class which inherits from the RequiredAttribute and set default values.
public class LocalizedRequiredAttribute : RequiredAttribute {
public LocalizedRequiredAttribute() { /* TypeDef = typeof(Resources);*/ }
}
public class MyModel {
[LocalizedRequired]
public string RequiredStringWithDesc { get; set; }
}

Related

Custom field validation error in ASP.NET Core 5.0 MVC

I somewhat can't wrap my head around it.
Lets assume simplest model possible:
public class Model
{
[Required]
[MaxLength(128)]
public string Name {get;set;}
}
If you now use it in form and declare validation, it will work. But default messages are not the most pleasant for average user (Field must be an array of length X etc).
And now comes my question, how to create custom validation error? I have seen one useful topic that I can't find anymore, but they were overriding some function and there was no info provided how to call it.
I'm mostly interested in MaxLength, because for Required you can just set Display, which won't work for MaxLength.
If you only want to change the default message try it with
[Required]
[MaxLength(128, ErrorMessage = "YourCustomMessageString")]
public string Name {get;set;}

Custom c# code in a Report

I am populating a report using an Object. Everything works fine, if my members are string, however, I have a few members, which are List<string>.
When I add these fields to the report (in designer, visual studio 2012) and run the report, they show as
'#Error'
Viewing the properties of the report, I can see that there is a 'custom code' tab, for which I've entered the following method to convert the list of strings into a array of string.
public string[] GetListItems(List<string> intList)
{
var s = intList.ToArray();
return s;
}
Then when I try and replace the field with an expression and enter the following:
=Join(Code.GetListItems(Fields!Aka.Value),",")
and then run the report, VS fails to build with the following:
Error 2 The Value expression for the textrun ‘Aka.Paragraphs[0].TextRuns[0]’ contains an error: [BC30456] 'GetListItems' is not a member of 'ReportExprHostImpl.CustomCodeProxy'.
Error 1 There is an error on line 0 of custom code: [BC30183] Keyword is not valid as an identifier.
Can anyone shed any light why this is?
That is C# code. Directly in the report you can only use Visual Basic. C# is avaliable as an external DLL.
For those who can't create go through the difficulties of implementing a third-party DLL. A simpler; but, a messier solution is as follows.
To summarize solution, you can create properties that returns a value that the reports can easily work with.
In my scenario, I had 2 classes:
public class Parent {
public Child { get; set; }
}
public class Child {
public string Name { get; set; }
}
The type that I was binding to my Report was Parent; however, I needed to access the Name property inside Child. In otherwords I needed to do:
Parent.Child.Name
This where I ran into the same problem that the OP ran into. Due to the scope of my project (a college project), I can't bother with third-party dlls or writing my own dll...
So what I did is add more properties to the Parent class.
public class Parent {
public Child { get; set; }
public string ChildName { get => Child.Name; }
}
public class Child {
public string Name { get; set; }
}
By adding an additional property to Parent that has an type that Reports can work with, I can now easily access the Child's Name through my Parentclass.
Of course this is messy; but, I'm in a situation where I can't go with the more complicated/time consuming dlls solution.
I just hope my instructors will allow this solution without penalty...
If you just created a report and showing an error code like
BC30016 labels are not valid outside of the method.
The reson in my case was that i have insert the random numerical value n the code property of a report1.rdlc
Hence to solve this type of error, goto report properties and remove the value from the code.

Rename Generic Class in WebAPI

I have a Generic Envelope class that i use as the common return object for the WebAPI as follows:
public class ApiEnvelope<T>
{
public bool Success { get; set; }
public Error Error { get; set; }
public Data<T> Data { get; set; }
}
Then I construct a HttpResponseMessage using:
Request.CreateResponse<ApiEnvelope<whatever>>(HttpStatusCude.OK, model);
The problem i have is that i would like the xml to be somewhat standard however the root name of the xml being returned is not standard and is coming through as ApiEnvelopeOfwhatever.
My question is how can i get the root name to be ApiEnvelope regardless of the type?
With generic class you got no chance, remove generic specification and set Data propert type to object.
I had a similar question and I got a decent answer, (I know this is old but it was a good answer): How to resolve Swashbuckle/Swagger -UI model naming issue for generics?
Now this is only part of the solution for your question, so you should look at Github repo: MakeGenericAgain. Use that to "regeneric" the resultant generated code, (big heads up: if you use this on the entire code and not just names of types, you will have a mess if you have properties name things like "NumberOfUnits" because that becomes "Number").
Sidenote:
If you want to "level up" your skills here, then use Rolsyn's SyntaxWalker to apply the renaming, and at the same time "cleanup" duplicated classes, as many design their REST-APIs with few shared "models" so a User and a Company might hace identical "Address" -properties based on identically shaped classes but it they are defined twice your NSwag -generated code will have Address and Address2, however using Roslyn, you can identify these ande rewrite the code to give a "perfect result".

Is it possible to use a variable for the `[Display(Name="Something")]` data annotation in MVC3 (C#)

not sure why, but the data annotation in MVC3 insist on having constant values, which i just can't understand for things like error messages and display names. I love these annotations, they are so easy to use and so powerful, but what if you need to support multiple languages?
Imagine i have the following model:
public class Person
{
public string First_Name { get; set; }
}
If i dont change anything and use the CRUD views that MVC3 will build for me I get a label next to the text fields that says "First_Name", so i add the data annotation to change the display name like so:
public class Person
{
[Display(Name="First name")]
public string First_Name { get; set; }
}
Which works just fine. But i want to supply a different display name depending on the users language, using a function i made previously GetDisplayName(string ToGet, string Language) which just returns the string i am interested in, but if i change my data annotation to this:
public class Person
{
[Display(Name=GetDisplayName("First_Name", "English"))]
public string First_Name { get; set; }
}
Then i get a compiler error telling me that the annotation requires a constant values, WHY????
Does anyone know a way to accomplish what i am trying to do? Thanks
UPDATE
Ok, it appears that the best way to do this is with .resx resource files as per several answers below and those in other posts. which works great for the most part.
Does anyone know how i can request a resource with a variable name? not in the context of data attributes this time, but just in controllers and views.
Basically at the moment i am getting at the resources with #Resources.SomeKey but i would like to be able to use that within a function in a #Resources["SomeOtherKey"] where SomeOtherKey is a dynamically generated string.
If you have your translated property names in Resource Files, that is supported out of the box... no need to extend the framework.
Just use:
[Display(Name = "Resource_Key", ResourceType = typeof(DataFieldLabels))]
public string myProperty { get; set; }
Where the "Resource_Key" is the key in your resource files... and the "DataFieldLabels" is the name of your base resource file.
EDIT:
In order to access resource files dynamically you can do:
ResourceSet resset = ResourceManager.GetResourceSet(culture, true, false);
var translated = resset.GetString("myToken")
DataAnnotations param values require constants, i.e. actual strings.
You can only assign constants.
You can do an extension and assign keyValues there and then the extension fetches from somewhere else, like a database or a resource file or a webservice or whatever.
You may want to check Hanselman's post regarding internationalization. Maybe it could help
[Display(Name = "strResourceString", ResourceType = typeof(Resourcefile.strResourceString))]

Validation Framework in .NET that can do edits between fields

From my experience many validation frameworks in .NET allow you to validate a single field at a time for doing things like ensuring a field is a postal code or email address for instance. I usually call these within-field edits.
In my project we often have to do between-field-edits though. For instance, if you have a class like this:
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
}
you might want to ensure that Max is greater than Min. You might also want to do some validation against an external object. For instance given you have a class like this:
public class Person
{
public string PostalCode { get; set; }
}
and for whatever reason you want to ensure that Postal Code exists in a database or a file provided to you. I have more complex examples like where a user provides a data dictionary and you want to validate your object against that data dictionary.
My question is: can we use any of the existing validation frameworks (TNValidate, NHibernate Validator) for .NET or do we need to use a rules engine or what?? How do you people in the real world deal with this situation? :-)
There's only one validation framework that I know well and that is Enterprise Library Validation Application Block, or VAB for short. I will answer your questions from the context of the VAB.
First question: Can you do state (between-field) validation in VAB?
Yes you can. There are multiple ways to do this. You can choose for the self validation mechanism, as follows:
[HasSelfValidation]
public class Range
{
public int Min { get; set; }
public int Max { get; set; }
[SelfValidation]
public void ValidateRange(ValidationResults results)
{
if (this.Max < this.Min)
{
results.AddResult(
new ValidationResult("Max less than min", this, "", "", null));
}
}
}
I must say I personally don't like this type of validations, especially when validating my domain entities, because I like to keep my validations separate from the validation logic (and keep my domain logic free from references to any validation framework). However, they need considerably less code than the alternative, which is writing a custom validator class. Here's an example:
[ConfigurationElementType(typeof(CustomValidatorData))]
public sealed class RangeValidator : Validator
{
public RangeValidator(NameValueCollection attributes)
: base(string.Empty, string.Empty) { }
protected override string DefaultMessageTemplate
{
get { throw new NotImplementedException(); }
}
protected override void DoValidate(object objectToValidate,
object currentTarget, string key, ValidationResults results)
{
Range range = (Range)currentTarget;
if (range.Max < range.Min)
{
this.LogValidationResult(results,
"Max less than min", currentTarget, key);
}
}
}
After writing this class you can hook this class up in your validation configuration file like this:
<validation>
<type name="Range" defaultRuleset="Default" assemblyName="[Range Assembly]">
<ruleset name="Default">
<validator type="[Namespace].RangeValidator, [Validator Assembly]"
name="Range Validator" />
</ruleset>
</type>
</validation>
Second question: How to do complex validations with possible interaction a database (with VAB).
The examples I give for the first question are also usable for this. You can use the same techniques: self validation and custom validator. Your scenario where you want to check a value in a database is actually a simple one, because the validity of your object is not based on its context. You can simply check the state of the object against the database. It gets more complicated when the context in which an object lives gets important (but it is possible with VAB). Imagine for instance that you want to write a validation that ensures that every customer, at a given moment in time, has no more than two unshipped orders. This not only means that you have to check the database, but perhaps new orders that are added or orders are deleted within that same context. This problem is not VAB specific, you will have the same problems with every framework you choose. I've written an article that describes the complexities we're facing with in these situations (read and shiver).
Third question: How do you people in the real world deal with this situation?
I do these types of validation with the VAB in production code. It works great, but VAB is not very easy to learn. Still, I love what we can do with VAB, and it will only get better when v5.0 comes out. When you want to learn it, start with reading the ValidationHOL.pdf document that you can found in the Hands-On Labs download.
I hope this helps.
I build custom validation controls when I need anything that's not included out of the box. The nice thing here is that these custom validators are re-usable and they can act on multiple fields. Here's an example I posted to CodeProject of an AtLeastOneOf validator that lets you require that at least one field in a group has a value:
http://www.codeproject.com/KB/validation/AtLeastOneOfValidator.aspx
The code included in the download should work as an easy to follow sample of how you could go about it. The downside here is that Validation controls included with ASP.Net don't often work well with asp.net-ajax.

Categories