views:

663

answers:

3

I checked the rest of the remoting questions, and this specific case did not seem to be addressed.

I have a .NET Remoting server/client set up. On the server side I have an object with a method that can throw an exception, and a client which will try to call that method.

Server:

public bool MyEquals(Guid myGuid, string a, string b)
{
    if (CheckAuthentication(myGuid))
    {
        logger.Debug("Request for \"" + a + "\".Equals(\"" + b + "\")");
        return a.Equals(b);
    }
    else
    {
        throw new AuthenticationException(UserRegistryService.USER_NOT_REGISTERED_EXCEPTION_TEXT);
    }
}

Client:

try
{
    bool result = RemotedObject.MyEquals(myGuid, "cat", "dog");
}
catch (Services.Exceptions.AuthenticationException e)
{
    Console.WriteLine("You do not have permission to execute that action");
}

When I call MyEquals with a Guid which causes CheckAuthentication to return false, .NET tries to throw the exception and says the AuthenticationException was unhandled. This happens server side. The exception is never marshaled over to the client-side, and I cannot figure out why. All of the questions I have looked at address the issue of an exception being handled client-side, but it isn't the custom exception but a base type. In my case, I can't even get any exception to cross the remoting boundary to the client. Here is a copy of AuthenticationException. It is in the shared library between both server and client.

[Serializable]
public class AuthenticationException : ApplicationException, ISerializable
{

    public AuthenticationException(string message)
        : base(message)
    {
    }

    public AuthenticationException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }

    #region ISerializable Members

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
    }

    #endregion
}
+2  A: 

Try catch(Exception) on the client side, and inspect the type of the exception being caught as well as any inner exceptions. It might give some clues.

Some other remarks:

  • ApplicationException is deprecated. You should normally derive from System.Exception.

  • I normally add the [Serializable] attribute to custom exceptions. Not sure if this is important.

  • You should normally override System.Exception.GetObjectData rather than explicitly implementing ISerializable.GetObjectData. In your case you're not serializing any additional data, so I would neither override it nor explicitly implement it. Again I'm unsure if this would have any impact.

My template for a serializable custom exception looks like the following, and I haven't had any problems with serialization over a remoting connection.

[Serializable]
public class CustomException : Exception
{

/// <summary>
/// Initializes a new instance of the <see cref="CustomException"/> class.
/// </summary>
public CustomException()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="CustomException"/> class with
/// a specified error message.
/// </summary>
public CustomException(string message) : base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomException"/> class with
/// a specified error message and a reference to the inner exception that is a cause
/// of this exception.
/// </summary>
public CustomException(string message, Exception inner) : base(message, inner)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomException"/> class with
/// serialized data.
/// </summary>
protected CustomException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}

}

UPDATE

Also if you're hosting the server code in IIS, you need the following in web.config to allow exceptions to propagate to the client:

  <system.web>
    ...
    <customErrors mode="Off" /> 
    ...
Joe
I implemented my AuthenticationException per your custom exception template. The exception was still NEVER caught on the client side. The server halted with "AuthenticationException was unhandled by user code" Do I have to specify to the server application that there is a client willing and waiting to handle any exceptions that are thrown?
Daniel Joseph
"Do I have to specify to the server application ..." - see update above
Joe
+1  A: 

First and foremost, do not inherit from ApplicationException. This advice has been around for a while, and I believe FxCop will automatically generate a message around this.

Next, you should usually decorate your custom exception with the [Serializable] attribute. I think this is your main issue, as I get an exception on the method call saying AuthenticationException is not marked as serializable.

Jesse C. Slicer
Ah, yes. Here is FxCop's message:warning : CA1058 : Microsoft.Design : Change the base type of 'AuthenticationException' so that it no longer extends 'ApplicationException'. This base exception type does not provide any additional value for framework classes. Extend 'System.Exception' or an existing unsealed exception type instead. Do not create a new exception base type unless there is specific value in enabling the creation of a catch handler for an entire class of exceptions.
Jesse C. Slicer
Ah. I'm sorry. I forgot to include that line when copying my class. I did decorate my exception with [Serializable].
Daniel Joseph
I'm reading your comment above to Joe regarding not having the exception caught on the client side. I have whipped together a quick Remoting server and client harness and it's getting caught client-side each and every time and NOT on the server. I'll ask a couple more qualifying questions to hopefully guide us toward the solution to this mystery. #1: Is the class which holds MyEquals descended from MarshalByRefObject? If not, it should be so that it will be marshalled over Remoting correctly. #2: How are you hosting the server? IIS, a Windows Service or just a stand-alone app to test?
Jesse C. Slicer
Answers to your questions (Thanks for being patient by-the-way and even whipping up a test app)#1: Yes, MyEquals is in a class that descends from MarshalByRefObject. It is the grandchild of that object, but I'm guessing that wouldn't cause the problem.#2: I am using a standalone test application. However, this console app constructs the remoted object inside of another dll assembly, so the remoted object can be bound to a Windows Service at a later time.
Daniel Joseph
Additionally, I built a test app myself, and I have the same issue. On the server side, I have a method that throws CustomException."throw new CustomException();" following that class model above. I still get the same error "CustomException was unhandled by user code" on the server and nothing on the client.
Daniel Joseph
I am so stupid! I didn't even think of this: I had the Visual Studio debugger to catch first instance unhandled exceptions. So Visual Studio stepped in before my client could. Disabled Visual Studio debugger: works like a charm! Thanks for all of the assistance though.
Daniel Joseph
I have a sneaky suspicion that "this console app constructs the remoted object inside of another dll assembly" might be related to the cause. The constructed object should be wrapped by the object your server is exposing and then THAT object should catch and rethrow the exception.
Jesse C. Slicer
Heh. Ignore my last comment then. Glad you have it going!
Jesse C. Slicer
A: 

There are different reasons for this error, and you guys have mentioned more than a couple.

I have noticed another reason for this error; and that's when the constructor of the remotable object being called remotely throws and exception. the exceptions are not being serialized because the object itself wasn't initialized at that point. i believe you should avoid ANY code that might cause a custom exception to be thrown inside the remotable object's constructor. and if a system exception was thrown during the execution of the code inside the constructor, you should deal with it as a system error (unknown) and build a mechanism to store the details of that exception using the file system or whatever.

.net remoting sounded really appealing back in the days, but the larger your project gets, and the more concepts you introduce to your code, the more weaknesses this technology reveals. it's a good technology, but you need lots of experience to make a robust solution out of it.

lilmoe