CSLA 4.1 and Silverlight - WcfErrorInfo vs. Native Exception

CSLA 4.1 and Silverlight - WcfErrorInfo vs. Native Exception

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


Jaans posted on Tuesday, April 26, 2011

Hi Rocky / all

This is in the context of a Silverlight application that uses CSLA 4.1.

As you know, if an exception is thrown on the Data Portal it gets wrapped - no actually repackaged/converted (for a more technically correct description) into a WcfErrorInfo class (with as much information about the original native exception as possible).

At the Silverlight client end, the original exception is presented as a chain of WcfErrorInfo class(es). I understand that this is is done because original exception is not neccesarily known to the Silverlight world (and sometimes not approrpriate for it). Part serialisation, part domain cohesion, part security, also at play here.

For many (if not most) scenario's the WcfErrorInfo wrapping of the native error that occurs on the DataPortal side is sufficient and the ExceptionTypeName is often usefull when I need to know which kind of exception took place.

I am however repeatedly running into some difficulty in not being able to have the original strongly typed exception available to me at the Silverlight end. Mostly this applies to custom exceptions that contain structured information that I need to get to, and I'm not looking forward to an elaborate XML Schema and string parsing alternative for each exception type I need :-(

What I'm proposing is a feature request for CSLA 4 where the DataPortal supports serializing known native exceptions natively and defaulting to WcfErrorInfo for the others.

It is my understanding that native WCF and Silverlight is able to do so (i.e. serialise an exception), provided it's a "KnownType". However, I'm not sure what the impact of such a request would have for the CSLA codebase.

I foresee that tagging an exception with the [KnownType] attribute (or a custom one if more suitable) should therefore indicate which exceptions should be serialised natively and which should be converted to WcfErrorInfo objects by the CSLA WCF DataPortal code.

Obviously it's up to the developer to make sure that the exception is present in both the .NET code base and the Silverlight end, and should likely be adorned with the [DataContract] and [KnownType] attributes. In theory at least.

I do hope this is at all feasable as it would simplifie so much for our CSLA based solutions.

Jaans

RockfordLhotka replied on Tuesday, April 26, 2011

I'd love to do this. What I don't know is how to make .NET aware of the set of "known exception types" on the Silverlight side.

Some are potentially easy - we could manually catalog the list of exception types in the Silverlight BCL, and maintain a hardcoded list of common exceptions in CSLA I suppose.

Beyond that everything gets difficult though. What about non-BCL exceptions in libraries like System.ServiceModel. Those libraries aren't part of "core .NET" or "core Silverlight" and you may or may not use them. Cataloging (and referencing) every possible Microsoft assembly from CSLA to get those types seems daunting.

And then we get to third-party libraries. Or your custom libraries. The problem is the same with both of these, because it is entirely unrealistic to catalog all the exceptions in every library.

So cataloging exceptions is an unworkable solution.

The next solution we could consider is for you to "register" exceptions you care about. Basically, you'd write code that runs as the SL app starts up. This code would call some special server-side object through the data portal to register the common types you plan to use. This could be tedious, and would complicate app startup (because you'd have to make sure no other work occurs until this async registration process completes).

However, even given that information I'm not entirely sure what we'd do with it. An exception graph could have known and unknown types intermixed. So what would we do with that? Maybe still return the WcfErrorInfo graph, but each WcfErrorInfo object would have a NativeException property that is normally null, but would be non-null for known exceptions?

Finally we arrive at the crux of the problem though. Because even if all of the above worked out, the MobileFormatter can only serialize types that implement IMobileObject. Exactly zero existing exception types implement this interface. And only your custom exception types can do so. We'll never convince Microsoft to implement this interface on their types, or DevExpress, or Infragistics, or anyone else.

This would require that you implement IMobileObject on your custom exception types - probably manually, unless we also created some MobileException base class, along with a field manager and so forth, so you could implement your custom exceptions somewhat like a ReadOnlyBase object I suppose.

So what we're really talking about, is a pretty complex operation that allows your custom exceptions to be serialized - sans any InnerException or other object references.

Surely there must be some simpler solution to this problem. Maybe some way by which you can hook into the process of converting the exception graph into the WcfErrorInfo graph such that you can add extra data to the graph during the process?

Jaans replied on Tuesday, April 26, 2011

Woa... thanks for your time and input on this Rocky! (I know you are pressed for time, especially with your sabbatical coming up).

Ok, so clearly you have thought this through and able to consider it in more depth - it surely give me some food for thought about the challenge it poses.

I do like your idea of expanding the WcfErrorInfo object to include a NativeException : MobileException property (default to null). But, as you pointed out, a key issue is the MobileFormatter, and InnerException graphs. And so from there your idea about something like including serialisation data (Byte Array / XML) that can be used to help re-create the exception on the Silverlight end (if I understood your thinking correctly).

