Browse Source

异步for循环支持取消口令,集合新增ToTree方法

懒得勤快 4 years ago
parent
commit
cd43463e9c

+ 90 - 72
Masuit.Tools.Abstractions/Extensions/BaseType/IEnumerableExtensions.cs

@@ -10,76 +10,6 @@ namespace Masuit.Tools
 {
     public static partial class IEnumerableExtensions
     {
-        /// <summary>
-        /// 遍历IEnumerable
-        /// </summary>
-        /// <param name="objs"></param>
-        /// <param name="action">回调方法</param>
-        /// <typeparam name="T"></typeparam>
-        public static void ForEach<T>(this IEnumerable<T> objs, Action<T> action)
-        {
-            foreach (var o in objs)
-            {
-                action(o);
-            }
-        }
-
-        /// <summary>
-        /// 异步foreach
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="source"></param>
-        /// <param name="maxParallelCount">并行线程数</param>
-        /// <param name="action"></param>
-        /// <returns></returns>
-        public static async Task ForeachAsync<T>(this IEnumerable<T> source, int maxParallelCount, Func<T, Task> action)
-        {
-            if (source.Any())
-            {
-                using SemaphoreSlim completeSemphoreSlim = new(1);
-                using SemaphoreSlim taskCountLimitsemaphoreSlim = new(maxParallelCount);
-                await completeSemphoreSlim.WaitAsync();
-                int runningtaskCount = source.Count();
-
-                foreach (var item in source)
-                {
-                    await taskCountLimitsemaphoreSlim.WaitAsync();
-                    Task.Run(async () =>
-                    {
-                        try
-                        {
-                            await action(item).ContinueWith(_ =>
-                            {
-                                Interlocked.Decrement(ref runningtaskCount);
-                                if (runningtaskCount == 0)
-                                {
-                                    completeSemphoreSlim.Release();
-                                }
-                            });
-                        }
-                        finally
-                        {
-                            taskCountLimitsemaphoreSlim.Release();
-                        }
-                    });
-                }
-
-                await completeSemphoreSlim.WaitAsync();
-            }
-        }
-
-        /// <summary>
-        /// 异步foreach
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="source"></param>
-        /// <param name="action"></param>
-        /// <returns></returns>
-        public static Task ForeachAsync<T>(this IEnumerable<T> source, Func<T, Task> action)
-        {
-            return ForAsync(source, (t, _) => action(t));
-        }
-
         /// <summary>
         /// 按字段去重
         /// </summary>
@@ -216,6 +146,62 @@ namespace Masuit.Tools
             return set;
         }
 
+        /// <summary>
+        /// 遍历IEnumerable
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <param name="action">回调方法</param>
+        /// <typeparam name="T"></typeparam>
+        public static void ForEach<T>(this IEnumerable<T> objs, Action<T> action)
+        {
+            foreach (var o in objs)
+            {
+                action(o);
+            }
+        }
+
+        /// <summary>
+        /// 异步foreach
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="maxParallelCount">最大并行数</param>
+        /// <param name="action"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public static async Task ForeachAsync<T>(this IEnumerable<T> source, Func<T, Task> action, int maxParallelCount, CancellationToken cancellationToken = default)
+        {
+            var list = new List<Task>();
+            foreach (var item in source)
+            {
+                if (cancellationToken.IsCancellationRequested)
+                {
+                    return;
+                }
+
+                list.Add(action(item));
+                if (list.Count >= maxParallelCount)
+                {
+                    await Task.WhenAll(list);
+                    list.Clear();
+                }
+            }
+
+            await Task.WhenAll(list);
+        }
+
+        /// <summary>
+        /// 异步foreach
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="action"></param>
+        /// <returns></returns>
+        public static Task ForeachAsync<T>(this IEnumerable<T> source, Func<T, Task> action, CancellationToken cancellationToken = default)
+        {
+            return ForeachAsync(source, action, source.Count(), cancellationToken);
+        }
+
         /// <summary>
         /// 异步Select
         /// </summary>
@@ -248,10 +234,42 @@ namespace Masuit.Tools
         /// <typeparam name="T"></typeparam>
         /// <param name="source"></param>
         /// <param name="selector"></param>
+        /// <param name="maxParallelCount">最大并行数</param>
+        /// <param name="cancellationToken">取消口令</param>
         /// <returns></returns>
-        public static Task ForAsync<T>(this IEnumerable<T> source, Func<T, int, Task> selector)
+        public static async Task ForAsync<T>(this IEnumerable<T> source, Func<T, int, Task> selector, int maxParallelCount, CancellationToken cancellationToken = default)
         {
-            return Task.WhenAll(source.Select(selector));
+            var list = new List<Task>();
+            int index = 0;
+            foreach (var item in source)
+            {
+                if (cancellationToken.IsCancellationRequested)
+                {
+                    return;
+                }
+
+                list.Add(selector(item, index++));
+                if (list.Count >= maxParallelCount)
+                {
+                    await Task.WhenAll(list);
+                    list.Clear();
+                }
+            }
+
+            await Task.WhenAll(list);
+        }
+
+        /// <summary>
+        /// 异步For
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="selector"></param>
+        /// <param name="cancellationToken">取消口令</param>
+        /// <returns></returns>
+        public static Task ForAsync<T>(this IEnumerable<T> source, Func<T, int, Task> selector, CancellationToken cancellationToken = default)
+        {
+            return ForAsync(source, selector, source.Count(), cancellationToken);
         }
 
         /// <summary>

+ 3 - 3
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -7,7 +7,7 @@
     <LangVersion>9.0</LangVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <CodeAnalysisRuleSet />
-    <Version>2.4.3.1</Version>
+    <Version>2.4.3.2</Version>
     <Authors>懒得勤快</Authors>
     <Description>Masuit.Tools基础公共库</Description>
     <Copyright>懒得勤快,长空X</Copyright>
@@ -20,9 +20,9 @@
     <RepositoryType>Github</RepositoryType>
     <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
     <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
-    <FileVersion>2.4.3.1</FileVersion>
+    <FileVersion>2.4.3.2</FileVersion>
     <Company>ldqk.org</Company>
-    <AssemblyVersion>2.4.3.1</AssemblyVersion>
+    <AssemblyVersion>2.4.3.2</AssemblyVersion>
     <PackageLicenseUrl>https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE</PackageLicenseUrl>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <IncludeSymbols>true</IncludeSymbols>

+ 24 - 0
Masuit.Tools.Abstractions/Models/Tree.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace Masuit.Tools.Models
+{
+    public class Tree<T>
+    {
+        public Tree(T value)
+        {
+            Value = value;
+        }
+
+        public T Value { get; set; }
+
+        /// <summary>
+        /// 父节点
+        /// </summary>
+        public virtual T Parent { get; set; }
+
+        /// <summary>
+        /// 子级
+        /// </summary>
+        public virtual ICollection<Tree<T>> Children { get; set; }
+    }
+}

+ 146 - 0
Masuit.Tools.Abstractions/Models/TreeExtensions.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Linq.Expressions;
 
 namespace Masuit.Tools.Models
 {
@@ -102,6 +103,151 @@ namespace Masuit.Tools.Models
             return result;
         }
 
+        /// <summary>
+        /// 平行集合转换成树形结构
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="idSelector"></param>
+        /// <param name="pidSelector"></param>
+        /// <param name="topValue">根对象parentId的值</param>
+        /// <returns></returns>
+        public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, string>> idSelector, Expression<Func<T, string>> pidSelector, string topValue = default) where T : ITreeParent<T>, ITreeChildren<T>
+        {
+            return ToTree<T, string>(source, idSelector, pidSelector, topValue);
+        }
+
+        /// <summary>
+        /// 平行集合转换成树形结构
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="idSelector"></param>
+        /// <param name="pidSelector"></param>
+        /// <param name="topValue">根对象parentId的值</param>
+        /// <returns></returns>
+        public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, int>> idSelector, Expression<Func<T, int>> pidSelector, int topValue = 0) where T : ITreeParent<T>, ITreeChildren<T>
+        {
+            return ToTree<T, int>(source, idSelector, pidSelector, topValue);
+        }
+
+        /// <summary>
+        /// 平行集合转换成树形结构
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="idSelector"></param>
+        /// <param name="pidSelector"></param>
+        /// <param name="topValue">根对象parentId的值</param>
+        /// <returns></returns>
+        public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, long>> idSelector, Expression<Func<T, long>> pidSelector, long topValue = 0) where T : ITreeParent<T>, ITreeChildren<T>
+        {
+            return ToTree<T, long>(source, idSelector, pidSelector, topValue);
+        }
+
+        /// <summary>
+        /// 平行集合转换成树形结构
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="idSelector"></param>
+        /// <param name="pidSelector"></param>
+        /// <param name="topValue">根对象parentId的值</param>
+        /// <returns></returns>
+        public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, Guid>> idSelector, Expression<Func<T, Guid>> pidSelector, Guid topValue = default) where T : ITreeParent<T>, ITreeChildren<T>
+        {
+            return ToTree<T, Guid>(source, idSelector, pidSelector, topValue);
+        }
+
+        /// <summary>
+        /// 平行集合转换成树形结构
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="TKey"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="idSelector"></param>
+        /// <param name="pidSelector"></param>
+        /// <param name="topValue">根对象parentId的值</param>
+        /// <returns></returns>
+        public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, Expression<Func<T, TKey>> idSelector, Expression<Func<T, TKey>> pidSelector, TKey topValue = default) where T : ITreeParent<T>, ITreeChildren<T> where TKey : IComparable
+        {
+            if (idSelector.Body.ToString() == pidSelector.Body.ToString())
+            {
+                throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
+            }
+
+            var pidFunc = pidSelector.Compile();
+            var idFunc = idSelector.Compile();
+            source = source.Where(t => t != null);
+            var temp = new List<T>();
+            foreach (var item in source.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue)))
+            {
+                item.Parent = default;
+                TransData(source, item, idFunc, pidFunc);
+                temp.Add(item);
+            }
+
+            return temp;
+        }
+
+        private static void TransData<T, TKey>(IEnumerable<T> source, T parent, Func<T, TKey> idSelector, Func<T, TKey> pidSelector) where T : ITreeParent<T>, ITreeChildren<T> where TKey : IComparable
+        {
+            var temp = new List<T>();
+            foreach (var item in source.Where(item => pidSelector(item)?.Equals(idSelector(parent)) == true))
+            {
+                TransData(source, item, idSelector, pidSelector);
+                item.Parent = parent;
+                temp.Add(item);
+            }
+
+            parent.Children = temp;
+        }
+
+        /// <summary>
+        /// 平行集合转换成树形结构
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="TKey"></typeparam>
+        /// <param name="source"></param>
+        /// <param name="idSelector"></param>
+        /// <param name="pidSelector"></param>
+        /// <param name="topValue">根对象parentId的值</param>
+        /// <returns></returns>
+        public static List<Tree<T>> ToTreeGeneral<T, TKey>(this IEnumerable<T> source, Expression<Func<T, TKey>> idSelector, Expression<Func<T, TKey>> pidSelector, TKey topValue = default) where TKey : IComparable
+        {
+            if (idSelector.Body.ToString() == pidSelector.Body.ToString())
+            {
+                throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
+            }
+
+            var pidFunc = pidSelector.Compile();
+            var idFunc = idSelector.Compile();
+            source = source.Where(t => t != null);
+            var temp = new List<Tree<T>>();
+            foreach (var item in source.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue)))
+            {
+                var parent = new Tree<T>(item);
+                TransData(source, parent, idFunc, pidFunc);
+                temp.Add(parent);
+            }
+
+            return temp;
+        }
+
+        private static void TransData<T, TKey>(IEnumerable<T> source, Tree<T> parent, Func<T, TKey> idSelector, Func<T, TKey> pidSelector) where TKey : IComparable
+        {
+            var temp = new List<Tree<T>>();
+            foreach (var item in source.Where(item => pidSelector(item)?.Equals(idSelector(parent.Value)) == true))
+            {
+                var p = new Tree<T>(item);
+                TransData(source, p, idSelector, pidSelector);
+                p.Parent = parent.Value;
+                temp.Add(p);
+            }
+
+            parent.Children = temp;
+        }
+
         /// <summary>
         /// 所有子级
         /// </summary>

