Reputation: 4540
Following the tutorial here
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
Reputation: 3025
Looks like you need to call myReader.Read before accessing its fields.
Upvotes: 1
Reputation: 4540
I found the answer to my problem at this page:
You need to disable these visual studio options:
Upvotes: 0
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