I'm speaking out of ignorance, so please bear with me here. The next bit is a bit of a thought-dump and I'm hopefully not missing something that invalidates it.

Would it make sense that we turn around the concept of "registering interest in certain Exceptions from the Silverlight end" and instead drive it from both ends? Specifically I could imagine something like this:

Server Side (.NET)

public class DoNotSendMyCustomException : Exception { ... }

[KnownType] / [RegisterKnownCslaException]
public class SendMyCustomException : Exception { ... }

[KnownType] / [RegisterKnownCslaException]
public class SendMyOtherCustomException : Exception { ... }

Client Side (Silverlight)
(likely this could be "Linked" like we link the same file for Csla business object class definitions between the .NET and Silverlight BusinessLogic assemblies)

[KnownType] / [RegisterKnownCslaException]
public class SendMyCustomException : Exception { ... }

Lets start with the Server Side (.NET) code - when the WcfErrorInfo graph is being built, only the "SendMyCustomException" and the "SendMyOtherCustomException" exceptions will have their "raw data" sent along to the Client (in a Byte Array / XML form) because they have the attribute marker (or some other registration).

Then on the Client Side (Silverlight) end, during the rehydration of the WcfResponse stream into objects, I foresee that only the "SendMyCustomException" is created for the NativeException property on the WcfErrorInfo instance.
The "SendMyOtherCustomException" is not created and left null because even though "raw" data for it was send from the server, the class / type for it didn't exist on the Silverlight end and thus not created (we could throw an exception here if more appropriate).

In summary, what I'm trying to say is that:

Is that sensible?

Johann

tmg4340 replied on Wednesday, April 27, 2011

