Skip to content

Instantly share code, notes, and snippets.

@pmhsfelix
Created December 7, 2010 01:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pmhsfelix/731300 to your computer and use it in GitHub Desktop.
Save pmhsfelix/731300 to your computer and use it in GitHub Desktop.
Service-side WCF asynchronous operation using Task<T>
// Note the OperationHasAsyncVersion attribute
[ServiceContract(Name = "IService")]
interface IService
{
[OperationContract]
[OperationHasAsyncVersion]
string Echo(string req);
Task<string> EchoAsync(string req);
}
// The operation behavior that changes the invoker
class OperationHasAsyncVersion : Attribute, IOperationBehavior
{
public void ApplyDispatchBehavior(
OperationDescription operationDescription,
System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
var type = operationDescription.DeclaringContract.ContractType;
var asyncMethod = type.GetMethod(operationDescription.SyncMethod.Name + "Async",
operationDescription.SyncMethod.GetParameters().Select(mi => mi.ParameterType).ToArray());
if (asyncMethod == null) throw new InvalidOperationException("No async method found");
Debug.Assert(dispatchOperation.Invoker != null);
var openType = typeof(AsyncTaskOperationInvoker<>);
var closedType = openType.MakeGenericType(new Type[] { operationDescription.SyncMethod.ReturnType });
dispatchOperation.Invoker = Activator.CreateInstance(closedType, asyncMethod, dispatchOperation.Invoker) as IOperationInvoker;
}
public void AddBindingParameters(
OperationDescription operationDescription,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
// nothing to do
}
public void ApplyClientBehavior(
OperationDescription operationDescription,
System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{
// nothing to do
}
public void Validate(OperationDescription operationDescription)
{
// nothing to do
}
}
// The invoker
class AsyncTaskOperationInvoker<T> : IOperationInvoker
{
private readonly MethodInfo _method;
private readonly IOperationInvoker _inner;
public AsyncTaskOperationInvoker(MethodInfo method, IOperationInvoker inner)
{
_method = method;
_inner = inner;
}
public object[] AllocateInputs()
{
return _inner.AllocateInputs();
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
var task = _method.Invoke(instance, inputs) as Task<T>;
Debug.Assert(task != null);
var arw = new AsyncResultWrapper<T>(state, task);
if (callback != null)
{
task.ContinueWith((t) => callback(arw));
}
return arw;
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
outputs = new object[0];
var arw = result as AsyncResultWrapper<T>;
Debug.Assert(arw != null);
return arw.Inner.Result;
}
public bool IsSynchronous
{
get { return false; }
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
throw new NotImplementedException();
}
}
// The AsyncResult wrapper that adds the state
class AsyncResultWrapper<T> : IAsyncResult
{
private readonly object _state;
private readonly Task<T> _inner;
private readonly IAsyncResult _ar;
public Task<T> Inner
{
get { return _inner; }
}
public AsyncResultWrapper(object state, Task<T> ar)
{
_state = state;
_inner = ar;
_ar = ar;
}
public object AsyncState
{
get { return _state; }
}
public WaitHandle AsyncWaitHandle
{
get { return _ar.AsyncWaitHandle; }
}
public bool CompletedSynchronously
{
get { return _ar.CompletedSynchronously; }
}
public bool IsCompleted
{
get { return _inner.IsCompleted; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment