Skip to content

Commit 0bd0bec

Browse files
authored
Merge pull request #316 from Senparc/Develop
[2024-11-27] v2.0.1-beta3 add lock to AddAndInitDynamicApi() method
2 parents 6c7f588 + 65a3032 commit 0bd0bec

File tree

2 files changed

+182
-156
lines changed

2 files changed

+182
-156
lines changed
Lines changed: 79 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,82 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>
4-
<Version>2.0.0-beta3</Version>
5-
<LangVersion>latest</LangVersion>
6-
<AssemblyName>Senparc.CO2NET.WebApi</AssemblyName>
7-
<RootNamespace>Senparc.CO2NET.WebApi</RootNamespace>
8-
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">true</GeneratePackageOnBuild>
9-
<Description>
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>
4+
<Version>2.0.1-beta3</Version>
5+
<LangVersion>latest</LangVersion>
6+
<AssemblyName>Senparc.CO2NET.WebApi</AssemblyName>
7+
<RootNamespace>Senparc.CO2NET.WebApi</RootNamespace>
8+
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">true</GeneratePackageOnBuild>
9+
<Description>
1010
Senparc.CO2NET.WebApi Application Performance Management
1111
</Description>
12-
<Copyright>Senparc Copyright © 2004~2024</Copyright>
13-
<PackageTags>WebApi,.NET Core,.NET Framework,Public,Base Library,CO2NET,盛派</PackageTags>
14-
<Authors>Senparc</Authors>
15-
<Owners>Senparc</Owners>
16-
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
17-
<ProjectUrl>https://github.com/Senparc/Senparc.CO2NET</ProjectUrl>
18-
<Title>Senparc.CO2NET.WebApi.dll</Title>
19-
<Summary>Senparc.CO2NET.WebApi</Summary>
20-
<PackageProjectUrl>https://github.com/Senparc/Senparc.CO2NET</PackageProjectUrl>
21-
<PackageIcon>icon.jpg</PackageIcon>
22-
<RepositoryUrl>https://github.com/Senparc/Senparc.CO2NET</RepositoryUrl>
23-
<Configurations>Debug;Release;Test</Configurations>
24-
<PackageReleaseNotes>
25-
v0.1 Genesis
26-
v0.2 Completed WebApiEngine first generation core version
27-
v0.2.3 Completed WebApiEngine second generation core version, fully supports dynamic API integration and corresponding XML generation
28-
v0.2.4 Added additional injectable classes or methods
29-
v0.2.5 Optimized asynchronous thread execution
30-
v0.2.5.7 Added ForbiddenExternalAccess parameter to set whether external access is allowed
31-
v0.2.6 Added WebApiEngineOptions
32-
v0.2.8 Provided .NET Standard 2.1 version
33-
v1.1 Provided the ability to synchronize parameter attributes to dynamic APIs
34-
v1.1.2 Optimized document extraction regular expressions
35-
v1.1.3 Added AddApiControllerAttribute option, default is true
36-
v1.3 Provided .NET 7.0 support
37-
v1.4.1 Used [ApiBind(Ignore = false)], added complete API generation ignore for the entire class
38-
v1.5.2.1 Organized log format
39-
v1.6.0
40-
1. Removed .NET 7.0 TargetFramework; added .NET 8.0 TargetFramework
41-
2. Removed reference to Microsoft.AspNetCore.Mvc.Core
42-
2. Referenced the latest Senparc.CO2NET.AspNet, .NET 6.0 and .NET 8.0 assemblies no longer depend on Microsoft.AspNetCore.Hosting.Abstractions and Microsoft.AspNetCore.Http.Abstractions
43-
[2024-09-11] v1.6.3 Updated Cache, removed InsertToCache(), added Count(prefix)
44-
[2024-10-07] v1.7.0 Stopped support for .NET 6.0
45-
[2024-10-07] v2.0.0-beta2
46-
1. Add UseLowerCaseApiName to WebApiEngineOptions
47-
2. Add unique WebApi name to duplicate method name
48-
</PackageReleaseNotes>
49-
</PropertyGroup>
50-
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
51-
<OutputPath>..\BuildOutPut</OutputPath>
52-
<DefineConstants>TRACE;RELEASE</DefineConstants>
53-
</PropertyGroup>
54-
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
55-
<OutputPath>..\BuildOutPut</OutputPath>
56-
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
57-
<DocumentationFile>..\BuildOutPut\Senparc.CO2NET.WebApi.xml</DocumentationFile>
58-
<Optimize>true</Optimize>
59-
<DebugType>pdbonly</DebugType>
60-
<ErrorReport>prompt</ErrorReport>
61-
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
62-
</PropertyGroup>
63-
<ItemGroup>
64-
<None Include="..\Senparc.CO2NET\icon.jpg" Pack="true" Visible="false" PackagePath="" />
65-
</ItemGroup>
66-
<ItemGroup>
67-
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
68-
<!--<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />-->
69-
</ItemGroup>
70-
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.1'">
71-
<PackageReference Include="Microsoft.AspNetCore.App" />
72-
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
73-
</ItemGroup>
74-
<ItemGroup>
75-
<ProjectReference Include="..\Senparc.CO2NET.AspNet\Senparc.CO2NET.AspNet.csproj" />
76-
<ProjectReference Include="..\Senparc.CO2NET\Senparc.CO2NET.csproj" />
77-
</ItemGroup>
78-
<ItemGroup>
79-
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.1.1" />
80-
</ItemGroup>
12+
<Copyright>Senparc Copyright © 2004~2024</Copyright>
13+
<PackageTags>WebApi,.NET Core,.NET Framework,Public,Base Library,CO2NET,盛派</PackageTags>
14+
<Authors>Senparc</Authors>
15+
<Owners>Senparc</Owners>
16+
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
17+
<ProjectUrl>https://github.com/Senparc/Senparc.CO2NET</ProjectUrl>
18+
<Title>Senparc.CO2NET.WebApi.dll</Title>
19+
<Summary>Senparc.CO2NET.WebApi</Summary>
20+
<PackageProjectUrl>https://github.com/Senparc/Senparc.CO2NET</PackageProjectUrl>
21+
<PackageIcon>icon.jpg</PackageIcon>
22+
<RepositoryUrl>https://github.com/Senparc/Senparc.CO2NET</RepositoryUrl>
23+
<Configurations>Debug;Release;Test</Configurations>
24+
<PackageReleaseNotes>
25+
v0.1 Genesis
26+
v0.2 Completed WebApiEngine first generation core version
27+
v0.2.3 Completed WebApiEngine second generation core version, fully supports dynamic API integration and corresponding XML generation
28+
v0.2.4 Added additional injectable classes or methods
29+
v0.2.5 Optimized asynchronous thread execution
30+
v0.2.5.7 Added ForbiddenExternalAccess parameter to set whether external access is allowed
31+
v0.2.6 Added WebApiEngineOptions
32+
v0.2.8 Provided .NET Standard 2.1 version
33+
v1.1 Provided the ability to synchronize parameter attributes to dynamic APIs
34+
v1.1.2 Optimized document extraction regular expressions
35+
v1.1.3 Added AddApiControllerAttribute option, default is true
36+
v1.3 Provided .NET 7.0 support
37+
v1.4.1 Used [ApiBind(Ignore = false)], added complete API generation ignore for the entire class
38+
v1.5.2.1 Organized log format
39+
v1.6.0
40+
1. Removed .NET 7.0 TargetFramework; added .NET 8.0 TargetFramework
41+
2. Removed reference to Microsoft.AspNetCore.Mvc.Core
42+
2. Referenced the latest Senparc.CO2NET.AspNet, .NET 6.0 and .NET 8.0 assemblies no longer depend on Microsoft.AspNetCore.Hosting.Abstractions and Microsoft.AspNetCore.Http.Abstractions
43+
[2024-09-11] v1.6.3 Updated Cache, removed InsertToCache(), added Count(prefix)
44+
[2024-10-07] v1.7.0 Stopped support for .NET 6.0
45+
[2024-10-07] v2.0.0-beta2
46+
1. Add UseLowerCaseApiName to WebApiEngineOptions
47+
2. Add unique WebApi name to duplicate method name
48+
[2024-11-27] v2.0.1-beta3 add lock to AddAndInitDynamicApi() method
49+
</PackageReleaseNotes>
50+
</PropertyGroup>
51+
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
52+
<OutputPath>..\BuildOutPut</OutputPath>
53+
<DefineConstants>TRACE;RELEASE</DefineConstants>
54+
</PropertyGroup>
55+
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
56+
<OutputPath>..\BuildOutPut</OutputPath>
57+
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
58+
<DocumentationFile>..\BuildOutPut\Senparc.CO2NET.WebApi.xml</DocumentationFile>
59+
<Optimize>true</Optimize>
60+
<DebugType>pdbonly</DebugType>
61+
<ErrorReport>prompt</ErrorReport>
62+
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
63+
</PropertyGroup>
64+
<ItemGroup>
65+
<None Include="..\Senparc.CO2NET\icon.jpg" Pack="true" Visible="false" PackagePath="" />
66+
</ItemGroup>
67+
<ItemGroup>
68+
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
69+
<!--<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />-->
70+
</ItemGroup>
71+
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.1'">
72+
<PackageReference Include="Microsoft.AspNetCore.App" />
73+
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
74+
</ItemGroup>
75+
<ItemGroup>
76+
<ProjectReference Include="..\Senparc.CO2NET.AspNet\Senparc.CO2NET.AspNet.csproj" />
77+
<ProjectReference Include="..\Senparc.CO2NET\Senparc.CO2NET.csproj" />
78+
</ItemGroup>
79+
<ItemGroup>
80+
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.1.1" />
81+
</ItemGroup>
8182
</Project>

