SocketException with WCF Dataportal

SocketException with WCF Dataportal

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


rwilkerson posted on Friday, September 11, 2009

We're trying to get the remote dataportal setup to work with our business objects using WCF. But, we're having problems when an Exception is thrown on the Server. It is not being communicated back to the Client. On the client a SocketException is thrown stating that the connection was closed.


Exception Type: System.Net.Sockets.SocketException
Exception Message: An existing connection was forcibly closed by the remote host


So...we backed up and tried to setup the Project Tracker demo with the same setup instead of our stuff. Using PtWin and WcfHost and following the book directly (page 594) .

To simulate the exception we added a "Throw New Exception" in the DataPortal_Fetch. We get the same behaviour with a SocketException being thrown on the PtWin side.

To Summarize...
Exceptions thrown on the server-side are causing the WCF connection to be dropped, which in turn is causing the client-side to throw a SocketException.

I know this has to be a setup or configuration issue, but we're at a loss to as to where to look next.

Any suggestions will be greatly appreciated.

Rick...

tmg4340 replied on Friday, September 11, 2009

I haven't really looked into how CSLA's WCF DataPortal setup works, but I can provide some general WCF information I've learned which should still apply.

WCF was intentionally designed to insulate clients from exceptions thrown within a WCF service.  This is because the presumption was that any exception the service generates isn't of any real value to the client, since (a) there very likely isn't anything the client can do about it anyway, (b) the exception thrown may not have any relevance to the service method called, and (c) services are supposed to be "black boxes", so letting the exception flow back to the client potentially exposes internals of the service that you may not want to.  As such, WCF will intercept any exception, replace it with a more generic exception, and pass that back to the client proxy.  The WCF infrastructure will see the exception return, fault the WCF channel, and close it.

Some of this behavior can be managed by defining a Fault contract on your WCF service.  This essentially defines "expected exceptions" that the service will throw.  If a service method throws one of these exceptions, that will not fault your channel, so you have a chance on the client to deal with it.  Creating fault contracts are not nearly as simple as they sound, largely because Fault contracts are very explicit about what exception types are allowed (e.g. throwing a subclass of the fault-contract exception doesn't satisfy the fault contract.)  My guess is that the WCF DP setup doesn't have one, and creating one would require modification of the CSLA WCF code.

Furthermore, in a "quirk" of WCF programming, if you use the recommended "using block" pattern of calling your WCF service, and the service has no Fault contract defined (or throws an exception that is not part of the Fault contract), this sequence of events will actually cause a problem when your using block tries to dispose of the client, because the dispose code tries to close the channel, which has been faulted (and closed), and you end up with the exception you're getting.

Ultimately, there are two separate issues involved.  If you want the "real" exception to return to the client, you have to set the "includeExceptionDetailInFaults" property to "true" in your service behavior configuration.  That's generally not recommended in a production environment, because it violates the spirit of the WCF service interface, but there's no real harm in doing it.  That doesn't necessarily mean that the exception you get on the client will be the one you throw - it should still be wrapped in another exception.  But the "real" exception will be part of the InnerException stack.  That is also a change to the CSLA code base, because that's a service-side configuration change - but it's a small one.

Solving the second issue is a little more complicated.  A Fault contract only gets you so far, and given the nature of Fault contracts in the current version of WCF, creating one would potentially be a non-trivial thing for Rocky (or anyone else) to do.  What I've resorted to in other projects that consume WCF services is creating a proxy wrapper class.  This wrapper class implements IDisposable, and I use it instead of the service proxy class in my "using" block.  It has a property that exposes the actual proxy class, so I don't have to do any funky delegation or dynamic class construction.  In the Dispose method of my wrapper class, I check the proxy's channel state, and if it's faulted, I call Abort() on the proxy.  This essentially clears the fault, and allows for a clean disposal of the proxy object.  You still lose the actual exception from the service, but at least your client doesn't crap out.  If you don't like the wrapper-class concept, you can still use a "using block" implementation, but you'll want to wrap the code in your using block inside a try/catch, and do the check/Abort() call in your catch block.  I chose the wrapper class so I'd only have to write that code once.  Smile [:)]

