RuleDescription Class not serializable

RuleDescription Class not serializable

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


chrislw posted on Thursday, September 27, 2007

I am creating validation controls on the UI based on rules in the business object.  I have everything working, but I getting the rule descriptions and then putting them into the RuleDescription class.  I tried to store the RuleDescription class in ViewState, but it tells me that the class is not serializable. 

I was thinking that ValidationRules.GetRuleDescriptions should return a serializable List<RuleDescription> rather than string(). 

I realize this could break other implementations, but it seems silly to populate a string() and then the class.  Does anyone else have this desire or need or would be willing to deal with the upgrade issues?

RockfordLhotka replied on Friday, September 28, 2007

(Chris and I have been emailing offline as well)

I thought your issue was that the friendly property name needed to be in the URI? Doing that is a breaking change to RuleArgs, because RuleArgs needs to always insert at least one parameter value in the URI, and today it does not - so any subclass would assume IT is adding the first parameter value.

I didn't realize you wanted to serialize a list of RuleDescription objects into ViewState.

I doubt that would be a good idea. Without testing it is hard to say, but I bet that serializing an array or list of string values will result in a smaller byte stream than serializing a list of a custom type.

In the web, the IO speed is typically a more limiting factor than CPU cycles on the web server.

Besides, why would you send this rule info out to the browser and back? It is always available to the server-side code in a relatively inexpensive way, and it can't change over time (the rules are set once for the life of the app).

Or am I missing something?

chrislw replied on Friday, September 28, 2007

Actually, the problem you describe was another item that I was looking at.  First, I would like the friendly name in the argument list.  I thought it might be the same change as putting ValueType in the argument list.  I thought that we could add an item to the name/value pair argument collection called FriendlyName if the argument was supplied.

The second issue is that I am needing the rule definitions on the UI and want to store them so I don't have to keep pushing them around.  I guess it makes sense to put it in Session rather than ViewState since there is no reason to put it on the client.  However, I was thinking that regardless, it was a waste to get the rules as a string() and then convert them to a List<RuleDescription> instead of just putting them directly as a List<RuleDescription>.  Seems to be a lot of extra work to get the same information.

chrislw replied on Friday, September 28, 2007

I wanted to add some description about the FriendlyName argument.  This is what I was thinking...  The constructor looks something like this right now:

 

Public Sub New(ByVal propertyName As String, ByVal friendlyName As String, ByVal maxValue As T)

MyBase.New(propertyName, friendlyName)

Me("MaxValue") = maxValue

Me("Format") = ""

Me("ValueType") = GetType(T).FullName

End Sub

Why not make it look like this (and the other constructor with friendlyName in it?

Public Sub New(ByVal propertyName As String, ByVal friendlyName As String, ByVal maxValue As T)

MyBase.New(propertyName, friendlyName)

Me("MaxValue") = maxValue

Me("Format") = ""

Me("ValueType") = GetType(T).FullName

Me("FriendlyName") = friendlyName

End Sub

chrislw replied on Friday, September 28, 2007

Oh, I just realized what you are saying..  Changing the base class instead of the specific arg class would mean that the other subclasses may think they are adding the first parameter when they are not...  Is this true?  I don't quite understand the ramifications, but I am thinking that the ToString method in DecoratedRuleArgs puts that string together.  Oh..  Except that you are saying people are accessing them by value instead of the name of the argument...  See, I was using the name of the argument, but the value is sometimes much easier since we don't know the same all the time.

Interesting...  Did I complicate it?

RockfordLhotka replied on Friday, September 28, 2007

Sad [:(]Friendly name is managed by RuleArgs, not DecoratedRuleArgs. Not all RuleArgs subclasses flow from the new DecoratedRuleArgs - though I did change all rules in CommonRules to work that way.

But no existing subclasses work that way of course. They all subclass RuleArgs directly, and that's where the friendly name is implemented.

And it is totally realistic for RuleArgs.ToString() to put the friendly name into the URI - but it would be a breaking change. Right now, any RuleArgs subclass (including DecoratedRuleArgs) is coded to build its ToString() result like:

?arg1=v2&arg2=v2

using code vaguely like:

Public Overrides Function ToString() As String
  Return MyBase.ToString() & "?arg1=v2&arg2=v2"
End Function

But if RuleArgs adds the friendly name as an argument, then all subclasses would need to assume that there's already a parameter there and the code would need to be more like:

Public Overrides Function ToString() As String
  Return MyBase.ToString() & "&arg1=v2&arg2=v2"
End Function

While this is a breaking change, it would also make David W happy, because then the original severity could be included in the URI as well, resulting in stuff like this:

rule://method/property?PropertyName=A%20name&
        OriginalSeverity=Error&OriginalStopProcessing=False

Though I still resist putting the severity in there at all, because it only knows the _original_ value, not the value that may be set by a rule method at runtime... The same with StopProcessing - the value is intended to be changed by rules at runtime, and so all that can be shown here is the original value.

Heck, even THAT isn't accurate. What you really get is the last value these two fields were set to use by the last execution of the rule method. The whole thing just bugs me...

But putting friendly name in is safe, because it is immutable. Unfortunately doing so is a breaking change

chrislw replied on Friday, September 28, 2007

In a slightly different direction, what about making the friendlyname available directly?  I don't see anyway of getting the rule list other than GetRuleDescriptions.  Before the rules are "broken", can I pass in a "rule" to get the friendly name or some similar method?  I guess what I'm saying is how can I get a FriendlyName base on any rule description?

RockfordLhotka replied on Friday, September 28, 2007

chrislw:

In a slightly different direction, what about making the friendlyname available directly?  I don't see anyway of getting the rule list other than GetRuleDescriptions.  Before the rules are "broken", can I pass in a "rule" to get the friendly name or some similar method?  I guess what I'm saying is how can I get a FriendlyName base on any rule description?

I thought about exposing the RuleMethod objects directly. The problem with that sort of move, is that it eliminates data hiding, and reduces the maintainability of CSLA. If that's public, then I can never change it without causing breaking changes. The URI offers at least one level of abstraction/indirection.

So the short answer, is that the details managed by RuleMethod are private, and you can't access them directly. This is the purpose behind GetRuleDescriptions - to expose this information in a useful manner, without breaking encapsulation and data hiding.

admin replied on Friday, September 28, 2007

chrislw:

The second issue is that I am needing the rule definitions on the UI and want to store them so I don't have to keep pushing them around.  I guess it makes sense to put it in Session rather than ViewState since there is no reason to put it on the client.  However, I was thinking that regardless, it was a waste to get the rules as a string() and then convert them to a List<RuleDescription> instead of just putting them directly as a List<RuleDescription>.  Seems to be a lot of extra work to get the same information.

A relatively simple solution may be to put a Shared/static method on RuleDescription that parses a list of URIs into a List<RuleDescription>. Seems like a common thing to do, and the method could work like:

Dim list As List<RuleDescription> = _
  RuleDescription.Parse(_customer.GetRuleDescriptions)

 

chrislw replied on Friday, September 28, 2007

That makes sense, but what about instead of taking a string() and putting it in the list, maybe do the same as the GetRuleDescriptions, but actually pass back a list instead of string().  I'm suggesting this instead of replacing the original just to prevent the breaking change although I'm not sure it is the best idea.

RockfordLhotka replied on Friday, September 28, 2007

chrislw:
That makes sense, but what about instead of taking a string() and putting it in the list, maybe do the same as the GetRuleDescriptions, but actually pass back a list instead of string().  I'm suggesting this instead of replacing the original just to prevent the breaking change although I'm not sure it is the best idea.

All GetRuleDescriptions() does is loop through the RuleMethod objects and call ToString() on each one. Each RuleMethod object (and the RuleArgs it contains) are responsible for generating the URI that is returned.

The correct answer really is to do the breaking change - it just isn't a happy thing...

Copyright (c) Marimer LLC