1

I'm working on a project that uses several WCF calls to perform a procedure. The first call returns a unique ID (Guid) which all of the following calls must supply. Server-side, each call is logged together with that unique ID so that the course of a single procedure can be watched (suppose that a client can begin several of those procedures at the same time and they are thread-safe).

A client-side example would be something like the following:

string clientName = GetClientName();
Guid uniqueId = wcfClient.Begin(clientName);
object data = wcfClient.GetData(clientName, uniqueId);

object processedData = ProcessDataLocally(data);

bool success = wcfClient.confirmDataValidity(clientName, uniqueId, processedData);

// ...

wcfClient.End(clientName, uniqueId);

If something goes wrong on the server side, I'd like that to be logged as well, using the uniqueId given to the call that went wrong. For that purpose, I made all the methods of the service use a function like the following:

private T DoProcess<T>(Guid uniqueId, Func<T> process)
{
    try
    {
        return process();
    }
    catch(Exception ex)
    {
        Log(ex, uniqueId);

        throw; // or throw the same thing every time, if the client
               // shouldn't know the details
    }
}

public object GetData(string clientName, Guid uniqueId)
{
    return DoProcess(uniqueId, () =>
    {
        object data;

        // Generate data

        return data;
    });
}

However, it seems that doing so doesn't catch all possible errors (for example, if the connection times out).

I read about implementing the IErrorHandler interface and attaching it to the service. While this seems to catch all errors, there seems to be no way to pass information to it other than the exception, such as the uniqueId. Therefore there is no way to log the ID.

Is it possible to catch all exceptions and include data that has been passed to the method in which the exception was thrown?

George T
  • 698
  • 9
  • 18

1 Answers1

2

An exception like Connection timed out does not arrive at the service so there is no way to catch that at the service side.

With that said, you can use IExtension to add extra information to the current OperationContext. The basic pattern for that is something like this:

public class OperationContextExtension : IExtension<OperationContext>
{
    private readonly Dictionary<string, object> extraInfo;

    public OperationContextExtension ()
    {
        extraInfo = new Dictionary<string, object>();
    }

    public static OperationContextExtension Current
    {
        get
        {
            OperationContextExtension c = OperationContext.Current.Extensions.Find<OperationContextExtension>();
            if (c == null)
            {
                c = new OperationContextExtension ();
                OperationContext.Current.Extensions.Add(c);
            }
            return c;
        }
    }

    public Dictionary<string, object> Extras
    {
        get { return extraInfo; }
    }

    public void Attach(OperationContext owner) { }

    public void Detach(OperationContext owner) { }
}

And then use that class in an IErrorHandler implementation :

class FaultErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return false;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
      Guid ID = OperationContextExtension.Current.Extras["uniqueId"] as Guid;

      LogIt(error.ToString() + ID);
    }
}

And then in GetData

public object GetData(string clientName, Guid uniqueId)
{
    OperationContextExtension.Current.Extras["uniqueId"] = uniqueId;
    return DoProcess(uniqueId, () =>
    {
        object data;

        // Generate data

        return data;
    });
}
Serve Laurijssen
  • 9,266
  • 5
  • 45
  • 98
  • `Current` is a static field; is it safe to use it while several calls could be happening at the same time? – George T Aug 30 '16 at 13:27
  • 1
    Yes these kinds of ambient values are common in WCF. Every thread gets a different value (if you have PerCall or PerSession configured) – Serve Laurijssen Aug 31 '16 at 11:11