+ 4 - 3
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -20,10 +20,10 @@
         <UserSecretsId>830c282f-f7c1-42be-8651-4cd06ac8e73f</UserSecretsId>
         <RepositoryType>Github</RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-        <Version>2.4.3.1</Version>
-        <FileVersion>2.4.3.1</FileVersion>
+        <Version>2.4.3.2</Version>
+        <FileVersion>2.4.3.2</FileVersion>
         <Company>ldqk.org</Company>
-        <AssemblyVersion>2.4.3.1</AssemblyVersion>
+        <AssemblyVersion>2.4.3.2</AssemblyVersion>
         <Authors>懒得勤快X</Authors>
         <RepositoryUrl>https://github.com/ldqk/Masuit.Tools</RepositoryUrl>
         <EmbedUntrackedSources>true</EmbedUntrackedSources>
@@ -255,6 +255,7 @@
         <Compile Include="..\Masuit.Tools.Abstractions\Models\TaobaoIP.cs">
             <Link>Models\TaobaoIP.cs</Link>
         </Compile>
+        <Compile Include="..\Masuit.Tools.Abstractions\Models\Tree.cs" Link="Models\Tree.cs" />
         <Compile Include="..\Masuit.Tools.Abstractions\Models\TreeExtensions.cs" Link="Models\TreeExtensions.cs" />
         <Compile Include="..\Masuit.Tools.Abstractions\Net\FtpClient.cs">
             <Link>Net\FtpClient.cs</Link>

