开发者

Caliburn.Micro: Recovering from Exception in IResult

开发者 https://www.devze.com 2023-02-08 09:54 出处:网络
This was posted at Caliburn.Micro discussions also. I\'m really looking for advice and opinions on the best way to solve.

This was posted at Caliburn.Micro discussions also. I'm really looking for advice and opinions on the best way to solve.

Say I have the following Action

public IEnumerable<IResult> SaveStation()
{ 
    yield return Busy.MakeBusy();
    yield return new StationSave(_station);
    yield return Busy.MakeNotBusy();
    yield return Show.Tab<StationBrowseViewModel>();
}

StationSave is an IResult wrapper around a simple (WCF) service invocation. The service uses FaultContract/FaultException for failures.

In the case of a fault, the user needs to be notified and the FaultContract will contain some useful info about what went wrong. Currently the Save result catches the exception and inserts it into ResultCompletionEventArgs of the Completed event. By doing so, the SequentialResult created by the pipeline is cancelled (because of the error), thus leaving the screen in a Busy state.

What I'm really after is ideas about the best way to recover from the error (remove the busy state) and notify the user (I have a couple of IResult implementations for different styles of notification which I would like to use) of the details provided in the fault contract. By attaching to the Completed event in my VM I can get the error, but at this point I am no longer in the context of the Action pipeline so any IResults I would like to use (the MakeNotBusy and my show notification implementation) I have to execute manually (and I'd have to new up my own ActionExecutionContext which I don't want to be doing).

I have had a look at Marco Amendola's rescue filter for Caliburn.Micro from here, but 开发者_JAVA百科again I can't pass back IResults from the Rescue method.

Have I missed something obvious? How do others handle this situation?


Both Rob Eisenberg and Marco Amendola have provided possible solutions in the CodePlex forum.

I have chosen to take Marco's RescueAttribute from his filters implementation and modify it slightly to allow execution of further IResult from the rescue method. This is the required change to RescueAttribute.HandleException

protected override bool HandleException(ActionExecutionContext context,
                                        Exception ex)
{
    var method = context.Target
                        .GetType()
                        .GetMethod(MethodName, new[] { typeof(Exception) });
    if (method == null) return false;

    try
    {
        var result = method.Invoke(context.Target, new object[] { ex });

        if (result is bool)
            return (bool) result;

        if (result is IResult)
            result = new[] { result as IResult };
        if (result is IEnumerable<IResult>)
            Coroutine.Execute(((IEnumerable<IResult>) result).GetEnumerator(), context);
        else if (result is IEnumerator<IResult>)
            Coroutine.Execute(((IEnumerator<IResult>) result), context);

        return true;
    }
    catch
    {
        return false;
    }
}

this allows the following in my VM:

public IEnumerable<IResult> Rescue(Exception ex)
{
    yield return Busy.MakeNotBusy();
    // in practice pass exception details through to notification
    yield return new NotificationPopup("Save station failed");
}

[Rescue]
public IEnumerable<IResult> SaveStation()
{ 
    yield return Busy.MakeBusy();
    yield return new StationSave(_station);
    yield return Busy.MakeNotBusy();
    yield return Show.Tab<StationBrowseViewModel>();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消