Problem accessing custom resources

Problem accessing custom resources

Old forum URL: forums.lhotka.net/forums/t/2745.aspx


david.wendelken posted on Friday, April 20, 2007

I've been building a custom validation rules library.  Like CommonRules, the rule error messages go into the Properties.Resources file in its project.

One of the components of this validation rules library are PublicRuleInfo and PublicRuleInfoList classes.  They take the list of rules for an object, look up the error message in the Resources file, and print a list of all rules for the object.  Works great for the standard rules.  These objects are instantiated in my custom subclass of BusinessBase.

Now it's time to add custom rules to an object.  Custom rules are one-off solutions and should not be coded in the StdRules library, they should be coded in the relevant object in the relevant project.  Of course, that custom rule's error message would also be placed in that project's Properties.Resources file.

Here comes the rub!

PublicRuleInfo has no way (at present) to know where the error message for the custom rule is to be found, if it's not in it's own Properties.Resources file.

Any ideas how I can pass along a reference to the right Properties.Resources file?  Or some other way to make it work?

 

Bayu replied on Saturday, April 21, 2007

Your explanation immediately triggers the corner in my brain that says 'Chain of Responsibility'. ;-)

This pattern 'bubbles' the responsibility from your central library to external providers. The way I see it is that the custom rules in your external libraries simply hook into the shared/global RuleManager. The interface for these providers also declares a GetResourceString or GetDescriptionMessage or so which your providers implement.

The 'chain' could be that if your provider returns a non-empty string, then this string is used and otherwise a default message is used. For inspiration think of the WinForms event-model. When your mouse hovers over a button, it in fact hovers over an entire stack of controls (the form, the panel that is on the form and contains the button and the button itself). The 'top-most' control that chooses to handle the event actually gets to handle it, despite of parenting control which may also define a handler. This is a chain of responsibility, the responsibility is deferred to the top-most control that subscribed to the event.

Am I somewhere on-track? ;-)
Bayu

david.wendelken replied on Monday, April 23, 2007

Bayu:
Your explanation immediately triggers the corner in my brain that says 'Chain of Responsibility'. ;-)

This pattern 'bubbles' the responsibility from your central library to external providers. The way I see it is that the custom rules in your external libraries simply hook into the shared/global RuleManager. The interface for these providers also declares a GetResourceString or GetDescriptionMessage or so which your providers implement.

The 'chain' could be that if your provider returns a non-empty string, then this string is used and otherwise a default message is used. For inspiration think of the WinForms event-model. When your mouse hovers over a button, it in fact hovers over an entire stack of controls (the form, the panel that is on the form and contains the button and the button itself). The 'top-most' control that chooses to handle the event actually gets to handle it, despite of parenting control which may also define a handler. This is a chain of responsibility, the responsibility is deferred to the top-most control that subscribed to the event.

Am I somewhere on-track? ;-)
Bayu

Bayu,

Here's the problem with that. 

In Csla, when a rule is broken, the rule code itself is responsible for saying what it's error message is.   This makes it very easy for it to look in it's own Properties.Resources to get an error message string.

But, I am not dealing with a broken rule, I am just listing all the rules for the object.  The data that I am working from is supplied by the framework class ValidationRules using the method GetRuleDescriptions().  The data is supplied as a collection of strings in this format:

rule://ruleName/propertyName?paramName1=paramValue1&paramName2=paramValue2

So, the actual rule code is not being asked to supply a text explanation of the rule.  (Besides, the way the sample rules are coded, they don't respond with error messages unless there is an error.)

So, basically, I have to to reconstruct what the message would be, from outside the rule code itself. 

The data above does not identify where the rule code is located, only that it exists. 

Now, I can deduce it is in one of three places, but I'm not sure how the program can... :) 

Logically, the rule would either be in the business object itself (i.e. Payment), a standard rules class within the same project (i.e., AccountsPayableCommonRules), or a generic rules class that is shared by many applications (i.e. CommonRules).

What I haven't figured out is how to find that "address info" programmatically.  

 

david.wendelken replied on Monday, April 23, 2007

david.wendelken:

What I haven't figured out is how to find that "address info" programmatically.  

Getting closer!  I implemented the list of Rules as a public property of RuleBusinessBase, which in turn subclasses BusinessBase.  My business object, of course, subclasses RuleBusinessBase.

The Rules property returns a PublicRuleInfoList object, which contains PublicRuleInfo objects.

The problem was that PublicRuleInfo only contained data from the rule: url-style string supplied by the framework.  It had no knowledge about the object it belonged to.

I've discovered that the "this"  keyword (C#), when queried in the RuleBusinessBase class, actually contains data about the business object that subclasses it!  Yeah!  That means I can supply PublicRuleInfoList with sufficient information to make logical deductions as to where to find the resources file.

Now all I have to do is figure out how to parse the data I've found, and how to get the desired resource via reflection.

david.wendelken replied on Monday, April 23, 2007

david.wendelken:
david.wendelken:

What I haven't figured out is how to find that "address info" programmatically.  

Getting closer!  I implemented the list of Rules as a public property of RuleBusinessBase, which in turn subclasses BusinessBase.  My business object, of course, subclasses RuleBusinessBase.

The Rules property returns a PublicRuleInfoList object, which contains PublicRuleInfo objects.

The problem was that PublicRuleInfo only contained data from the rule: url-style string supplied by the framework.  It had no knowledge about the object it belonged to.

I've discovered that the "this"  keyword (C#), when queried in the RuleBusinessBase class, actually contains data about the business object that subclasses it!  Yeah!  That means I can supply PublicRuleInfoList with sufficient information to make logical deductions as to where to find the resources file.

Now all I have to do is figure out how to parse the data I've found, and how to get the desired resource via reflection.

Well, not so close after all.  Haven't figured out the necessary syntax yet. :(

I added a new parameter to the PublicRuleInfoList factory method.  It is the actual business object the list of rules belongs to.  I exposed it as a property that it's child objects (PublicRuleInfo)could access and called it BusinessObject.

I added a new parameter to the PublicRuleInfo constructor, which is a reference to the PublicRuleInfoList the PublicRuleInfo object is in, and called the private variable it populates _parentList.

Maybe there's a better way, but the above method is simple and seems to work.

However, once I  try to make use of the above information in order to determine which resource file to load, I get a problem.  Basically, the section below is not working.

// Find the type of business object it is.

Type
businessObjectType = _parentList.BusinessObject.GetType();

// Try the business object's assembly resource file first. If that fails, we will try the std rule assembly.
// Assume for now that all resource files have the default name of Resources.resx.

ResourceManager
res = new ResourceManager("Resources", businessObjectType.Assembly);

try
{
    _ruleDescription = res.GetString(
"rule" + _ruleName);
}
catch (System.Resources.MissingManifestResourceException ex)
{
    // Darn!!! All calls to res.GetString come here regardless of whether the values are in the resources file or not.
}

Bayu replied on Monday, April 23, 2007

Wow ....

I am sorry, but somewhere you lost me. :-S

Your last post refers to an exception of which I guess it could have to do with not specifying the root-namespace of your project, but this is a WILD guess.

Nonetheless, a colleague of mine uses this to load an embedded image resource:

        Dim strNamespace As String = My.Application.ApplicationContext.MainForm.GetType.Namespace
        Dim strResourceName As String = strNamespace & "." & LCase(strName) & ".gif"

        Dim oIcon As Object = System.Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream(strResourceName)

        If Not oIcon Is Nothing Then
            Return New Bitmap(System.Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream(strResourceName))
        Else
            Return Nothing
        End If


Please not that I am officially off-track. My knowledge simply does not extend into the required level of detail on validation rules. Sorry. ;-)

