Of BusinessRules and CurrentCulture

Of BusinessRules and CurrentCulture

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


lwmorris posted on Tuesday, June 23, 2009

I have an address business object that I want to add some business rules to.

To start with, I added a couple of resource files to hold my regex expression that would validate a postal code for the US and AU. The US allows 5 digits, AU allows 4.

Unfortunately, the OnDeserialize event calls the AddBusinessRules and AddInstanceBusinessRules BEFORE the Thread’s CurrentCulture has been set. Is there any hope that all this context information can be set before the business object is deserialize?

skagen00 replied on Tuesday, June 23, 2009

Maybe I'm answering a question you're not asking...

If you have business rules on the address that depend on a country (not culture - that's not where the real dependency is) maybe you should have the country be a property of the address.

The default value for the country of the address could be established from the currentculture if you like but at least in the systems I've worked on a configuration value suffices (for default country).

That is - if your validation of an address depends the country, it should be a property of your object.

For my country table I had a RegEx stored for each country that determined the valid postal code.

skagen00 replied on Tuesday, June 23, 2009

i.e.
///
/// Validation rule to ensure that the postal code is valid.
///
/// Postal code to validate.
///
/// Validity of the postal code
private static bool IsValidPostalNumber(PostalCode target, Csla.Validation.RuleArgs e)
{
string errorMessage;
Country postalCountry = target.GetCountryInfo();

if (!Regex.IsMatch(target.PostalNumber, postalCountry.PostalCodeRegexMatch))
{

// The error message has a {0} parameter that allows us to
// convey the format for the county.
errorMessage = String.Format(CultureInfo.CurrentCulture,
BusinessRules.InvalidPostalCodeFormat, postalCountry.PostalCodeName,
postalCountry.PostalCodeFormatInstruction);

e.Description = errorMessage;
return false;
}
return true;
}

lwmorris replied on Tuesday, June 23, 2009

Actually, I could get it to work for address just like you suggest, but there are other business rules that are still dependent upon what country the web site is running under. For example, SSN may be required for the US, but TFN is optional in AU.

Either way, The dataportal context information is not being set until way after the object's AddBusinessRules methods have been called.

It just seems to me that one would want all the DataPortalContext information set before the business object is asked for business rules.

lwmorris replied on Tuesday, June 23, 2009

I see that adding

[OnDeserialized()]
private void OnDeserializedHandler(StreamingContext context)
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(_clientCulture);
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(_clientUICulture);
}

to DataPortalContext.cs will set the CultureInfo for me prior to AddBusinessRules being called, I'm just not sure I'm doing the right thing.

skagen00 replied on Tuesday, June 23, 2009

Are you adding a different business rule via AddInstanceBusinessRule based on the culture?

If so, I would suggest going with a single business rule that handles all cultures (and just check the culture within the business rule). (That is, don't make these instance business rules)

AddInstanceBusinessRules is the less performant way to go and is kept in for backwards compatibility for the most part (my understanding).

lwmorris replied on Tuesday, June 23, 2009

That way works.

I think at this point it's just philosophical. I would expect context information to have been set prior to the business objects firing AddBusinessRules, AddAuthorizationRules, etc.

Thanks for your help!

skagen00 replied on Tuesday, June 23, 2009

No problem - where in the lifecycle were you setting currentculture/currentuiculture?

Application_AcquireRequestState in global.asax?

RockfordLhotka replied on Tuesday, June 23, 2009

It is possible that there's an issue in the data portal around culture.

The data portal transfers the UI culture automatically. It may be that it should transfer both culture values automatically?

lwmorris replied on Tuesday, June 23, 2009

Eventually Csla.Server.DataPortal.cs will setup all the context information like LogicalExecutionLocation, ClientContext, GlobalContext, CurrentCulture, etc…
Unfortunately, this is called AFTER the business object has been deserialized and AddBusinessRules, AddAuthorizationRules, etc. have been called. During this window, one doesn’t have access to any of the context or culture information.

I think the problem is in Csla.Server.DataPortal.cs. All the method signatures look something like:

public DataPortalResult Update(object obj, DataPortalContext context)
{
try
{
SetContext(context);

I think the object is deserialized by WCF before the Update method is called and before the SetContext(context) line executes.

I can get things to work properly if I change Csla.Server.DataPortal.SetContext to an internal method, then place the following in Csla.Server.DataPortalContext.cs:

[OnDeserialized()]
private void OnDeserializedHandler(StreamingContext context)
{
DataPortal.SetContext(this);
}

I just don’t know that the OnDeserializedHandler in DataPortalContext is guaranteed to fire before the business object is deserialized. Can I add [DataMember (Order=0)] to UpdateRequest, etc. to guarantee this?

Did any of this make sense?

RockfordLhotka replied on Tuesday, June 23, 2009

At a high level, the order of operation in a normal remote data portal call
is:

1. Object graph deserializes on server
2. Data portal runs your request authorization code (if any)
3. Data portal sets up server context (culture, ApplicationContext, etc)
4. Data portal determines if you want a transaction
5. Data portal determines if you want an object factory
6. Data portal invokes your data methods


-----Original Message--
From: lwmorris [mailto:cslanet@lhotka.net]
Sent: Tuesday, June 23, 2009 3:41 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Of BusinessRules and CurrentCulture

Eventually Csla.Server.DataPortal.cs will setup all the context information
like LogicalExecutionLocation, ClientContext, GlobalContext, CurrentCulture,
etcb

lwmorris replied on Tuesday, June 23, 2009

It does appear to work as you say.

However, AddBusinessRules, etc. fire while the object graph is being deserialized on the server in the OnDeserialized method of Csla.Core.BusinessBase.

That means any culture related business logic (phone numbers, postal codes, SSN/TFN) are executed before you know the cultural context.

The end result is the business object is invalid because you’re not using the correct cultural rules.

[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnDeserialized(StreamingContext context)
{
ValidationRules.SetTarget(this);
if (_fieldManager != null)
FieldManager.SetPropertyList(this.GetType());
InitializeBusinessRules();
InitializeAuthorizationRules();
FieldDataDeserialized();
}

Copyright (c) Marimer LLC