During network operations and IO, there are often limitations that can be easily solved even by the user (albeit experienced, yes), but it is difficult to learn about them in advance. A busy file, an unstable connection - I often lack the buttons to repeat, especially when the whole process takes and rolls back.
So far, on my knee, I have implemented a more or less universal solution to this problem:
public enum ExceptionHandle { Abort, Retry, Ignore } public class ExceptionEventArgs { public Exception Exception { get; } public ExceptionHandle? Handled { get; set; } public ExceptionEventArgs(Exception ex) { this.Exception = ex; } } public static class ExceptionHandler { public static event EventHandler<ExceptionEventArgs> Handler; public static void TryExecute(Action action) { TryExecute(() => { action(); return true; }, false); } public static T TryExecute<T>(Func<T> action, T whenIgnored) { ExceptionHandle? handled = ExceptionHandle.Retry; while (handled == ExceptionHandle.Retry) { try { return action(); } catch (Exception ex) { handled = OnHandler(new ExceptionEventArgs(ex)); if (handled.HasValue) { switch (handled.Value) { case ExceptionHandle.Abort: throw; break; case ExceptionHandle.Retry: break; case ExceptionHandle.Ignore: break; default: throw new ArgumentOutOfRangeException(); } } else { throw; } } } return whenIgnored; } private static ExceptionHandle? OnHandler(ExceptionEventArgs e) { if (Handler == null || !Handler.GetInvocationList().Any()) { ExceptionDispatchInfo.Capture(e.Exception).Throw(); } else { Handler.Invoke(null, e); } return e.Handled; } } Thus, any ExceptionHandler.Handler subscriber can either resolve the problems automatically, or throw the solution on the user. Any dangerous code can now be wrapped:
var tested = ExceptionHandler.TryExecute(() => { using (var destination = new MemoryStream()) { using (Stream stream = entry.Open()) stream.CopyTo(destination); return destination.Length == entry.Length; } }, false); In general, the current implementation seems to me already tolerable and it works. But, I suspect that such solutions are already there somewhere, I just could not find them. Can someone advise where to get or at least look at ready-made solutions? Well, if there are jambs in my code, I would not refuse help either.
UPD: Yes, I understand that even so there are problem situations - the action can be one-time (close the connection, crash the sql session, and whatever you want to do). It already remains on conscience of the one who uses the code. Although, I would also look at interesting options on this issue, you will limit this fig.
UPD2: I have not yet been able to figure out whether it is possible to wrap one such unit in another, but now as a result, on the abortion of the internal unit, the external unit goes back to processing.