exception handling via polymorphism

2019-07-15 00:14发布

I need a point of view in this design that I am proposing for handling exceptions.

I have a wcf service that throws different types of fault exceptions:

  1. throw new FaultException < BusinessRuleViolationFault >(...)
  2. throw new FaultException < SomeOtherViolationFault >(...)
  3. ...
  4. ...

All these fault exception classes implement an interface called IViolationFault, which has a single HandleException method. The implementation of each such method is different depending on the class they are in; so for example when these exception classes are caught in the client, all I need to do is just call that HandleException() method and it will be taken care of without me having to write an IF condition to differentiate between the types of errors. In BusinessRuleViolationFault's HandleException() method I might just want to show a message on the screen, while in the other one I might want to log it somewhere and trigger some other action as well...

catch (FaultException<BusinessRuleViolationFault> ex)
 {
ex.HandleException();
 }
catch (FaultException<SomeOtherViolationFault> ex)
 {
ex.HandleException();
 }

Questions

  1. Is there any thing wrong in this approach?
  2. Is there a way I can reduce the number of catch blocks and have just one catch block take care of all exception types?
  3. Is this a good object oriented way to acheive polymorphism while handling exceptions?

EDIT

Ive changed the code to inherit from a base class instead of implementing an interface. My BusinessRuleViolationFault class has the HandleException method, but I am not able to get the HandleException method in the client catch block. What am doing wrong? This is how it is thrown in the service

BusinessRuleViolationFault bf = new BusinessRuleViolationFault("");
throw new FaultException<BusinessRuleViolationFault>(bf, new FaultReason(new FaultReasonText("Fault reason here")));

This is my BusinessRuleViolationFault code

[DataContract]
public class BusinessRuleViolationFault : BaseFault
{
    public BusinessRuleViolationFault(string message)
        : base(message)
    {

    }


    [OperationContract]
    public override string HandleException()
    {
        return "BusinessRuleViolationFault executed";
    }
}



[DataContract]
public abstract class BaseFault
{
    public BaseFault(string message)
    {
        Message = message;
    }

    [DataMember]
    public string Message { get; set; }

    [OperationContract]
    public abstract string HandleException();
}

Do let me have your thoughts on this. Thanks for your time...

标签: .net wcf oop
3条回答
Deceive 欺骗
2楼-- · 2019-07-15 00:56

There is nothing wrong with this approach, it is just a normal use of polymorphism.

You can reduce the number of catch blocks by having all your custom exception classes derive from a base exception class instead of just implementing an interface. For example:

public abstract class FaultException : Exception
{
    public abstract void HandleException()
}

public class Faultexception<T> : FaultException
{
     public override void HandleException()
     {
      //your code here
     }
}

This way you can catch all your exceptions with just the following:

catch (FaultException ex) 
{
     ex.HandleException();
}
查看更多
Emotional °昔
3楼-- · 2019-07-15 01:00

EDIT: Correct sample code.

At least with WCF, where this is the way to implement custom faults, there is nothing wrong to do it that way. Although, you loose polymorphism, as you cannot to something like this

// Does not work
try
{
}
catch (FaultException<IVoliationFault> ex)
{
  ex.HandleException();
}

to catch your FaultExceptions.

You also (obviously) cannot change the base class System.ServiceModel.FaultException<TDetail> or even System.ServiceModel.FaultException to include any HandleException() method.

What you can do is, to work with reflection to extract the TDetail of the FaultException, if any, and then work with that:

        try
        {
        }
        catch (FaultException ex)
        {
            var detail = ex.GetDetail<IViolationFault>(); // EDIT: Specify type

            if (detail != null)
            {
                detail.HandleException();
            }
            else
            {
                // Either not a FaultException<TDetail>, or not FaultException<IViolationFault>.
                throw;
            }
        }
...

public static class FaultExceptionExtensions
{
    public static T GetDetail<T>(this FaultException exception)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        Type type = exception.GetType(); // EDIT: use "exception" instead of "ex"
        if (!type.IsGenericType)
        {
            return default(T);
        }

        Type genType = type.GetGenericArguments()[0];

        PropertyInfo pi = type.GetProperty("Detail", genType);
        Debug.Assert(pi != null, "FaultException<" + genType + ">.Detail property is missing");

        object val = pi.GetValue(exception, null);

        if (!typeof(T).IsInstanceOfType(val))
        {
            return default(T);
        }

        return (T)val;
    }

}
查看更多
劫难
4楼-- · 2019-07-15 01:11

You might want to take a look at the functional approach documented in this blog post that allows you to make your try/catch logic a little clearer/terse. eg:

TryCatch.Try(() =>
         {
             using (var stream = File.OpenRead(FileTextBox.Text))
             {
                 Trace.WriteLine(stream.Length);
             }
         })
.Catch<FileNotFoundException,
    DirectoryNotFoundException,
    UnauthorizedAccessException>
    (ex =>
    {
        MessageBox.Show(ex.Message, "Fail");
    });
查看更多
登录 后发表回答