Mark Kadlec
Mark Kadlec

Reputation: 8450

How do you return value from WCF call if you are using generic System.action() call?

In order to reduce the amount of code our team put in a StartProcess method to call the WCF implementation for all calls from a single method. It all works great, but since the actual call from the generic method uses a System.Action to invoke, it can never return a value and since one of the calls I am making I would like to make a request/reply, I can't get that return value.

Here is the Generic method to handle the WCF requests:

public static ProcessResultDC StartProcess<T>(T setupData, string processName, Action<T> action, string exteriorAccountNumber, string companyCode) where T : IAmValidatable, IHaveAProcessGuid {

    var result = new ProcessResultDC { Status = ProcessStatusEnum.Accepted };
    // Do some authentication stuff here

    try {

        action(setupData);    // <-- This returns void, but I would like to return a string to the client

    } catch (Exception exc) {
        result.Status = ProcessStatusEnum.Error;
        result.Messages.Add(exc.Message);
    }
    return result;
}

And here is the client call:

var createOnDemandDataDc = new CreateOnDemandDataDc { RawData = punchRawData };
var result = TimeForceProcessHelper.StartProcess(createOnDemandDataDc, "Create On Demand Data", x => new CreateOnDemandDataProxy().CreateOnDemandData(createOnDemandDataDc), "NA", companyCode);

The result variable has no way of knowing about the string from the action call, even though my createOnDemandDataDc implementation returns a string.

Is there a better way to do this? Or can I simply do something other than an Action() call in order to get the return string?

Upvotes: 0

Views: 356

Answers (2)

Nick Butler
Nick Butler

Reputation: 24383

This is how I do AOP too. You need two methods for each aspect - one for an Action and one for a Func

If you write the one for Func first:

public static ProcessResultDC StartProcess<T, TResult>(
  T setupData,
  string processName,
  Func<T, TResult> fn,
  string exteriorAccountNumber,
  string companyCode
)
  where T : IAmValidatable, IHaveAProcessGuid
{
  var result = new ProcessResultDC { Status = ProcessStatusEnum.Accepted };

  // Do some authentication stuff here

  try
  {
    result.Result = fn(setupData);
  }
  catch (Exception exc)
  {
    result.Status = ProcessStatusEnum.Error;
    result.Messages.Add(exc.Message);
  }

  return result;
}

Then you can write the one for Action to use the Func implementation:

public static ProcessResultDC StartProcess<T>(
  T setupData,
  string processName,
  Action<T> fn,
  string exteriorAccountNumber,
  string companyCode
)
  where T : IAmValidatable, IHaveAProcessGuid
{
  return StartProcess(
    setupData,
    processName,
    t => { fn( t ); return 0; },
    exteriorAccountNumber,
    companyCode
  );
}

Upvotes: 1

dthorpe
dthorpe

Reputation: 36082

Instead of using Action<T> for the callback param, you could use Func<T, TResult> to allow the callback to return a value to the StartProcess caller. However, this will require that all your callbacks would need to be changed to functions that return a value. Probably not a happy thought if you only have one callback that needs to return a reply.

Another somewhat dirty approach might be to write your response to the currently active OperationContext directly in your CreateOnDemandDataDc callback function. Look at OperationContext.Current.RequestContext.Reply and related properties/methods.

Upvotes: 2

Related Questions