5

In our external boundaries that expose WCF services, we convert all internal exceptions to FaultException. This is a manual process which often has minor flaws unique to each implementation. It has been copy/pasted and mindlessly modified (or forgotten) for each exposed method. To reduce the errors, I wanted to create an aspect which would catch any unhandled exception.

We do a mapping of internal exceptions to fault exceptions. How can I send a mapping function to the aspect?

If I add a property to the aspect like this:

[Serializable]
public sealed class FaultExceptionConverter : OnExceptionAspect {
    public Func<Exception, FaultException> FaultConverter { get; set }
}

I cannot (as expected by Attribute restrictions) initialize it like [FaultExceptionConverter(FaultConverter = MyConversionMethod)] (where MyConversionMethod is some method assignable to Func<Exception, FaultException>). Is there any pattern for passing this type of parameter to the aspect? Many types can be passed into aspects. Is this a common problem?

If there happens to be a better way to accomplish this, I would appreciate the advice.

Ryan Gates
  • 4,501
  • 6
  • 50
  • 90
carlpett
  • 12,203
  • 5
  • 48
  • 82
  • 2
    I did provide an answer to your question below, but then I thought - why does your aspect itself not implement the required conversion routine? That would certainly simplify things. Unless you have multiple ways of constructing a FaultException from an Exception this actually seems the more appropriate way to do this? – RJ Lohan Dec 12 '12 at 20:48
  • @RJLohan: This is intended to be a generally usable aspect. We have over a hundred integration points which all have different fault contracts with their consumers which are in turn spread over some 20-30 organizations. Needless to say, it would be hard to craft such a general conversion routine :) – carlpett Dec 12 '12 at 20:53

2 Answers2

2

I've encountered similar frustrating limitations in aspect implementation, and one approach I've used to get around them is to have the aspect treat the class it is implemented on as some 'provider' type, which it can callback on to request other bits at runtime.

So, in your case, I imagine an OnException override that looks something like;

public override void OnException(MethodExecutionArgs args)
{
    IFaultConverterProvider provider = args.Instance as IFaultConverterProvider;
    if (null != provider)
        Func<Exception, FaultException>exceptionConverterFunc = provider.GetFunc();
}

Where IFaultConverterProvider is some interface you define and implement on the attributed type to provide the extra parameters you are missing.

Then, as a bit of a sanity check, you can introduce some compile-time validation into your aspect to ensure that the type it is applied to actually implements this required interface;

public override bool CompileTimeValidate(Type type)
{
    if (!type.IsImplementationOf(typeof(IFaultConverterProvider )))
    {
        // The aspect must be in a type which implements IFaultConverterProvider 
        Message.Write(
            MessageLocation.Of(type),
            SeverityType.Error,
            "CUSTOM02",
            "Cannot apply [MyFaultExceptionAspect] to type {0} because it does not implement IFaultConverterProvider .", type);
            return false;
    }

    return true;
}
RJ Lohan
  • 6,497
  • 3
  • 34
  • 54
  • This is quite a clever approach. The only drawback I can see right off is if the user (service developer) would want to provide different mapping logic on a per method basis... I will think on that, for with that exception (pun not intended) it should solve the problem. – carlpett Dec 12 '12 at 20:56
0

Unfortunately, you can pass only bool, byte, char, short, int, long, float, double, string, Type, and enumerators to Attribute.

As a workaround, you can pass typeof(FaultConverterClass) as Type and "MyConversionMethod" as string and call your conversion method using reflection (of course, only in case if this method is static). It sounds quite "dirty", but you can add compile time validation that method exists and have expected signature.

Alexander Bortnik
  • 1,219
  • 14
  • 24