will RegularExpressionAttribute and other validators on Automatic properties work? - c#

I'm using xsd2code for generating classes from schema. how ever I'm suspicious that auto properties can validate values or not?
because if I enable AutomaticProperties in xsd2code here is what I get for member with Regex restriction.
[System.Xml.Serialization.XmlAttributeAttribute(DataType="token", AttributeName="color")]
[System.ComponentModel.DataAnnotations.RegularExpressionAttribute("#[\\dA-F]{6}([\\dA-F][\\dA-F])?")]
public string Color { get; set; }
And when AutomaticProperties is disabled
[System.Xml.Serialization.XmlAttributeAttribute(DataType="token", AttributeName="color")]
[System.ComponentModel.DataAnnotations.RegularExpressionAttribute("#[\\dA-F]{6}([\\dA-F][\\dA-F])?")]
public string Color
{
get
{
return this._color;
}
set
{
System.ComponentModel.DataAnnotations.ValidationContext validatorPropContext = new System.ComponentModel.DataAnnotations.ValidationContext(this, null, null);
validatorPropContext.MemberName = "Color";
Validator.ValidateProperty(value, validatorPropContext);
this._color = value;
}
}
It seems these are not equivalent. so I think its a bug in xsd2code or maybe I'm misunderstanding something. whats the purpose of second generated code?
I thought RegularExpressionAttribute would also validate automatic properties too.

Related

Correct use of properties and auto implemented properties?

I'm only at chapter 5 in "Essential C#" and not sure if i understand the difference correctly. I tried to make the model below to test one instance of everything in the properties chapter - and it works - but is the example acceptable use of the two ways one can implement properties or are there better ways?
using MarkdownSharp; // StackOverflow's md processor
public class Article
{
public string Headline { get; set; }
public string Content
{
get
{
return _content;
}
set
{
var md = new Markdown();
var html = md.Transform(value);
_content = html;
}
}
private string _content;
public DateTime Published { get; set; } = DateTime.Now;
}
This question may be better suited for codereview.stackexchange, although it's perhaps too tiny a snippet and to vague a question for that.
Personally, I shy away from magic properties that act in surprising ways. It tends to make for APIs that can be hard to use because they are surprising, even if they are somehow “clever” under the hood. You have a property where you set a different value than the one you get out. One thing where this can break would be the += operator, which suddenly would work in very weird ways with your Content property.
I'd probably go with something like
public class Article
{
private string content;
private string renderedContent;
public string Headline { get; set; }
public string Content
{
get { return content; }
set
{
content = value;
renderedContent = null; // reset cached rendered content
}
}
public string RenderedContent
{
get
{
if (renderedContent == null)
{
renderedContent = new Markdown().Transform(content);
}
return renderedContent;
}
}
public DateTime Published { get; set; } = DateTime.Now;
}
As for whether to use field-backed properties, or auto-properties, or computed properties ... that's up to you to decide based on what the property is supposed to do. Auto-properties are fine for simply storing and retrieving a value, e.g. Published or Headline here. You need the explicit backing field as soon as you do something more than just reading or writing it in the getter and setter, as shown here in Content. RenderedContent could be just a computed property, but I chose to cache the value after initial conversion because you kinda do the same. This pattern here doesn't convert the Markdown until it's actually needed, though.

InvalidDataContractException on WP7 application on class that is serializable