Welcome to the complicated world of WCF.  Hang on for the ride...

HTH

- Scott

rwilkerson replied on Friday, September 11, 2009

Thanks for the reply and your information does explain a few things. But I don't think we're talking about the same situation. You seem to be talking about problems that would occur if I was writing a custom WCF Service. (As in chapter 21)

I'm using the wcf dataportal included with CSLA. Here's the part of the book that leads me to believe the behaviour we're seeing is not correct. Page 428 (C# 2008).

"In addition to context information, exception data from the server should flow back to the client with full fidelity. The UI often needs to know the specifics about any server-side exceptions in order to properly notify the user..."

When working under the local dataportal our exception is caught as a DataPortalException within which a BusinessException explaining the details. From what I'm understanding from the book the behaviour should be same when using a remote dataportal.

Thanks,
Rick...

tmg4340 replied on Friday, September 11, 2009

Yes, it should be.  But whether it is or not depends on how Rocky created the WCF contracts that he is using, and how the proxy class is working.  No, CSLA does not use a "standalone WCF service" when implementing the WCF DataPortal.  But it's still using WCF, and thus the same rules still apply.

Having said that, I  took a look at the WCF proxy code in the 3.7 version of CSLA I have.  I don't see anything in the way the proxy is coded that is letting exceptions bubble up to the service boundary.  The proxy code doesn't use the "using block" pattern - everything in the proxy code is wrapped in try/catch blocks with explicit Close/Abort calls.  And it doesn't look like the server-side code lets any exceptions leak out.  So the pitfalls I mentioned should not be biting you.

That leads us back to a configuration issue with your WCF setup.  I generally tell people that 80% of getting a WCF service to work correctly is getting the configuration right.  It's almost ridiculously confusing, there's very little documentation, and you can't figure out the problems until runtime.  In any event, you'll need to give us some more information (how you're hosting your remote DP, your WCF configuration setup, etc.) before we can make some suggestions.

HTH

- Scott

RockfordLhotka replied on Friday, September 11, 2009

Your configuration does need to allow exceptions to flow back from the server. Normally that's not recommended behavior, but in the case of the data portal that is exactly what you want to occur.

If you create a new WCF service using VS, the config will include that behavior, with a comment suggesting you remove the behavior for production. Don't remove the behavior, as without it the WCF channel faults and you won't get the exception back the way the data portal expects.

rwilkerson replied on Friday, September 11, 2009

Here's our config settings. (Minus the angle-brackets...the editor doesn't seem to be working quite right)

=========Sever========
system.serviceModel
service
!-- Before deployment, you should remove the returnFaults behavior configuration to avoid disclosing information in exception messages --
service name="Csla.Server.Hosts.WcfPortal" behaviorConfiguration="returnFaults"
endpoint contract="Csla.Server.Hosts.IWcfPortal" binding="wsHttpBinding"
/service
/services
behaviors
serviceBehaviors
behavior name="returnFaults"
serviceDebug includeExceptionDetailInFaults="true"
/behavior
/serviceBehaviors
/behaviors
/system.serviceModel

=========Client========
system.serviceModel
client
endpoint name="WcfDataPortal"
address="http://10.0.0.109/PTrackerHost/WcfPortal.svc"
binding="wsHttpBinding"
contract="Csla.Server.Hosts.IWcfPortal"
/client
/system.serviceModel
=====================

From what we can tell it's exactly as it is in the book.

Would the configuration need to be different if the two machines don't exist in the same Active Directory Domain? We don't join our testing VM's to the local domain, so in this case the Developer workstation (running PtWin) is in a domain and the server (VM) is not.

Thanks,
Rick...

RockfordLhotka replied on Friday, September 11, 2009

What exception are you throwing?

Exceptions must be serializable to flow from server back to client. Most standard exceptions are serializable, but SecurityException has some quirks.

It is possible that any custom exception, or exceptions from data layer frameworks, or non-core-.NET sources might not be properly implemented, and thus may not be serializable.