Bayu

david.wendelken replied on Monday, April 23, 2007

Thanks, Bayu.

I've got a partial work-around using the C# version of Utilities.CallByName that Rocky ported.  In another thread, he suggested hooking in the VB version, which is a built-in feature library in VB.

I'm going to give that a try.

david.wendelken replied on Tuesday, April 24, 2007

Forget all the rules infrastructure, here's my problem in a nutshell.

I have resource strings in a resources file in a common code library.  Those work for 90+% of the business cases.

However, occasionally I need additional resource strings from other application-specific code libraries. 

I have a generic code routine in my common code library that needs to look up the resource strings from the appropriate resource file, be it in the common code library or in a given application-specific code library.

My generic code routine knows which assembly to look into.  It knows what resource file to look into.  It knows what string in the resource file to look up.

I cannot figure out a workable syntax that actually looks it up and gives the answer back to me. :(

 

 

 

Bayu replied on Tuesday, April 24, 2007

Ah,

Thanks for pulling me back on your track! ;-)

This routine of yours, have you tried to make it load settings from a local resource file? Does it only fail for resource files from app-specific, hence external, resources or does it fail for any resource file (including local ones)?

Bayu


EDIT:

This post is obsolete.

Just noticed your other thread where you are more into the specifics. I'll keep an eye on that one now.
I suggest we discontinue this thread for now.

ajj3085 replied on Tuesday, April 24, 2007

Dave,

Is there a reason the application assembly can't use a subclass of RuleArgs to pass a custom string? You could even code  the common library to use a string from its resource file if the application library doesn't pass a value.

It seems that would be easier then all this work you're going through..

Just a thought.

Andy

david.wendelken replied on Tuesday, April 24, 2007

ajj3085:

Dave,

Is there a reason the application assembly can't use a subclass of RuleArgs to pass a custom string? You could even code  the common library to use a string from its resource file if the application library doesn't pass a value.

It seems that would be easier then all this work you're going through..

Just a thought.

Andy

Rule error strings are like this:

{rulePropertyName} must not be more than {maxLength} characters.

I could look up that string when the object loads the rule and pass it in as a parameter.
The object certainly has easy access to the Properties.Resources file at that point in time.

In this case, when I call ValidationRules.AddRule, I mention the name of the property the rule is attached to ( which becomes rulePropertyName ) and an additional parameter whose property name is maxLength.  This is standard Csla behavior from the point of view of someone trained "by the book" to add a pre-existing validation rule..

If I have to pass in the base error string as an additional parameter, every programmer who adds custom rules to a class can no longer follow the book, they have to follow my custom process instead.  They have to know to look up and pass in the error string above, and they have to do it using a property name that is standardized - otherwise I won't know it's the base message to start with before I do substitutions on the error string.  That's a training cost I would prefer to avoid.

Plus, strings take up memory.  If there are lots of per-instance rules in the application, memory usage requirements would go way up. 

The way I'm trying to code does require one difference in programmer behaviour from "the book".  Error message strings look like this:

{rulePropertyName} must not be more than {maxLength} characters.

instead of this:

{0} must not be more than {1} characters.

I find the added clarity and built-in documentation in the error strings well worth the added training time, as other than {rulePropertyName} (which is deduced), all the other {} tags match the parameter names exactly.

In addition, if someone wanted to switch from Csla.Validation.CommonRules to CslaSrd.Validation.StdRules, they would have a very simple job to do.  Monkey work, just adding the reference and the using clauses, plus a global substitute and replace on CommonRules to StdRules.

Copyright (c) Marimer LLC