So you are writing a client app that calls a set of WCF services. Those services implement all the business logic (including validation), and you want to create a set of "thin" business objects on the client. Did I get that right?
In your objects, you'll still have DataPortal_XYZ methods, they'll just call the WCF services instead of talking to the database. That part is actually pretty easy, because (conceptually) you have "WCF Service==Stored Procedure" from an architectural perspective.
But then the issue is that the service has validated your data and found it to be wrong. How do you get the "wrongness" into your object so it can in turn be expressed to the UI via data binding?
First, your WCF service obviously must have some contract by which it can return a list of what's wrong. Without that list you are done at step one. That list must detail what is wrong on a per-data-field basis or it isn't useful to you either. It should be quite possible to come up with a general validation-error-result contract that can be used by all service implementations (perhaps via a fault).
Second, your DataPortal_XYZ method must be prepared to accept this contract (in lieu of the normal success result). Upon accepting this validation-error-result from the service, you now have all the info you need to create a set of broken rules.
Third, you need a rule method that understands how to pull a specific property's errors out of the validation-error-result message. This rule method must be attached to every property on your object.
Fourth, your DataPortal_XYZ can call ValidationRules.CheckRules(), thus running the rules for each property, which of coruse means that each property will get the broken rules from the validation-error-result message.
Of course I haven't TRIED any of this, so there may be (probably are) hidden bits of complexity. But at a high level this certainly seems like a good solution
Yup, this was the path I've gone down. #3 is were I get a little hung up though. My response message has an array called ValidationResultExceptionInfo. These are very simple objects with properties like PropertyName, Description, Severity & ResultCode. PropertyName and Description tell me which property flunked validation and why and they are of the most interest to me from a CSLA point of view.
I need to figure out how to implement what you describe. It's a little fuzzy in my head but I'm certain it will work.
Thanks for the response Rocky. Your suggestion has given me something to focus. I'll post whatever I come up with.
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContractAttribute(
typeof(GreetingFault),
Action = "http://www.contoso.com/GreetingFault",
ProtectionLevel = ProtectionLevel.None)]
string MyMethod(string Name);
}
#region Validation
[DataContractAttribute]
public class GreetingFault
{
private string _msg;
private BrokenRules _brokenRulesList;
[DataMemberAttribute]
public BrokenRules BrokenRulesList
{
get { return _brokenRulesList; }
set { _brokenRulesList = value; }
}
[DataMemberAttribute]
public string Msg
{
get { return this._msg; }
set { this._msg = value; }
}
public GreetingFault(string message)
{
this._msg = message;
_brokenRulesList = new BrokenRules();
_brokenRulesList.Add(new BrokenRule("Rule1", "S1", "Descr1"));
_brokenRulesList.Add(new BrokenRule("Rule2", "S2", "Descr2"));
_brokenRulesList.Add(new BrokenRule("Rule3", "S3", "Descr3"));
}
}
[DataContract]
public class BrokenRule
{
string _ruleName;
string _ruleSeverity;
string _ruleDescription;
[DataMember]
public string RuleName
{
get { return _ruleName; }
set { _ruleName = value; }
}
[DataMember]
public string RuleSeverity
{
get { return _ruleSeverity; }
set { _ruleSeverity = value; }
}
[DataMember]
public string RuleDescription
{
get { return _ruleDescription; }
set { _ruleDescription = value; }
}
public BrokenRule(string ruleName, string ruleSeverity, string ruleDescription)
{
_ruleName = ruleName;
_ruleSeverity = ruleSeverity;
_ruleDescription = ruleDescription;
}
}
[CollectionDataContract]
public class BrokenRules : List<BrokenRule> { }
#endregion
public class Service1 : IService1
{
public string MyMethod (string Name)
{
throw new FaultException<GreetingFault>(new GreetingFault("A Greeting error occurred." ));
}
static void Main(string[] args)
{
...
}
}
Client code:
try
{
localhost.Service1 ws = new localhost.Service1();
MessageBox.Show(ws.MyMethod(“Name”));
}
catch (System.Web.Services.Protocols.SoapException ex)
{
Console.WriteLine(ex.Detail.InnerXml);
}
After playing with this some more, I think I've hit a limitation.
Here's the scenario:
A Dog : BusinessBase instance is saved. A WCF message is sent to the service and the service runs a validation process. A list of broken rules is returned to the DataPortal_XYZ method as part of a response message. These broken rules contain property/description sufficient to work within the CSLA validation design.
In the DataPortal_XYZ method of the client Dog (a CSLA BusinessBase object), a ProcessBrokenRules method is run that takes the server-side generated list of broken rules and attaches each one to its respective property. So, to use my earlier example, no dog may be named Fido. The property name is "Name" and can be attached via the ValidationRules' AddRule() method AFTER the DataPortal_XYZ method is called. At this point, everything works. Errors are correctly populated and I can read the descriptions and use them with an error provider.
The problem though is that the BrokenRules collection is pretty much untouchable. If the user makes corrections to the instance of Dog he's working with and wants to save again, he can't because from the busines object's perspective, it's still invalid. No new WCF message can be sent to the server through the data portal because a CSLA exception is thrown stating that the object is in an invalid condition. Even were this possible, there's no way to reset the broken rules in order to accommodate a new set of broken rules from the server.
Neither the Remove() nor Clear() methods work on the BrokenRulesCollection work in the context of a business class. They are not designed to be used directly, otherwise I could call them just prior to sending my WCF message to the services (validation) layer. I think at this point, I would need to branch away from CSLA and come up with a different method of doing business validation.
Here's some pseudo-code of what I'd like to be able to do:
function DataPortal_Update()
begin
ResetBrokenRules()
msg = CreateWCFMessage()
SendMessage(msg)
ProcessBrokenRules(msg.BrokenRules)
end
"You have "WCF Service==Stored Procedure" from an architectural perspective", right? If the "Stored Procedure" fails the reasons are reported back to your DataPortal_XYZ method. You could analyse this response and set accordingly some private flags in your object, then check your business rules; the rules should take into account values of the flags.
I might be wrong, but I suspect that you are trying to pass back in your WCF Service/Stored Procedure a "properly typed" collection of broken rules. I would think that either your communications support type fidelity or not. If yes (“a pure CSLA solution end to end”), there is no need to move the collection of broken rules back and forth because you move your BO itself. If no ("WCF Service==Stored Procedure"), you should not move either the broken rules collection or BO: you are moving just data. You might be able to make a hybrid work, but I do not think that it would be a good solution.
You might be able to make a hybrid work, but I do not think that it would be a good solution.Yup, I agree. I'm constrained by certain decisions made by other people, but I've a real need to leverage binding, validation, sorting and filtering within a rich client application. For these reasons, I was going for a thin client implementation of CSLA, but I don't want to have to hack the validation stuff because I prefer to stay on the primary branch.
SultanOfSuede:Yup, I agree. I'm constrained by certain decisions made by other people, but I've a real need to leverage binding, validation, sorting and filtering within a rich client application. For these reasons, I was going for a thin client implementation of CSLA, but I don't want to have to hack the validation stuff because I prefer to stay on the primary branch.
What exactly are the people making decisions trying to acomplish?As explained to me, my company wants every application to use a service for data retrieval so that if someone in another department needs data, there's less work to do. Flick a switch and voila. They can just get pointed to the app's service. Unfortunately, this is not SOA, but once certain memes get started, they never go away. As a result, enormous amounts of time and money are spent writing "SOA" apps when all available metrics indicate that there is little history of departments pinging each other for data. Suppose I have an app with 500 public methods. Of these, maybe 2% are used externally and over the course of the app's lifetime, maybe another 2% will be used by external consumers still within our company. Why then do we spend so much time writing hundreds of methods as services when they really aren't? The assumption made by my company's architects is that the application data will have an interface that always matches what Bob in accounting needs. It also presupposes that everyone is agreed on security, validation, error-handling, etc. This of course is all hogwash, but hey, it's what I've to work with.
If you are trying to _really_ be SOA, then you must view
this as two separate applications, each with their own reponsibilties.
The result of that view, is that your client app is responsible
for providing the user with the best possible experience – almost certainly
including implementing its own validation logic.
The fact that the same (or similar) validation logic will be in
some of the services you call doesn’t matter – that’s their
responsibility.
SOA is NOT A WAY TO REUSE CODE. Rather the reverse: it is a
great way to duplicate code all over the place. Sad but true.
Rocky
From: SultanOfSuede
[mailto:cslanet@lhotka.net]
Sent: Thursday, June 14, 2007 9:35 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Working with BrokenRules in a WCF environment
You might be able to make a hybrid work, but I do not think that it would be a good solution.
Yup, I agree. I'm constrained by certain decisions made by
other people, but I've a real need to leverage binding, validation, sorting and
filtering within a rich client application. For these reasons, I was going for
a thin client implementation of CSLA, but I don't want to have to hack the
validation stuff because I prefer to stay on the primary branch.
Copyright (c) Marimer LLC