Răsfoiți Sursa

从 ResilientUtils 迁移到 Retrier

龙腾猫跃 4 luni în urmă
părinte
comite
0bdeb20c0e
3 a modificat fișierele cu 18 adăugiri și 82 ștergeri
  1. 2 2
      Shared/Utils/DirectoryUtils.cs
  2. 16 14
      Shared/Utils/FileUtils.cs
  3. 0 66
      Shared/Utils/ResilientUtils.cs

+ 2 - 2
Shared/Utils/DirectoryUtils.cs

@@ -103,7 +103,7 @@ public static class DirectoryUtils {
             // 同一磁盘剪切,直接调用 Move(这只修改文件夹名,效率更高)
             Logger.Trace($"剪切文件夹到同一磁盘:{sourceFolder} → {destFolder}");
             DirectoryUtils.Delete(destFolder); // Move 要求此前不存在对应文件夹
-            ResilientUtils.RetryOn<IOException>(() => {
+            Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
                 FileUtils.SetReadOnly(sourceFolder, false);
                 Directory.Move(PathUtils.ForApi(sourceFolder), PathUtils.ForApi(destFolder));
             });
@@ -144,7 +144,7 @@ public static class DirectoryUtils {
                 folder = PathUtils.ForApi(folder);
                 foreach (string filePath in DirectoryUtils.EnumerateFiles(folder)) FileUtils.Delete(filePath); // 删除文件
                 foreach (string str in DirectoryUtils.EnumerateDirectories(folder)) DeleteInternal(str); // 递归删除子文件夹
-                ResilientUtils.RetryOn<IOException>(() => {
+                Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
                     FileUtils.SetReadOnly(folder, false);
                     Directory.Delete(folder, true);
                 }); // 最终删除文件夹

+ 16 - 14
Shared/Utils/FileUtils.cs

@@ -12,7 +12,8 @@ public static class FileUtils {
     /// </summary>
     public static Stream ReadAsStream(string filePath, Type? type = null) {
         if (type is null) {
-            return ResilientUtils.RetryOn<IOException, Stream>(() => new FileStream(PathUtils.ForApi(filePath), FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
+            return Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, func: _ 
+                => new FileStream(PathUtils.ForApi(filePath), FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
         } else {
             return type.Assembly.GetManifestResourceStream(filePath) ?? throw new FileNotFoundException($"Embedded resource \"{filePath}\" was not found.");
         }
@@ -93,7 +94,7 @@ public static class FileUtils {
     public static void Write(string filePath, byte[] content) {
         DirectoryUtils.Create(PathUtils.RemoveLastPart(filePath));
         Logger.Trace($"写入文件:{filePath}({content.Length} 字节)");
-        ResilientUtils.RetryOn<IOException>(() => {
+        Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
             FileUtils.Delete(filePath);
             File.WriteAllBytes(PathUtils.ForApi(filePath), content);
         });
@@ -105,7 +106,7 @@ public static class FileUtils {
     /// 若文件已存在,则会覆盖原文件。
     /// </summary>
     public static void Write(string filePath, Stream stream) {
-        ResilientUtils.RetryOn<IOException>(() => {
+        Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
             FileUtils.Delete(filePath);
             using FileStream fileStream = FileUtils.CreateAsStream(filePath);
             if (stream.CanSeek && stream.Position != 0) stream.Seek(0, SeekOrigin.Begin);
@@ -120,7 +121,7 @@ public static class FileUtils {
     public static FileStream CreateAsStream(string filePath) {
         DirectoryUtils.Create(PathUtils.RemoveLastPart(filePath));
         Logger.Trace($"创建文件流:{filePath}");
-        return ResilientUtils.RetryOn<IOException, FileStream>(() => {
+        return Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, func: _ => {
             FileUtils.Delete(filePath);
             return new FileStream(PathUtils.ForApi(filePath), FileMode.Create);
         });
@@ -130,7 +131,8 @@ public static class FileUtils {
     /// 在临时文件夹下创建一个随机名称的文件,并返回其路径。
     /// </summary>
     public static string CreateRandom()
-        => ResilientUtils.RetryOn<IOException, string>(Path.GetTempFileName);
+        => Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, func: _ 
+            => Path.GetTempFileName());
 
     #endregion
 
@@ -156,7 +158,7 @@ public static class FileUtils {
             DirectoryUtils.Create(PathUtils.RemoveLastPart(destFilePath));
             Logger.Trace($"复制文件:{sourceFilePath} → {destFilePath}");
             if (FileUtils.Exists(destFilePath)) FileUtils.SetReadOnly(destFilePath, false);
-            ResilientUtils.RetryOn<IOException>(() 
+            Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ 
                 => File.Copy(PathUtils.ForApi(sourceFilePath), PathUtils.ForApi(destFilePath), true));
         }
     }
@@ -174,7 +176,7 @@ public static class FileUtils {
         } else if (string.Compare(sourceFilePath, destFilePath, ignoreCase: true) == 0) {
             // 路径仅大小写不同
             Logger.Trace($"剪切文件到自身,但大小写不同:{sourceFilePath} → {destFilePath}");
-            ResilientUtils.RetryOn<IOException>(() => {
+            Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
                 var temp = Path.Combine(PathUtils.RemoveLastPart(sourceFilePath), Path.GetRandomFileName());
                 FileUtils.SetReadOnly(sourceFilePath, false);
                 if (FileUtils.Exists(destFilePath)) FileUtils.SetReadOnly(destFilePath, false);
@@ -186,7 +188,7 @@ public static class FileUtils {
             DirectoryUtils.Create(PathUtils.RemoveLastPart(destFilePath));
             FileUtils.Delete(destFilePath);
             Logger.Trace($"剪切文件:{sourceFilePath} → {destFilePath}");
-            ResilientUtils.RetryOn<IOException>(() => {
+            Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
                 FileUtils.SetReadOnly(sourceFilePath, false);
                 if (FileUtils.Exists(destFilePath)) FileUtils.SetReadOnly(destFilePath, false);
                 File.Move(PathUtils.ForApi(sourceFilePath), PathUtils.ForApi(destFilePath));
@@ -220,7 +222,7 @@ public static class FileUtils {
             }
         }
         // 永久删除
-        ResilientUtils.RetryOn<IOException>(() => {
+        Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => {
             FileUtils.SetReadOnly(filePath, false);
             File.Delete(PathUtils.ForApi(filePath));
         });
@@ -251,12 +253,12 @@ public static class FileUtils {
         }
         // 在 STA 线程中执行删除方法
         if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) {
-            ResilientUtils.RetryOn<IOException>(Run);
+            Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ => Run());
         } else {
             System.Runtime.ExceptionServices.ExceptionDispatchInfo? internalEx = null; // 捕获内部异常
             var thread = new Thread(() => { 
                 try { 
-                    ResilientUtils.RetryOn<IOException, COMException>(Run); 
+                    Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException or COMException, action: _ => Run()); 
                 } catch (Exception ex) { 
                     internalEx = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex); 
                 } 
@@ -375,7 +377,7 @@ public static class FileUtils {
         DirectoryUtils.Create(PathUtils.RemoveLastPart(outputFullPath));
         FileUtils.Delete(outputFullPath);
         Logger.Trace($"将文件夹中的内容压缩为 zip 文件:{sourceDirectory} → {outputFullPath}");
-        ResilientUtils.RetryOn<IOException>(()
+        Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, action: _ 
             => ZipFile.CreateFromDirectory(PathUtils.ForApi(sourceDirectory), PathUtils.ForApi(outputFullPath)));
     }
 
@@ -401,7 +403,7 @@ public static class FileUtils {
     public static void CreateZipFromFiles(string outputFullPath, IDictionary<string, string> sources) {
         DirectoryUtils.Create(PathUtils.RemoveLastPart(outputFullPath));
         FileUtils.Delete(outputFullPath);
-        using var archive = ResilientUtils.RetryOn<IOException, ZipArchive>(()
+        using var archive = Retrier.Attempt(delay: _ => TimeSpan.FromMilliseconds(200), isRetryAllowed: ex => ex is IOException, func: _ 
             => ZipFile.Open(PathUtils.ForApi(outputFullPath), ZipArchiveMode.Create));
         Logger.Trace($"创建 zip 文件:{sources.Count} 个文件 → {outputFullPath}\n{sources.Select(p => $"- {p.Value} → {p.Key}").Join('\n')}");
         foreach (var pair in sources) archive.CreateEntryFromFile(PathUtils.ForApi(pair.Value), pair.Key.Replace('\\', '/'), CompressionLevel.Optimal);
@@ -523,4 +525,4 @@ public class FileChecker {
             return ex.GetDisplay(false);
         }
     }
-}
+}

+ 0 - 66
Shared/Utils/ResilientUtils.cs

@@ -1,66 +0,0 @@
-namespace MeloongCore;
-public static class ResilientUtils {
-
-    /// <summary>
-    /// 在抛出任意异常时,延迟并自动重试。
-    /// </summary>
-    public static void Retry(Action action, int maxAttempts = 2, int delayMs = 200, [CallerMemberName] string caller = "")
-        => RetryOn<Exception>(action, maxAttempts, delayMs, caller);
-    /// <summary>
-    /// 在抛出特定异常时,延迟并自动重试。
-    /// </summary>
-    public static void RetryOn<TException>(Action action, int maxAttempts = 2, int delayMs = 200, [CallerMemberName] string caller = "") where TException : Exception 
-        => RetryOn<TException, object?>(() => { action(); return null; }, maxAttempts, delayMs, caller);
-    /// <summary>
-    /// 在抛出特定异常时,延迟并自动重试。
-    /// </summary>
-    public static void RetryOn<TException1, TException2>(Action action, int maxAttempts = 2, int delayMs = 200, [CallerMemberName] string caller = "") where TException1 : Exception where TException2 : Exception
-        => RetryOn<TException1, TException2, object?>(() => { action(); return null; }, maxAttempts, delayMs, caller);
-
-    /// <summary>
-    /// 在抛出任意异常时,延迟并自动重试。
-    /// </summary>
-    public static void Retry<TOut>(Func<TOut> func, int maxAttempts = 2, int delayMs = 200, [CallerMemberName] string caller = "")
-        => RetryOn<Exception, TOut>(func, maxAttempts, delayMs, caller);
-    /// <summary>
-    /// 在抛出特定异常时,延迟并自动重试。
-    /// </summary>
-    public static TOut RetryOn<TException, TOut>(Func<TOut> func, int maxAttempts = 2, int delayMs = 200, [CallerMemberName] string caller = "") where TException : Exception {
-        int attempt = 0;
-        while (true) {
-            try {
-                return func(); // 成功则退出
-            } catch (TException ex) {
-                attempt++;
-                if (attempt < maxAttempts) {
-                    Logger.Warn(ex, $"第 {attempt} 次 {caller} 尝试失败,将在 {delayMs}ms 后重试");
-                    if (delayMs > 0) Thread.Sleep(delayMs);
-                } else { // 超过最大尝试次数
-                    Logger.Warn(ex, $"第 {attempt} 次 {caller} 尝试失败,已到达最大尝试次数");
-                    throw;
-                }
-            }
-        }
-    }
-    /// <summary>
-    /// 在抛出特定异常时,延迟并自动重试。
-    /// </summary>
-    public static TOut RetryOn<TException1, TException2, TOut>(Func<TOut> func, int maxAttempts = 2, int delayMs = 200, [CallerMemberName] string caller = "") where TException1 : Exception where TException2 : Exception {
-        int attempt = 0;
-        while (true) {
-            try {
-                return func(); // 成功则退出
-            } catch (Exception ex) when (ex is TException1 or TException2) {
-                attempt++;
-                if (attempt < maxAttempts) {
-                    Logger.Warn(ex, $"第 {attempt} 次 {caller} 尝试失败,将在 {delayMs}ms 后重试");
-                    if (delayMs > 0) Thread.Sleep(delayMs);
-                } else { // 超过最大尝试次数
-                    Logger.Warn(ex, $"第 {attempt} 次 {caller} 尝试失败,已到达最大尝试次数");
-                    throw;
-                }
-            }
-        }
-    }
-
-}