TheGateKeeper
TheGateKeeper

Reputation: 4540

Can't send exceptions from my WCF application

Following the tutorial here

http://beyondrelational.com/modules/2/blogs/79/posts/11543/throwing-exceptions-from-wcf-service-faultexception.aspx

I ended up wit the following code:

Interface:

[ServiceContract]
public interface IAuthenticator
{
    [OperationContract]
    [FaultContract(typeof(AuthenticationException))]
    Account authenticateApplication(string userName, string Password);
}

Exception:

[DataContract]
public class AuthenticationException
{
    private string validationError;

    [DataMember]
    public string ValidationError
    {
        set { validationError = value; }
        get { return validationError; }
    }

    public AuthenticationException(string valError)
    {
        validationError = valError;
    }
}

And finally this is how I throw errors in my implementation of authenticateApplication:

catch (InvalidUsernameException)
{
    throw new FaultException<AuthenticationException>(new AuthenticationException("The username you entered could not be found in our database."), new FaultReason("Error"));
}

The problem with this is that instead of sending the error back to the client, the app WCF app is crashing saying I didn't handle the exception.

If it matters, here is how I call from my client:

try
{
    myAcc = httpProxy.authenticateApplication("some text", "some other text");
}
catch (FaultException<AuthenticationException> ex)
{
    MessageBox.Show(ex.Detail.ValidationError);
    return;
}

Edit: Here is my stack trace:

   at AuthenticatorService.Authenticator.authenticateApplication(String userName, String Password) in E:\Miscellaneous\Applications\Web 2.0 Creator\AuthenticatorService\AuthenticatorService\AuthenticatorService\Authenticator.cs:line 109
   at SyncInvokeauthenticateApplication(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)

Edit Edit:

Here is the full try catch block:

try
{
    using (myConnection)
    {
        using (myCommand)
        {
            //Tell it to execute the stored procedure on the database
            myCommand.CommandText = "findUsername";
            myCommand.CommandType = CommandType.StoredProcedure;
            myCommand.Parameters.Add("@userName", SqlDbType.NVarChar, 20);
            myCommand.Parameters["@userName"].Value = userName;

            //If the reader returns 0 rows, that means the username doesn't exist in the database, so step there and return an exception
            using (myReader)
            {
                myReader = myCommand.ExecuteReader();
                if (myReader.HasRows == false)
                {
                    InvalidUsernameException iue = new InvalidUsernameException();
                    throw iue;
                }
                else //Else we store the fields
                {
                    myAcc.Password = myReader[1].ToString();
                    isActive = Convert.ToBoolean(myReader[2]);
                    myAcc.Key = myReader[3].ToString();
                    myAcc.ExpiryDate = myReader[4].ToString();
                }
            }
        }
    }
}
catch (SqlException)
{
    throw new FaultException<AuthenticationException>(new AuthenticationException("There was an error while connecting the database, please contact support."), new FaultReason("Error"));
}
catch (InvalidOperationException)
{
    throw new FaultException<AuthenticationException>(new AuthenticationException("An error in the program while connecting to the database."), new FaultReason("Error"));
}
catch (InvalidUsernameException)
{
    throw new FaultException<AuthenticationException>(new AuthenticationException("The username you entered could not be found in our database."), new FaultReason("Error"));
}
catch (Exception)
{
    throw new FaultException<AuthenticationException>(new AuthenticationException("There was a general error during the process."), new FaultReason("Error"));
}

Upvotes: 0

Views: 499

Answers (3)

Ian Horwill
Ian Horwill

Reputation: 3025

Looks like you need to call myReader.Read before accessing its fields.

Upvotes: 1

TheGateKeeper
TheGateKeeper

Reputation: 4540

I found the answer to my problem at this page:

http://sergecalderara.wordpress.com/2008/11/25/systemservicemodelfaultexception1-was-unhandled-by-user-code/

You need to disable these visual studio options:

enter image description here

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1039438

Try adding a parameterless constructor to the AuthenticationException class. Or:

[DataContract]
public class AuthenticationException
{
    [DataMember]
    public string ValidationError { get; set; }
}

and in your service:

throw new FaultException<AuthenticationException>(
    new AuthenticationException 
    { 
        ValidationError = "The username you entered could not be found in our database."
    }, 
    new FaultReason("Error")
);

Also bare in mind that this works only for exceptions that are occuring inside the try/catch block. If there's some other exception occurring in some other part of the service that is not translated to a FaultContract you won't catch it as FaultException<T> on the client.

Also it is recommended to centralize the exception handling of your WCF service at a single location by using a custom IErrorHandler that will propagate the faults instead of putting try/catch blocks all around your code.

Upvotes: 2

Related Questions