While this potentially solves the registration issue, you still have the MobileFormatter issues.  Your custom exceptions would have to implement the IMobileFormatter interface (or else there'd have to be some sort of mobile-formatter-compliant base class), otherwise you can't send them through the SL DataPortal.  And in order for this to work, your entire exception hierarchy would have to be MobileFormatter-compatible.  Since Exception isn't, the deserialization process would likely fail on the SL side.

(The "obvious" alternative is to not derive from any .NET exception, but I honestly don't know whether it's legal to throw an non-Exception-derived exception...)

I actually like Rocky's idea about providing hooks in the mobile-formatting process better - mostly because it's the only way I can think of to solve this problem.  Smile  I'm not exactly sure how that would work in an exception scenario, but giving you the ability to inject your own data directly into the WcfErrorInfo byte stream is probably about as close as you're going to get.  Similar hooks would have to exist on the deserialization side, where you'd pull the data out and into your own structures.  I don't think creating derived instances of WcfErrorInfo to hold your custom data would work, as then you're back to the KnownType issue...

- Scott

RockfordLhotka replied on Wednesday, April 27, 2011

One possible compromise here, is that the code that creates the WcfErrorInfo graph could maybe be a little smarter.

Right now that code walks through the exception graph, copying data from each exception into a WcfErrorInfo. The result is that there's a WcfErrorInfo graph that has the same number of objects as the original exception graph. What it doesn't do is grab "extra" information available from various specialized exception types.

I can see a couple potential solutions.

One is for you to be able to register your own exception->errorinfo copier methods with the graph copier. Your method would be associated with a specific exception type, and the copier would invoke your method to do the copy, instead of using its default implementation. This way you could copy any information you wanted from the exception to the WcfErrorInfo. The WcfErrorInfo would probably need a dictionary property into which you'd copy this data.

So you'd have (in concept):

var info = new WcfErrorInfo();
info.ExtraData["Value1"] = myException.Value1;

Another is for the copier code to examine each exception object to see if it implements IMobileObject. If an exception does implement IMobileObject, the exception could be included with the WcfErrorInfo in some OriginalException property. It would be your responsibility to ensure that any such exception types on the server are also on the client - and if you missed a reference on the client you'd get one of those obscure WCF failure exceptions because deserialization would fail.

But the result on the client would be (assuming e is the callback args):

var info = e.Error.ErrorInfo;
Exception ex = info.OriginalException;

As I mentioned in my previous post, this would only work if the following is true:

  1. The server-side exception implements IMobileObject
  2. The client has access to the same exception type as the server
  3. We did some major surgery to WcfErrorInfo, because right now it is a light-weight WCF data contract object, and this change would force us to make it into a heavier weight MobileObject

Then again, it could require some major surgery to WcfErrorInfo to allow it to return even the dictionary of simple values, because a dictionary can't be part of a data contract either...

Jaans replied on Wednesday, April 27, 2011

Thanks guys

I quite like the concept of registering a "TypeConverter" method / delegate that is invoked when a particular exception is encountered by the WcfErrorInfo graph logic. I don't like that this could result in some heavy-weight changes to WcfErrorInfo :-(

Off topic: I guess such a TypeConverter concept could be extended to any type - not just exception for that matter and allow scenarios where compact lightweight or custom serialization payloads could be implemented - in theory. But I digress...

Implementing the IMobileObject interface on custom exceptions will solve quite a few scenarios, but as mentioned before, does little for .NET Framework exceptions. Maybe we can't have our cake afterall...

What I'm wondering is if there isn't an alternative or at least some existing design to leverage. I found an interesting article embedded in MSDN regarding WCF with Silverlight and the serialization of Exceptions (both built-in and custom). I would appear that on the Silverlight end, the exception arrives as an "ArrayOfXElement" structure which the proxy class uses to extract and re-hydrate a Silverlight Instance of the original exception.

Here's the link: http://blogs.msdn.com/b/suwatch/archive/2009/01/21/wcf-silverlight-exception-and-serialization.aspx 
Does this help at all? Since WcfErrorInfo is already a WCF data contract object, could we not in some way leverage its ability to serialize/de-serialize exception objects?
Maybe that's what can be used as the "intermediate raw serialised data" for the exception?

Thanks for the input - I wish I could suggest more concrete solution options...

RockfordLhotka replied on Wednesday, April 27, 2011

This is interesting.

The cheapest thing we could do is an interesting combination of these ideas.

Specifically, add a string UserData property to WcfErrorInfo, and allow you to register a delegate/lambda with the WcfErrorInfo type in Csla.Server.Hosts.Silverlight (.NET only).

Basically what I'm suggesting is that you could register a Func<string, Exception> with CSLA, so when your particular type of exception is encountered your code would return a string containing whatever data you want to return to the client. This string would be put into the UserData property of the WcfErrorInfo object.

You could return an XElement (as a string) if you wanted - and then on the client you could easily convert the string back into an XElement.

This would allow CSLA to pass the data back to the client, without restricting (much) what you might choose to send.

Jaans replied on Thursday, April 28, 2011

I like it!

Would we be able know the "Type Name" of the original exception on the Silverlight side? 

If so, a Generic Method on the CSLA WcfErrorInfo that accepts a delegate/lambda could then attempt to create that exception instance from the "UserData"?

On a related note, I tried to build a simple solution with Silverlight 4 and WCF 4 as per the above article to see the generated code, hoping to glean some insight into what is possible on the Silverlight side.
What is very interesting is that with WCF 4.0 it doesn't seem to work like that anymore. More accurately, I was unable to get the Silverlight Service Proxy generator (slsvcutil.exe) - that gets invoked when adding a "Service Reference" to Silverlight - to generate the intermediate bits for deserializing a custom exception. I does work for the built in exception types, but there is a caveat: It doesn't work when you specify the base class (e.g. Exception) for the contract but then serialize an instance of a more derived class (e.g. DivideByZeroException).

I'm not sure if I'm doing something wrong, or it's just not designed to work that way... this could be a death knell for the idea.
(I'd be happy to email/attach the simple solution if interested - it's very basic).

In my "travels" I did come across a System.ServiceModel.ExceptionDetail class that is apparently intended to "wrap" Exception graphs for serialization. This works well and has no issue with serialization/deserialization no matter what exception I provide it (Exception, DivideByZeroException, CustomException). But in many ways this is similar to the WcfErrorInfo class... I think?

Jaans replied on Thursday, April 28, 2011

Scratch that... I managed to get it working now.

I needed to move the CustomException into a separate class library (both .NET and SL) and ensure the [KnownType] attributes are complete.

 

RockfordLhotka replied on Thursday, April 28, 2011

Jaans

Would we be able know the "Type Name" of the original exception on the Silverlight side? 

The original type name is already available through the ExceptionTypeName property of WcfErrorInfo. So I think it is safe to say yes :)

I suspect the other solution you discovered is something Juval Lowy wrote to solve the same problem as WcfErrorInfo. Although I'm sure more people than just Juval and I have tried to address this issue.

tmg4340 replied on Thursday, April 28, 2011

Presuming you're talking about ExceptionDetail, that's actually a class built into .NET to provide additional information when the "includeExceptionDetailInFaults" service option is set.  It allows you to throw a FaultException<ExceptionDetail> fault that provides information fairly similar to what WcfErrorInfo uses.  Its constructor takes an exception and builds a WCF-compliant graph of ExceptionDetail objects.

It's recommended to shut that off in production environments, since it exposes detailed exception info.  But there's nothing actually stopping you from using it.

HTH

- Scott

Copyright (c) Marimer LLC