That's really the only case where an exception should leak out of the data portal. Otherwise the data portal catches all exceptions, serializes them and returns them to the client. Obviously if the exception can't be serialized that'd be a problem.

rwilkerson replied on Monday, September 14, 2009

Ok...progress. In our simple TestException we hadn't marked it as Serializable. So, that helped, but we then started getting a problem with missing SerializationInfo. That lead us down the path to the following information.

StackOverflow: http://stackoverflow.com/questions/486460/how-to-serialize-an-exception-object-in-c
MSDN: http://msdn.microsoft.com/en-us/library/ms173163.aspx

Once we added the constructor to support serialization everything SEEMED to be working. It was then we noticed that the *Message* property wasn't coming across. In the DataPortal_Fetch for ProjectList we added a

Throw New TestException("Here is my message")

The Message property on the caught exception is: "Exception of type 'ProjectTracker.Library.TestException' was thrown."

BUT..if we do this:

Throw New Exception("Here is my message")

Then the Message property on the caught exception is: "Here is my message"

Things we've Tried:
* Overloading the Message property (exposing the base Message property)
* Overriding the Message property (exposing the base Message property) (required to be readonly)
* Overloading the Message property (exposing our own backing field)
* Overriding the Message property (exposing our own backing field) (required to be readonly)


The question now is what would keep the exception message in a custom exception from flowing through the dataportal?

Thanks,
Rick...

=====ToString on exception that is caught=======
"Csla.DataPortalException: DataPortal.Fetch failed (Exception of type 'ProjectTracker.Library.TestException' was thrown.) ---> Csla.Reflection.CallMethodException: DataPortal_Fetch method call failed ---> ProjectTracker.Library.TestException: Exception of type 'ProjectTracker.Library.TestException' was thrown.
--- End of inner exception stack trace ---
at ProjectTracker.Library.ProjectList.DataPortal_Fetch()
at dm(Object , Object[] )
at Csla.Reflection.MethodCaller.CallMethod(Object obj, DynamicMethodHandle methodHandle, Object[] parameters)
at Csla.Reflection.MethodCaller.CallMethod(Object obj, DynamicMethodHandle methodHandle, Object[] parameters)
at Csla.Reflection.MethodCaller.CallMethod(Object obj, String method, Object[] parameters)
at Csla.Reflection.LateBoundObject.CallMethod(String method)
at Csla.Server.SimpleDataPortal.Fetch(Type objectType, Object criteria, DataPortalContext context)
--- End of inner exception stack trace ---
at ProjectTracker.Library.ProjectList.DataPortal_Fetch()
at dm(Object , Object[] )
at Csla.Reflection.MethodCaller.CallMethod(Object obj, DynamicMethodHandle methodHandle, Object[] parameters)
at Csla.Reflection.MethodCaller.CallMethod(Object obj, DynamicMethodHandle methodHandle, Object[] parameters)
at Csla.Reflection.MethodCaller.CallMethod(Object obj, String method, Object[] parameters)
at Csla.Reflection.LateBoundObject.CallMethod(String method)
at Csla.Server.SimpleDataPortal.Fetch(Type objectType, Object criteria, DataPortalContext context)
at Csla.DataPortal.Fetch(Type objectType, Object criteria)
at Csla.DataPortal.Fetch[T]()
at ProjectTracker.Library.ProjectList.GetProjectList() in D:\Projects\Test Projects\CslaNet\vb\ProjectTrackervb\ProjectTracker.Library\ProjectList.vb:line 9
at PTWin.ProjectSelect.ProjectSelect_Load(Object sender, EventArgs e) in D:\Projects\Test Projects\CslaNet\vb\ProjectTrackervb\PTWin\ProjectSelect.vb:line 51"

=====Here's the custom exception class====
Serializable() _
Public Class TestException
Inherits Exception

Public Sub New()
End Sub

Public Sub New(ByVal message As String)
MyBase.New(message)
End Sub

Protected Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, _
ByVal context As System.Runtime.Serialization.StreamingContext)

End Sub

End Class

Copyright (c) Marimer LLC