UPDATE 2011/01/03
Fixed a couple bugs:
- Missing exception handling on callback execution
- Missing null check on Message property
Many thanks to Luca D’Angelo for pointing them out.
A user recently asked, on Caliburn.Micro forum, for an equivalent version of WebServiceResult, a convenience IResult built by Rob in the (full) Caliburn’s Contact Manager sample.
Porting was quite straightforward, despite the important difference in the framework internal structure.
Here is the class source:
using System;
using System.Linq;
using System.Linq.Expressions;
public class WebServiceResult<T, K> : IResult
where T : new()
where K : EventArgs<
{
readonly static Func<bool> ALWAYS_FALSE_GUARD= () => false;
readonly static Func<bool> ALWAYS_TRUE_GUARD = () => true;
private readonly Action<K> _callback;
private readonly Expression<Action<T>> _serviceCall;
private ActionExecutionContext _currentContext;
private Func<bool> _originalGuard;
public WebServiceResult(Expression<Action<T>> serviceCall)
{
_serviceCall = serviceCall;
}
public WebServiceResult(Expression<Action<T>> serviceCall, Action<K> callback)
{
_serviceCall = serviceCall;
_callback = callback;
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public void Execute(ActionExecutionContext context)
{
_currentContext = context;
//if you would to disable the control that caused the service to be called, you could do this:
ChangeAvailability(false);
var lambda = (LambdaExpression)_serviceCall;
var methodCall = (MethodCallExpression)lambda.Body;
var eventName = methodCall.Method.Name.Replace("Async", "Completed");
var eventInfo = typeof(T).GetEvent(eventName);
var service = new T();
eventInfo.AddEventHandler(service, new EventHandler<K>(OnEvent));
_serviceCall.Compile()(service);
}
public void OnEvent(object sender, K args)
{
//re-enable the control that caused the service to be called:
ChangeAvailability(true);
try {
if (_callback != null)
_callback(args);
Completed(this, new ResultCompletionEventArgs());
} catch (Exception ex) {
Completed(this, new ResultCompletionEventArgs{ Error = ex });
}
}
private void ChangeAvailability(bool isAvailable)
{
if (_currentContext == null || _currentContext.Message == null) return;
if (!isAvailable) {
_originalGuard = _currentContext.CanExecute;
_currentContext.CanExecute = ALWAYS_FALSE_GUARD;
}
else if (_currentContext.CanExecute == ALWAYS_FALSE_GUARD) {
_currentContext.CanExecute = _originalGuard ?? ALWAYS_TRUE_GUARD;
}
_currentContext.Message.UpdateAvailability();
}
}
The invocation:
//in the service code
public class MyService
{
[OperationContract]
public int DoWork(int value)
{
return new Random().Next();
}
}
//in the calling ViewModel code
public IEnumerable<IResult> CallService()
{
int theParameter = 0;
yield return new WebServiceResult<MyServiceClient, DoWorkCompletedEventArgs>(
x => x.DoWorkAsync(theParameter),
x => UseResultElsewhere(x.Result)
);
}
Advertisement