Created
December 7, 2010 01:06
-
-
Save pmhsfelix/731300 to your computer and use it in GitHub Desktop.
Service-side WCF asynchronous operation using Task<T>
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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