Message template using custom resource provider

Topics: Validation Application Block
Jan 9, 2009 at 7:14 PM
Ok, the System.Web.Compilation.IResourceProvider gives the ability to override the default resource provider with one of your own.  So this allows you to load resource values from a database instead of a RESX file.

That is great for an ASP.NET web application.  But what about a Windows Forms application? or a console application? 

Surely you don't use the System.Web namespace do you?  I can't find an IResourceProvider class equivalent for other applications types?  Is it possible to override the default provider for a windows app?

What if the app was a self hosted WCF application?  And I wanted to use the VAB but instead of RESX files, I wanted to pull resources from the database?

What am I missing?

Jan 12, 2009 at 9:34 AM
Hi,

Please see this post: http://www.codeplex.com/entlib/Thread/View.aspx?ThreadId=39720


Hope this helps.

Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com
Jan 13, 2009 at 10:19 PM
Thanks.  So I gather from this that the only way to do this would be to create our own custom validators, that inherit the existing ones?

Like this?

public class LocalizedStringLengthValidator : StringLengthValidator
{
        protected override string GetMessage(object objectToValidate, string key)
        {
            return string.Format(
                CultureInfo.CurrentCulture,
                this.MessageTemplate, //EDIT THIS TO INSERT MESSAGE PULLED FROM DATABASE
                objectToValidate,
                key,
                this.Tag,
                this.rangeChecker.LowerBound,
                this.rangeChecker.LowerBoundType,
                this.rangeChecker.UpperBound,
                this.rangeChecker.UpperBoundType);
        }
}

And this would need to be done for each Validator type we use?  Is this close? 

I could not find where the messageTemplateResourceName and messageTemplateResourceType values are used.

Thanks.

Jan 14, 2009 at 8:37 AM
Hi,

Here is my sample CustomValidator:

[

ConfigurationElementType(typeof(CustomValidatorData))]
public class CustomStringLengthValidator : StringLengthValidator
{
public CustomStringLengthValidator(int lowerBound, RangeBoundaryType lowerBoundType, int upperBound, RangeBoundaryType upperBoundType) :
base(lowerBound, lowerBoundType, upperBound, upperBoundType){ }  

 

protected override string DefaultNegatedMessageTemplate
{
get
{
    //Process here the access to the Message that is from the database.
    string messageFromDB = GetMessageFromDatabase();
    return messageFromDB;
}
}

protected
override string DefaultNonNegatedMessageTemplate
{
get
{
    //Process here the access to the Message that is from the database.
    string messageFromDB = GetMessageFromDatabase();
    return messageFromDB;

}
}
}



Hope this Helps.

Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com

 

 

Jan 14, 2009 at 9:58 AM
Just to add, the messageTemplateResourceType refers to the type that contains the localized resources, the .resx file.  messageTemplateResourceName is used together with the messageTemplateResourceType.  It corresponds to the name or key of the resource string.


Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.

entlib.support@avanade.com  

 
Jan 14, 2009 at 12:47 PM
This is a great start.  Thanks.  It is too bad we have to subclass these just to change the localized message source.

Another question..... is the messageTemplateResourceName and messageTemplateResourceType values available from within the DefaultNegatedMessageTemplate and DefaultNonNegatedMessageTemplate properties?  Are their values stored internally within the StringLengthValidator class or one of the superclasses?
Jan 14, 2009 at 1:10 PM
Hi,

No they are not.


Valiant Dudan
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com
Jan 14, 2009 at 2:06 PM
Really.... so how can I determine which string resource to load if I can't set and then access the resource key values?
Jan 14, 2009 at 5:27 PM
Ok, playing around with this here... I also had to add this constructor to your sample to avoid a runtime error:

        //constructor used for validators defined in app.config
        public CustomStringLengthValidator(NameValueCollection attributes) : base(0)
        {
        }

The base(0) obviously hardcodes the upperBound to 0 instead of passing in the config value from app.config, such as:

Int32.Parse(attributes.Get("upperBound"))

doing this:  base(Int32.Parse(attributes.Get("Divisor"))) looks a bit messy... and also doesn't yet address the other params that could be passed in the attributes collection.

What is the cleanest way for a custom validator (that inherits directly from an existing validator) to handle configuration from the app.config?

thx



Jan 14, 2009 at 8:45 PM
After more playing around, the constructor I added now looks like this:

       public CustomStringLengthValidator(NameValueCollection attributes)
            : base(Int32.Parse(attributes.Get("lowerBound")),
                    (RangeBoundaryType)Enum.Parse(typeof(RangeBoundaryType), attributes.Get("lowerBoundType")),
                    Int32.Parse(attributes.Get("upperBound")),
                    (RangeBoundaryType)Enum.Parse(typeof(RangeBoundaryType), attributes.Get("upperBoundType")),
                    Convert.ToBoolean(attributes.Get("negated")))
        {
        }

This works.... but is ugly.  Not to mention the default error message provided if you exclude, say, the upperBoundType attribute from the configuration file... the error looks like this:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentNullException: Value cannot be null.
Parameter name: value

Not very intuitive at all.

I am guessing this is a bit more painful because I am inheriting from StringLengthValidator instead of just Validator.

Also, I have created my own CustomStringLengthValidatorAttribute class to go with this.  Of course I couldn't inherit from the existing StringLengthValidatorAttribute class because it is sealed.  I guess I have to do this so that I can introduce my own attribute property "resourceName", since the messageTemplateResourceName value is not accessible.

Does all of this sound right (and required)?  I want to make sure I'm not missing any shortcuts... especially if I am considering doing this for all of the validators.

thx



Jan 15, 2009 at 9:53 AM
You could inherit from the BaseValidationAttribute, it has the MessageTemplateResourceName and MessageTemplateResourceType property.  I don't get though why you need these two, you want to get your message from the database, right?  Regarding the constructor for the validator, you can't avoid having that code because the only way to pass the parameters to the validator is through the NameValueCollection parameters. 

You could also inherit from the ValueValidator but I think using the StringLengthValidator itself allows you to just use the existing implementation instead of writing them again. 


Sarah Urmeneta
Global Technology and Solutions
Avanade, Inc.
entlib.support@avanade.com  
Jan 15, 2009 at 12:50 PM
Thanks Sarah.

I'll check into the BaseValidationAttribute.  Although, I believe this would only get me access when the validators are defined through configuration and not with attributes.  Correct?

The reason I need access to MessageTemplateResourceName, or some alternative, is that I need to know what resource to load from the database.  I can't use property name because there could be multiple validators on each property.  I can't use property name and validator type because there could be multiple validators of the same type defined with different Rulesets.  Therefore, I need a string resource name that is unique per validator instance, per Ruleset.  At least that is my understanding now.

Regarding the inheritance from StringLengthValidator, I will go with that.  I just wanted to make sure the implementation was as clean as possible, because I will have to do the same for all of the other built-in validators in order to enable messages pulling from the database.

Jan 15, 2009 at 1:23 PM
Yeah, that's true.  Regarding the resourcename, I don't know of a way how would you restrict it to be unique per ruleset. If it need not be that restrictive, the easiest way would be to put it also in the NameValueCollection parameter.