+ 2 - 3
Masuit.Tools.Core/Models/IQueryableExt.cs

@@ -1,10 +1,9 @@
-using Masuit.Tools.Models;
-using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
 using System;
 using System.Linq;
 using System.Threading.Tasks;
 
-namespace Masuit.Tools.Core.Models
+namespace Masuit.Tools.Models
 {
     public static partial class IQueryableExt
     {

+ 3 - 0
Masuit.Tools/Masuit.Tools.csproj

@@ -236,6 +236,9 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Models\TaobaoIP.cs">
       <Link>Models\TaobaoIP.cs</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Models\Tree.cs">
+      <Link>Models\Tree.cs</Link>
+    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Models\TreeExtensions.cs">
       <Link>Models\TreeExtensions.cs</Link>
     </Compile>

BIN
Masuit.Tools/Properties/AssemblyInfo.cs


+ 1 - 1
Masuit.Tools/package.nuspec

@@ -4,7 +4,7 @@
     <!--*-->
     <id>Masuit.Tools.Net</id>
     <!--*-->
-    <version>2.4.3.1</version>
+    <version>2.4.3.2</version>
     <title>Masuit.Tools</title>
     <!--*-->
     <authors>masuit.com</authors>

+ 27 - 8
NetCoreTest/Program.cs

@@ -1,11 +1,14 @@
-using Masuit.Tools.Models;
+using Masuit.Tools;
+using Masuit.Tools.Models;
 using Masuit.Tools.Reflection;
 using Masuit.Tools.Security;
 using Microsoft.AspNetCore;
 using Microsoft.AspNetCore.Hosting;
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Linq;
 
 namespace NetCoreTest
 {
@@ -16,17 +19,26 @@ namespace NetCoreTest
             var myClass = new MyClass()
             {
                 MyProperty1 = 1,
-                Name = "1",
+                Id = "mcc",
+                Pid = "mc",
                 Parent = new MyClass()
                 {
-                    Name = "mc",
+                    Id = "mc",
+                    Pid = "ccc",
                     Parent = new MyClass()
                     {
-                        Name = "ccc"
+                        Id = "ccc"
                     }
                 }
             };
-            var path = myClass.Path(c => c.Name);
+            var allParent = myClass.AllParent().Append(myClass);
+            var tree = allParent.ToTreeGeneral(c => c.Id, c => c.Pid);
+            Console.WriteLine(tree.ToJsonString(new JsonSerializerSettings()
+            {
+                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
+                Formatting = Formatting.Indented
+            }));
+            var path = myClass.Path(c => c.Id);
             Console.WriteLine(path);
 
             myClass.SetProperty(nameof(MyClass.MyProperty1), 1);
@@ -49,25 +61,32 @@ namespace NetCoreTest
 
     }
 
-    public class MyClass : ITreeParent<MyClass>
+    public class MyClass : ITree<MyClass>
     {
         [Description("test")]
-        public string MyProperty { get; set; }
+        public string Pid { get; set; }
         public int? MyProperty1 { get; set; }
 
         /// <summary>
         /// 名字
         /// </summary>
-        public string Name { get; set; }
+        public string Id { get; set; }
 
         /// <summary>
         /// 父节点
         /// </summary>
+        [JsonIgnore]
         public virtual MyClass Parent { get; set; }
 
         /// <summary>
         /// 子级
         /// </summary>
+        [JsonIgnore]
         public ICollection<MyClass> Children { get; set; } = new List<MyClass>();
+
+        /// <summary>
+        /// 名字
+        /// </summary>
+        public string Name { get; set; }
     }
 }