src/Senparc.CO2NET.WebApi/WebApiEngines/WebApiEngineExtensions.cs

Lines changed: 103 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ namespace Senparc.CO2NET.WebApi.WebApiEngines
1717
/// </summary>
1818
public static class WebApiEngineExtensions
1919
{
20+
public static object WebApiInitObject = new object();
21+
public static bool WebApiInitFinished = false;
22+
23+
2024
/// <summary>
2125
/// 初始化动态API
2226
/// </summary>
@@ -49,107 +53,128 @@ public static void AddAndInitDynamicApi(this IServiceCollection services, IMvcBu
4953
/// <param name="builder"></param>
5054
/// <param name="services"></param>
5155
/// <param name="options"> WebApiEngine 配置</param>
52-
private static void AddAndInitDynamicApi(this IServiceCollection services,
53-
(IMvcCoreBuilder coreBuilder, IMvcBuilder builder) builder,
56+
private static void AddAndInitDynamicApi(this IServiceCollection services,
57+
(IMvcCoreBuilder coreBuilder, IMvcBuilder builder) builder,
5458
Action<WebApiEngineOptions> options = null)
5559
{
56-
services.AddScoped<FindApiService>();
57-
services.AddScoped(s => new WebApiEngine(options));
58-
59-
var webApiEngine = new WebApiEngine(options);
60-
61-
bool preLoad = true;
62-
63-
//确保 ApiBind 已经执行扫描和注册过程
64-
services.AddApiBind(preLoad);//参数为 true,确保重试绑定成功
65-
66-
//确保目录存在
67-
if (webApiEngine.BuildXml)
60+
lock (WebApiInitObject)
6861
{
69-
webApiEngine.TryCreateDir(webApiEngine.DocXmlPath);
70-
}
62+
if (WebApiInitFinished)
63+
{
64+
return;
65+
}
66+
67+
try
68+
{
7169

72-
var dt1 = SystemTime.Now;
70+
services.AddScoped<FindApiService>();
71+
services.AddScoped(s => new WebApiEngine(options));
7372

74-
var apiGroups = ApiBindInfoCollection.Instance.GetGroupedCollection();
75-
var apiGouupsCount = apiGroups.Count();
73+
var webApiEngine = new WebApiEngine(options);
7674

77-
ConcurrentDictionary<string, (int apiCount, double costMs)> assemblyBuildStat = new ConcurrentDictionary<string, (int, double)>();
75+
bool preLoad = true;
7876

79-
List<Task> taskList = new List<Task>();
77+
//确保 ApiBind 已经执行扫描和注册过程
78+
services.AddApiBind(preLoad);//参数为 true,确保重试绑定成功
8079

81-
//因为模块数量比较少,这里使用异步反而会开销略大
82-
//WeixinApiAssemblyNames.Keys.AsParallel().ForAll(async category =>
83-
//WeixinApiAssemblyNames.Keys.ToList().ForEach(category =>
84-
var keys = WebApiEngine.ApiAssemblyNames.Keys.ToList();
85-
for (int i = 0; i < keys.Count; i++)
86-
{
87-
var category = keys[i];
88-
var threadIndex = i;
89-
var wrapperTask = Task.Factory.StartNew(async () =>
90-
{
91-
try
80+
//确保目录存在
81+
if (webApiEngine.BuildXml)
9282
{
83+
webApiEngine.TryCreateDir(webApiEngine.DocXmlPath);
84+
}
9385

94-
//此处使用 Task 效率并不比 Keys.ToList() 方法快
95-
webApiEngine.WriteLog($"Get API Groups: {threadIndex + 1}/{apiGouupsCount}, now dealing with: {category}");
96-
var dtStart = SystemTime.Now;
97-
var apiBindGroup = apiGroups.FirstOrDefault(z => z.Key == category);
86+
var dt1 = SystemTime.Now;
9887

99-
var apiCount = await webApiEngine.BuildWebApi(apiBindGroup).ConfigureAwait(false);
100-
var apiAssembly = webApiEngine.GetApiAssembly(category);
88+
var apiGroups = ApiBindInfoCollection.Instance.GetGroupedCollection();
89+
var apiGouupsCount = apiGroups.Count();
10190

102-
//程序部件:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/advanced/app-parts?view=aspnetcore-2.2
103-
if (builder.coreBuilder != null)
104-
{
105-
builder.coreBuilder.AddApplicationPart(apiAssembly);
106-
}
107-
else
108-
{
109-
builder.builder.AddApplicationPart(apiAssembly);
110-
}
91+
ConcurrentDictionary<string, (int apiCount, double costMs)> assemblyBuildStat = new ConcurrentDictionary<string, (int, double)>();
11192

112-
assemblyBuildStat[category] = (apiCount: apiCount, costMs: SystemTime.DiffTotalMS(dtStart));
113-
}
114-
catch (Exception ex)
93+
List<Task> taskList = new List<Task>();
94+
95+
//因为模块数量比较少,这里使用异步反而会开销略大
96+
//WeixinApiAssemblyNames.Keys.AsParallel().ForAll(async category =>
97+
//WeixinApiAssemblyNames.Keys.ToList().ForEach(category =>
98+
var keys = WebApiEngine.ApiAssemblyNames.Keys.ToList();
99+
for (int i = 0; i < keys.Count; i++)
115100
{
116-
SenparcTrace.BaseExceptionLog(ex);
101+
var category = keys[i];
102+
var threadIndex = i;
103+
var wrapperTask = Task.Factory.StartNew(async () =>
104+
{
105+
try
106+
{
107+
108+
//此处使用 Task 效率并不比 Keys.ToList() 方法快
109+
webApiEngine.WriteLog($"Get API Groups: {threadIndex + 1}/{apiGouupsCount}, now dealing with: {category}");
110+
var dtStart = SystemTime.Now;
111+
var apiBindGroup = apiGroups.FirstOrDefault(z => z.Key == category);
112+
113+
var apiCount = await webApiEngine.BuildWebApi(apiBindGroup).ConfigureAwait(false);
114+
var apiAssembly = webApiEngine.GetApiAssembly(category);
115+
116+
//程序部件:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/advanced/app-parts?view=aspnetcore-2.2
117+
if (builder.coreBuilder != null)
118+
{
119+
builder.coreBuilder.AddApplicationPart(apiAssembly);
120+
}
121+
else
122+
{
123+
builder.builder.AddApplicationPart(apiAssembly);
124+
}
125+
126+
assemblyBuildStat[category] = (apiCount: apiCount, costMs: SystemTime.DiffTotalMS(dtStart));
127+
}
128+
catch (Exception ex)
129+
{
130+
SenparcTrace.BaseExceptionLog(ex);
131+
}
132+
});
133+
taskList.Add(wrapperTask.Unwrap());
117134
}
118-
});
119-
taskList.Add(wrapperTask.Unwrap());
120-
}
121-
//foreach (var category in WebApiEngine.ApiAssemblyNames.Keys)
122-
//{
135+
//foreach (var category in WebApiEngine.ApiAssemblyNames.Keys)
136+
//{
123137

124-
//}
138+
//}
125139

126-
Task.WaitAll(taskList.ToArray());
140+
Task.WaitAll(taskList.ToArray());
127141

128-
//保存 XML文件
129-
webApiEngine.SaveDynamicApiXml();
142+
//保存 XML文件
143+
webApiEngine.SaveDynamicApiXml();
130144

131-
#region 统计数据
132-
var totalCost = SystemTime.DiffTotalMS(dt1);
145+
#region 统计数据
146+
var totalCost = SystemTime.DiffTotalMS(dt1);
133147

134-
//Func<object, int, string> outputResult = (text, length) => string.Format($"{{0,{length}}}", text);
148+
//Func<object, int, string> outputResult = (text, length) => string.Format($"{{0,{length}}}", text);
135149

136-
webApiEngine.WriteLog("");
137-
webApiEngine.WriteLog(string.Format("{0,35} | {1,15}| {2,15} |{3,15}", "Category Name", "API Count", "Cost Time", "Average"));
138-
webApiEngine.WriteLog(new string('-', 90));
139-
foreach (var item in assemblyBuildStat)
140-
{
141-
var apiCount = item.Value.apiCount;
142-
var cost = item.Value.costMs;
143-
var avg = Math.Round(cost / apiCount, 3);
144-
webApiEngine.WriteLog(string.Format("{0,35} | {1,15}| {2,15} |{3,15}", item.Key, apiCount, $"{cost}ms", $"{avg}ms"));
150+
webApiEngine.WriteLog("");
151+
webApiEngine.WriteLog(string.Format("{0,35} | {1,15}| {2,15} |{3,15}", "Category Name", "API Count", "Cost Time", "Average"));
152+
webApiEngine.WriteLog(new string('-', 90));
153+
foreach (var item in assemblyBuildStat)
154+
{
155+
var apiCount = item.Value.apiCount;
156+
var cost = item.Value.costMs;
157+
var avg = Math.Round(cost / apiCount, 3);
158+
webApiEngine.WriteLog(string.Format("{0,35} | {1,15}| {2,15} |{3,15}", item.Key, apiCount, $"{cost}ms", $"{avg}ms"));
159+
}
160+
webApiEngine.WriteLog(new string('=', 90));
161+
var totalApi = assemblyBuildStat.Values.Sum(z => z.apiCount);
162+
webApiEngine.WriteLog(string.Format("{0,35} | {1,15}| {2,15} |{3,15}", $"Total", $"API Count:{totalApi}", $"Cost:{totalCost}ms", $""));
163+
webApiEngine.WriteLog($"Total Average Cost: {Math.Round(totalCost / totalApi, 4)} ms \t\tTask Count: {webApiEngine.TaskCount}");
164+
webApiEngine.WriteLog("");
165+
166+
#endregion
167+
}
168+
catch (Exception ex)
169+
{
170+
SenparcTrace.BaseExceptionLog(ex);
171+
}
172+
finally
173+
{
174+
WebApiInitFinished = true;
175+
}
145176
}
146-
webApiEngine.WriteLog(new string('=', 90));
147-
var totalApi = assemblyBuildStat.Values.Sum(z => z.apiCount);
148-
webApiEngine.WriteLog(string.Format("{0,35} | {1,15}| {2,15} |{3,15}", $"Total", $"API Count:{totalApi}", $"Cost:{totalCost}ms", $""));
149-
webApiEngine.WriteLog($"Total Average Cost: {Math.Round(totalCost / totalApi, 4)} ms \t\tTask Count: {webApiEngine.TaskCount}");
150-
webApiEngine.WriteLog("");
151177

152-
#endregion
153178
}
154179

155180

0 commit comments

Comments
 (0)