I'm having an strange error when trying to save an object into isolated storage. I have a class that has some properties, here's the code :
[DataContract]
public class ExerciseStatistic
{
[XmlIgnore]
public int CorrectAnswers
{
get
{
return Attempts.Where(a => a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int IncorrectAnswers
{
get
{
return Attempts.Where(a => !a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int AnswerAttempts
{
get { return Attempts.Count; }
}
public List<AnswerAttempt> Attempts { get; set; }
public ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>();
}
}
public class AnswerAttempt
{
public DateTime AttemptDate { get; set; }
public string AttemptTargetName { get; set; }
public string AttemptName { get; set; }
public bool AttemptAnswerIsCorrect { get; set; }
}
However, when trying to save it with this sentence :
IsolatedStorageSettings.ApplicationSettings["a"] = new ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>()
{
new AnswerAttempt()
{
AttemptAnswerIsCorrect = true,
AttemptDate = DateTime.Now,
AttemptName = "lala",
AttemptTargetName = "lala2"
},
new AnswerAttempt()
{
AttemptAnswerIsCorrect = false,
AttemptDate = DateTime.Now,
AttemptName = "lalab",
AttemptTargetName = "lalab2"
}
}
};
I'm getting an exception like this one (i changed a bit the signature of the code with fake names, but for the example it serves its purpose) :
Type 'XX.Model.FirstClass.SecondClass' cannot be serialized. Consider
marking it with the DataContractAttribute attribute, and marking all
of its members you want serialized with the DataMemberAttribute
attribute.
I don't understand why the serializer is trying to serialize an object of my model (which is not serializable) when the class that I'm giving it doesn't have any references to that kind of type... what am i missing? -> nope, i don't want to add datacontract attributes to classes that i don't need and am not planning to serialize, so please don't answer with this :)
You might experience this problem if you work through the reference procedure in "Walkthrough: Consuming OData with MVVM for Windows Phone" at http://msdn.microsoft.com/en-us/library/hh394007(v=VS.92).aspx
When you get to the point where you call :
Return DataServiceState.Serialize(_context, collections);
You might get an InvalidDataContractException with the message:
Type 'DataBoundApp1.Northwind.NorthwindEntities' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Thanks to the answer by Daniel Perez, I was able to resolve this problem and I am documenting my steps to clarify the solution for others:
Show hidden files in Solution Explorer
Open the file "Reference.cs" (under your Service Reference, expand
Reference.datasvcmap)
If your Data Service Context class is missing the [DataContract]
attribute, add it as shown here:
.
namespace OCC.WindowsPhone.OrlandoCodeCampService
{
[DataContract] <--- I ADDED THIS
public partial class OrlandoCodeCampEntities : global::System.Data.Services.Client.DataServiceContext
{..}
Once I added the DataContract attribute, the problem went away!
It seems to me you try to exclude properties from serialization by using XmlIgnore.
From the documentation:
You can opt out members from serialization by using the IgnoreDataMemberAttribute.
so try using IgnoreDataMemberAttribute instead of XmlIgnore to opt out members from serialization.
I also had some troubles with DataContract in the very same situation as you, therefore I reverted to plain old XML serialization to strings, which i then stored in isolated storage. This also eases debugging.
I don't think that this is a proper answer, but it's what i had to do in order to fix it.
After changing some more the code, i realised that this was failing EVEN if I wasn't saving anything to the isolated storage. Just declaring a DataContract attribute on the type made the error arise. I must think that WP7's framework at some point is parsing all classes that have this attribute, and for some strange and obscure reason (which i can't find) it's looking for them in other classes as well. I added the DataContract attributes in the classes that the framework is complaning about, and also some KnownType attributes as well, and everything started to run smoothly... weird weird... if someone can shed some light into this, i'd be happy (i hate it when i solve a problem but without knowing the exact cause)

Attributes of properties in MetadataType are ignored by EntLib Validation

It's an EntLib-Validator-issue again. I'm playing with EntLib 5.0 in C# and .Net 4.0 on XP pro.
I have some business objects (partial classes) generated by T4 templates. So I decided to put their validation attributes in buddy-classes by using MetadataTypeAttribute as definitely recommended by the documentation of entLib 5.0 (msdn).
But the Validator object I get from the ValidatorFactory doesn't know about the validation attributes, defined in the metadata-class.
The business object is defined like this:
[MetadataType(typeof(PatientMetadata))]
public partial class Patient
{
private string _Name;
private int _DiagnosisCount;
public int DiagnosisCount
{
get
{
return _DiagnosisCount;
}
set
{
if (value != _DiagnosisCount)
{
_DiagnosisCount = value;
}
}
}
public string Name
{
get
{
return _Name;
}
set
{
if (value != _Name)
{
_Name = value;
}
}
}
}
And the metadata class like this, according to documentation:
public class PatientMetadata
{
[RangeValidator(4)]
public int DiagnosisCount { get; set; }
[StringLengthValidator(64, ErrorMessage = "Name must not exceed 64 chars.")]
public string Name { get; set; }
}
If I know try to do validation this way:
var factory = ValidationFactory.DefaultCompositeValidatorFactory;
var validator = factory.CreateValidator<Patient>();
...then watching into validator (during debugging) already says, that it's just an AndCompositeValidator without any children validators.
Again, if I put the validation attributes right in the Patient class, it works perfectly.
By now, I have no real idea, what I'm missing here, since I think doing everything according to the docs.
Thanks in advance to you guys!
The property names of the metadata class must match the property names of the main class.
In your case your metadata class should look like:
public class PatientMetadata
{
[RangeValidator(0, RangeBoundaryType.Inclusive, 10, RangeBoundaryType.Ignore)]
public int DiagnosisCount { get; set; }
[StringLengthValidator(6, ErrorMessage = "Name must not exceed 6 chars.")]
public string Name { get; set; }
}
Also, the docs indicate the accepted approach is to declare all return types as object. However, the docs also talk about using properties but in their example use fields so take it under advisement. :)

Why does StringValidator always fail for custom configuration section?

I have created a custom configuration section in a c# class library by inheriting from ConfigurationSection. I reference the class library in my web application (also c#, ASP.NET), fill in the appropriate attributes and everything works great. The problem starts when I start adding validators.
For example, this property:
[ConfigurationProperty("appCode", IsRequired = true)]
public string ApplicationCode
{
get
{
return (string)base["appCode"];
}
set
{
base["appCode"] = value;
}
}
As is it works fine, but as soon as I add this:
[StringValidator(MinLength=1)]
It bombs with the following error:
The value for the property 'appCode' is not valid. The error is: The string must be at least 1 characters long.
I get this error even though a valid appCode value is in my web.config file. If I remove the validator it works perfectly. Does anyone know how to get around this?
I was able to work around this issue by using an explicit ConfigurationProperty as the key to my properties collection rather than a string, as per the following implementation:
public class AssemblyElement : ConfigurationElement
{
private static readonly ConfigurationProperty _propAssembly;
private static readonly ConfigurationPropertyCollection _properties;
static AssemblyElement()
{
_propAssembly = new ConfigurationProperty("assembly", typeof(string), null, null, new StringValidator(1), ConfigurationPropertyOptions.IsKey | ConfigurationPropertyOptions.IsRequired);
_properties = new ConfigurationPropertyCollection();
_properties.Add(_propAssembly);
}
internal AssemblyElement() { }
public AssemblyElement(string assemblyName)
{
this.Assembly = assemblyName;
}
[ConfigurationProperty("assembly", IsRequired = true, IsKey = true, DefaultValue = "")]
[StringValidator(MinLength = 1)]
public string Assembly
{
get { return (string)base[_propAssembly]; }
set { base[_propAssembly] = value; }
}
internal AssemblyName AssemblyName
{
get { return new AssemblyName(this.Assembly); }
}
protected override ConfigurationPropertyCollection Properties
{
get { return _properties; }
}
}
(This code is closely modeled after the code reflected from the AssemblyInfo configuration element class. I still wish I didn't have to duplicate my validations, but at least this code allows me to specify a blank default value while still requiring a value to be entered.)
Seems like the answer is indeed because they don't have a default value. Seems odd, so if someone has a better answer let me know and I'll accept theirs.
I had this problem for a while, then I realized that the validators are not for making attribute or elements required, they are for validating them.
To make a property required you need to use the IsRequired and ConfigrationPropertyOptions.IsRequired, e.g.
[ConfigurationProperty("casLogoutUrl", DefaultValue = null, IsRequired = true, Options = ConfigurationPropertyOptions.IsRequired)]
[StringValidator(MinLength=10)]
Or (if using the api)
ConfigurationProperty casLoginUrl = new ConfigurationProperty("casLoginUrl", typeof(string), null, null, new StringValidator(1), ConfigurationPropertyOptions.IsRequired);
Doing this, the Configuration framework will handle the property being required itself, and the validator handles validating what's in the value. Validators are not meant for making something required.
This also works on elements to make child elements required. E.g. if you are making a custom ConfigSection with child elements and need a child element to be required. However, if you make a CustomValidator that inherits from ConfigurationValidatorBase, you need to make use of ElementInformation.IsPresent, e.g.
public override void Validate(object value)
{
CredentialConfigurationElement element = (CredentialConfigurationElement)value;
if (!element.ElementInformation.IsPresent)
return; //IsRequired is handle by the framework, don't throw error here only throw an error if the element is present and it fails validation.
if (string.IsNullOrEmpty(element.UserName) || string.IsNullOrEmpty(element.Password))
throw new ConfigurationErrorsException("The restCredentials element is missing one or more required Attribute: userName or password.");
}
Long story short, you are missing the options part of your attribute to make it required and shouldn't use StringValidator(MinLength=1) to make it required. In fact StringValidator(MinLength=1) is completely redundant. If you make it required it's impossible for MinLength=1 to fail without the Required failing first because if it's present, it's guaranteed to be at least 1 character long.
Change your validator to
[ConfigurationProperty("appCode", IsRequired = true, Options=ConfigurationPropertyOptions.IsRequired)]
Then ditch the string validator.
The resolving of the StringValidator can be done by any one of the following:
Removing MinLength argument
Setting MinLength = 0
Removing the StringValidator Attribute
Adding DefaultValue to the ConfigurationProperty Attribute
The Ideal definition for the property is like:
[ConfigurationProperty("title", IsRequired = true, DefaultValue = "something")]
[StringValidator(InvalidCharacters = "~!##$%^&*()[]{}/;’\"|\\"
, MinLength = 1
, MaxLength = 256)]
public string Title
{
get { return this["title"] as string; }
set { this["title"] = value; }
}

How can I retrieve the instance of an attribute's associated object?

I'm writing a PropertiesMustMatch validation attribute that can take a string property name as a parameter. I'd like it to find the corresponding property by name on that object and do a basic equality comparison. What's the best way to access this through reflection?
Also, I checked out the Validation application block in the Enterprise Library and decided its PropertyComparisonValidator was way too intense for what we need.
UPDATE: For further clarification (to provide some context), the goal is simply validation that enforces field matching (e.g., password verification). We'd like it to work with property-level attribute data annotations that inherit from the ValidationAttribute class, if possible.
UPDATE: In case anyone is curious, I ended up solving the actual business problem through tweaking code provided as an answer to this question
You can't, basically. The code that checks the object for the presence of the attribute must also assume responsibility for telling any code which type/object it was looking at. You can't obtain any additional metadata from within an attribute.
You cannot do that. See also this question. Try to change the logic to work with the object, checking its attributes, not vice versa. You can also provide more information about your task, not just this narrow question.
You can something like this.
//target class
public class SomeClass{
[CustomRequired(ErrorMessage = "{0} is required", ProperytName = "DisplayName")]
public string Link { get; set; }
public string DisplayName { get; set; }
}
//custom attribute
public class CustomRequiredAttribute : RequiredAttribute, IClientValidatable
{
public string ProperytName { get; set; }
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var propertyValue = "Value";
var parentMetaData = ModelMetadataProviders.Current
.GetMetadataForProperties(context.Controller.ViewData.Model, context.Controller.ViewData.Model.GetType());
var property = parentMetaData.FirstOrDefault(p => p.PropertyName == ProperytName);
if (property != null)
propertyValue = property.Model.ToString();
yield return new ModelClientValidationRule
{
ErrorMessage = string.Format(ErrorMessage, propertyValue),
ValidationType = "required"
};
}
}

Categories

Resources