diff --git a/Blog.Core.AdminMvc/Blog.Core.AdminMvc.csproj b/Blog.Core.AdminMvc/Blog.Core.AdminMvc.csproj deleted file mode 100644 index 57f47e0..0000000 --- a/Blog.Core.AdminMvc/Blog.Core.AdminMvc.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net5.0 - - - - - - - diff --git a/Blog.Core.AdminMvc/Startup.cs b/Blog.Core.AdminMvc/Startup.cs deleted file mode 100644 index a9bf97b..0000000 --- a/Blog.Core.AdminMvc/Startup.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Ocelot.DependencyInjection; -using Ocelot.Middleware; - -namespace Blog.Core.AdminMvc -{ - public class Startup - { - /** - *┌──────────────────────────────────────────────────────────────┐ - *│ 描 述:当前项目为空,只是模拟一个MVC客户端 - *│ 作 者:anson zhang - *└──────────────────────────────────────────────────────────────┘ - */ - - - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - services.AddOcelot(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseOcelot().Wait(); - } - } -} diff --git a/Blog.Core.AdminMvc/appsettings.json b/Blog.Core.AdminMvc/appsettings.json deleted file mode 100644 index 9e3249b..0000000 --- a/Blog.Core.AdminMvc/appsettings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Logging": { - "IncludeScopes": false, - "Debug": { - "LogLevel": { - "Default": "Warning" - } - }, - "Console": { - "LogLevel": { - "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Debug" - } - } - }, - "AllowedHosts": "*" -} diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index b35ae50..63f6c84 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -169,7 +169,7 @@ "Enabled": true }, "Consul": { - "Enabled": false + "Enabled": true }, "IpRateLimit": { "Enabled": true diff --git a/Blog.Core.Extensions/Authorizations/Policys/ApiResponseHandler.cs b/Blog.Core.Extensions/Authorizations/Policys/ApiResponseHandler.cs index 3ee0387..6aedc54 100644 --- a/Blog.Core.Extensions/Authorizations/Policys/ApiResponseHandler.cs +++ b/Blog.Core.Extensions/Authorizations/Policys/ApiResponseHandler.cs @@ -1,4 +1,4 @@ -using Blog.Core.AuthHelper.Policys; +using Blog.Core.Model; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; diff --git a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMidd.cs b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMidd.cs index 0f8c385..056bc6c 100644 --- a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMidd.cs +++ b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMidd.cs @@ -1,4 +1,4 @@ -using Blog.Core.AuthHelper.Policys; +using Blog.Core.Model; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using System; diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index 1223a06..df3da09 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -139,6 +139,16 @@ namespace Blog.Core.Extensions ConsoleHelper.WriteSuccessLine($"RabbitMQ: True"); } + // Consul 注册服务 + if (!Appsettings.app("Middleware", "Consul", "Enabled").ObjToBool()) + { + Console.WriteLine($"Consul service: False"); + } + else + { + ConsoleHelper.WriteSuccessLine($"Consul service: True"); + } + // EventBus 事件总线 if (!Appsettings.app("EventBus", "Enabled").ObjToBool()) { diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.csproj b/Blog.Core.Gateway/Blog.Core.Gateway.csproj new file mode 100644 index 0000000..09f6dee --- /dev/null +++ b/Blog.Core.Gateway/Blog.Core.Gateway.csproj @@ -0,0 +1,26 @@ + + + + net5.0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/Blog.Core.Gateway/Controllers/UserController.cs b/Blog.Core.Gateway/Controllers/UserController.cs new file mode 100644 index 0000000..b03889a --- /dev/null +++ b/Blog.Core.Gateway/Controllers/UserController.cs @@ -0,0 +1,32 @@ +using Blog.Core.Common.HttpContextUser; +using Blog.Core.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; + +namespace Blog.Core.Gateway.Controllers +{ + [Authorize] + [Route("/gateway/[controller]/[action]")] + public class UserController : ControllerBase + { + private readonly IUser _user; + + public UserController(IUser user) + { + _user = user; + } + + [HttpGet] + public MessageModel> MyClaims() + { + return new MessageModel>() + { + success = true, + response = _user.GetClaimsIdentity().ToList() + }; + } + } +} diff --git a/Blog.Core.Gateway/Extensions/ApiResponseHandler.cs b/Blog.Core.Gateway/Extensions/ApiResponseHandler.cs new file mode 100644 index 0000000..c4af645 --- /dev/null +++ b/Blog.Core.Gateway/Extensions/ApiResponseHandler.cs @@ -0,0 +1,77 @@ +using Blog.Core.Model; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Blog.Core.Gateway.Extensions +{ + /// + /// 这里不需要,目前集成的是 Blog.Core.Extensions 下的接口处理器 + /// 但是你可以单独在网关中使用这个。 + /// + public class ApiResponseHandler : DelegatingHandler + { + JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings() + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await base.SendAsync(request, cancellationToken); + var contentType = response.Content.Headers.ContentType?.MediaType ?? ""; + if (!contentType.Equals("application/json")) return response; + + dynamic result = null; + var resultStr = await response.Content.ReadAsStringAsync(); + try + { + result = JsonConvert.DeserializeObject(resultStr); + } + catch (Exception) + { + return response; + } + + if (result != null && result.code == 500) resultStr = result.msg.ToString(); + + var apiResponse = new ApiResponse(StatusCode.CODE200).MessageModel; + if (response.StatusCode != HttpStatusCode.OK || result.code == (int)HttpStatusCode.InternalServerError) + { + var exception = new Exception(resultStr); + apiResponse = new ApiResponse(StatusCode.CODE500).MessageModel; + } + else if (result.code == (int)HttpStatusCode.Unauthorized) + { + apiResponse = new ApiResponse(StatusCode.CODE401).MessageModel; + + } + else if (result.code == (int)HttpStatusCode.Forbidden) + { + apiResponse = new ApiResponse(StatusCode.CODE403).MessageModel; + + } + else + { + + } + + var statusCode = apiResponse.status == 500 ? HttpStatusCode.InternalServerError + : apiResponse.status == 401 ? HttpStatusCode.Unauthorized + : apiResponse.status == 403 ? HttpStatusCode.Forbidden + : HttpStatusCode.OK; + + response.StatusCode = statusCode; + response.Content = new StringContent(JsonConvert.SerializeObject(apiResponse, jsonSerializerSettings), Encoding.UTF8, "application/json"); + + return response; + } + } + + +} diff --git a/Blog.Core.Gateway/Extensions/OcelotMildd.cs b/Blog.Core.Gateway/Extensions/OcelotMildd.cs new file mode 100644 index 0000000..d8e3393 --- /dev/null +++ b/Blog.Core.Gateway/Extensions/OcelotMildd.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Builder; +using Ocelot.Middleware; +using System.Threading.Tasks; + +namespace Blog.Core.Gateway.Extensions +{ + public static class OcelotMildd + { + public static async Task UseOcelotMildd(this IApplicationBuilder app) + { + await app.UseOcelot(); + return app; + } + + } +} diff --git a/Blog.Core.AdminMvc/OcelotGatewaySet.json b/Blog.Core.Gateway/OcelotGatewaySet.json similarity index 82% rename from Blog.Core.AdminMvc/OcelotGatewaySet.json rename to Blog.Core.Gateway/OcelotGatewaySet.json index be4292c..362529e 100644 --- a/Blog.Core.AdminMvc/OcelotGatewaySet.json +++ b/Blog.Core.Gateway/OcelotGatewaySet.json @@ -1,14 +1,6 @@ { "Routes": [ { - "DownstreamPathTemplate": "/api/{url}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 8081 - } - ], "UpstreamPathTemplate": "/gateway/api/{url}", "UpstreamHttpMethod": [ "Get", @@ -18,17 +10,17 @@ ], "LoadBalancerOptions": { "Type": "RoundRobin" - } - }, - { - "DownstreamPathTemplate": "/is4api/{url}", + }, + "DownstreamPathTemplate": "/api/{url}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", - "Port": 5004 + "Port": 8081 } - ], + ] + }, + { "UpstreamPathTemplate": "/gateway/is4api/{url}", "UpstreamHttpMethod": [ "Get", @@ -38,10 +30,23 @@ ], "LoadBalancerOptions": { "Type": "RoundRobin" - } + }, + "DownstreamPathTemplate": "/is4api/{url}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 5004 + } + ] } ], "GlobalConfiguration": { - "BaseUrl": "http://localhost:9000" + "BaseUrl": "http://localhost:9000", + "ServiceDiscoveryProvider": { + "Host": "localhost", + "Port": 8500, + "Type": "Consul" + } } } \ No newline at end of file diff --git a/Blog.Core.AdminMvc/Program.cs b/Blog.Core.Gateway/Program.cs similarity index 100% rename from Blog.Core.AdminMvc/Program.cs rename to Blog.Core.Gateway/Program.cs diff --git a/Blog.Core.AdminMvc/Properties/launchSettings.json b/Blog.Core.Gateway/Properties/launchSettings.json similarity index 89% rename from Blog.Core.AdminMvc/Properties/launchSettings.json rename to Blog.Core.Gateway/Properties/launchSettings.json index 218573b..6265ef3 100644 --- a/Blog.Core.AdminMvc/Properties/launchSettings.json +++ b/Blog.Core.Gateway/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "Blog.Core.AdminMvc": { + "Blog.Core.Gateway": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "http://localhost:9000", diff --git a/Blog.Core.Gateway/Startup.cs b/Blog.Core.Gateway/Startup.cs new file mode 100644 index 0000000..5f98e1c --- /dev/null +++ b/Blog.Core.Gateway/Startup.cs @@ -0,0 +1,76 @@ +using Blog.Core.Common; +using Blog.Core.Extensions; +using Blog.Core.Gateway.Extensions; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Ocelot.DependencyInjection; +using Ocelot.Provider.Consul; + +namespace Blog.Core.AdminMvc +{ + public class Startup + { + /** + *┌──────────────────────────────────────────────────────────────┐ + *│ 描 述:模拟一个网关项目 + *│ 测 试:http://localhost:9000/gateway/user/MyClaims + *│ 测 试:http://localhost:9000/gateway/api/blog + *│ 测 试:http://localhost:9000/gateway/is4api/GetAchieveUsers + *│ 作 者:anson zhang + *└──────────────────────────────────────────────────────────────┘ + */ + public Startup(IConfiguration configuration, IWebHostEnvironment env) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(new Appsettings(Configuration)); + + services.AddAuthentication_JWTSetup(); + + services.AddAuthorization(options => + { + options.AddPolicy("GW", policy => policy.RequireRole("GW").Build()); + }); + + services.AddControllers(); + + services.AddHttpContextSetup(); + + services.AddCorsSetup(); + + services.AddOcelot().AddConsul(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseCors(Appsettings.app(new string[] { "Startup", "Cors", "PolicyName" })); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + app.UseOcelotMildd().Wait(); + } + } +} diff --git a/Blog.Core.AdminMvc/appsettings.Development.json b/Blog.Core.Gateway/appsettings.Development.json similarity index 100% rename from Blog.Core.AdminMvc/appsettings.Development.json rename to Blog.Core.Gateway/appsettings.Development.json diff --git a/Blog.Core.Gateway/appsettings.json b/Blog.Core.Gateway/appsettings.json new file mode 100644 index 0000000..59f40d8 --- /dev/null +++ b/Blog.Core.Gateway/appsettings.json @@ -0,0 +1,31 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning", + "Microsoft.Hosting.Lifetime": "Debug" + } + } + }, + "AllowedHosts": "*", + "Startup": { + "Cors": { + "PolicyName": "CorsIpAccess", + "EnableAllIPs": false, + "IPs": "http://127.0.0.1:2364,http://localhost:2364" + } + }, + "Audience": { + "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", + "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", + "Issuer": "Blog.Core", + "Audience": "wr" + } + +} diff --git a/Blog.Core.Extensions/Authorizations/Policys/ApiResponse.cs b/Blog.Core.Model/ApiResponse.cs similarity index 75% rename from Blog.Core.Extensions/Authorizations/Policys/ApiResponse.cs rename to Blog.Core.Model/ApiResponse.cs index 014845a..0927aca 100644 --- a/Blog.Core.Extensions/Authorizations/Policys/ApiResponse.cs +++ b/Blog.Core.Model/ApiResponse.cs @@ -1,11 +1,10 @@ -using Blog.Core.Model; - -namespace Blog.Core.AuthHelper.Policys + +namespace Blog.Core.Model { public class ApiResponse { - public int Status { get; set; } = 404; - public string Value { get; set; } = "No Found"; + public int Status { get; set; } = 200; + public string Value { get; set; } = ""; public MessageModel MessageModel = new MessageModel() { }; public ApiResponse(StatusCode apiCode, string msg = null) @@ -24,6 +23,12 @@ namespace Blog.Core.AuthHelper.Policys Value = "很抱歉,您的访问权限等级不够,联系管理员!"; } break; + case StatusCode.CODE404: + { + Status = 404; + Value = "资源不存在!"; + } + break; case StatusCode.CODE500: { Status = 500; @@ -36,13 +41,14 @@ namespace Blog.Core.AuthHelper.Policys { status = Status, msg = Value, - success = false + success = apiCode != StatusCode.CODE200 }; } } public enum StatusCode { + CODE200, CODE401, CODE403, CODE404, diff --git a/Blog.Core.sln b/Blog.Core.sln index da69603..10cc592 100644 --- a/Blog.Core.sln +++ b/Blog.Core.sln @@ -15,17 +15,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Services", "Blog. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Common", "Blog.Core.Common\Blog.Core.Common.csproj", "{97D32A49-994C-44C5-A167-51E71D173B6F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.FrameWork", "Blog.Core.FrameWork\Blog.Core.FrameWork.csproj", "{44A2006E-3EFC-4179-B400-866178C66556}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Tasks", "Blog.Core.Tasks\Blog.Core.Tasks.csproj", "{F8E9FA1F-4079-4F62-B717-E389BC0014E8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Extensions", "Blog.Core.Extensions\Blog.Core.Extensions.csproj", "{558F1B39-07E4-4FAB-BE7E-5B6104607064}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.ConsoleApp", "Blog.Core.ConsoleApp\Blog.Core.ConsoleApp.csproj", "{BB9FBBDF-B112-44F2-ABB7-9C31CFB619FE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.AdminMvc", "Blog.Core.AdminMvc\Blog.Core.AdminMvc.csproj", "{06D885F3-6352-4BF6-B826-DEA742DFFBD7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D9833F24-7BD0-486F-B028-F1FD098AA1E1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{D9833F24-7BD0-486F-B028-F1FD098AA1E1}" ProjectSection(SolutionItems) = preProject .dockerignore = .dockerignore .editorconfig = .editorconfig @@ -44,7 +38,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EDA8901E EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Tests", "Blog.Core.Tests\Blog.Core.Tests.csproj", "{300A8113-8033-4184-BE28-FC48D8349CD0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blog.Core.EventBus", "Blog.Core.EventBus\Blog.Core.EventBus.csproj", "{E4D54281-8812-4C4D-AAE6-1365F172020B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateways", "Gateways", "{E2BD7D4D-9ED5-41CD-8401-C3FB26F203BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Gateway", "Blog.Core.Gateway\Blog.Core.Gateway.csproj", "{FC3D5C02-DED1-4A39-A8D9-A2EE1BE5A775}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generators", "Generators", "{047A9723-9AAC-42E3-8C69-B3835F15FF96}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.FrameWork", "Blog.Core.FrameWork\Blog.Core.FrameWork.csproj", "{52D318A2-F44E-4CB7-8DD4-483357D4333F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventBus", "EventBus", "{A592C96A-4E44-4F2A-AC21-30683AF6C493}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.EventBus", "Blog.Core.EventBus\Blog.Core.EventBus.csproj", "{17C9E9DC-E926-4C90-9025-3DAC55D7EDA3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.ConsoleApp", "Blog.Core.ConsoleApp\Blog.Core.ConsoleApp.csproj", "{0B3265A9-6716-4D28-8648-C64D5E692ACA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -76,10 +82,6 @@ Global {97D32A49-994C-44C5-A167-51E71D173B6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {97D32A49-994C-44C5-A167-51E71D173B6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {97D32A49-994C-44C5-A167-51E71D173B6F}.Release|Any CPU.Build.0 = Release|Any CPU - {44A2006E-3EFC-4179-B400-866178C66556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {44A2006E-3EFC-4179-B400-866178C66556}.Debug|Any CPU.Build.0 = Debug|Any CPU - {44A2006E-3EFC-4179-B400-866178C66556}.Release|Any CPU.ActiveCfg = Release|Any CPU - {44A2006E-3EFC-4179-B400-866178C66556}.Release|Any CPU.Build.0 = Release|Any CPU {F8E9FA1F-4079-4F62-B717-E389BC0014E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F8E9FA1F-4079-4F62-B717-E389BC0014E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {F8E9FA1F-4079-4F62-B717-E389BC0014E8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -88,28 +90,36 @@ Global {558F1B39-07E4-4FAB-BE7E-5B6104607064}.Debug|Any CPU.Build.0 = Debug|Any CPU {558F1B39-07E4-4FAB-BE7E-5B6104607064}.Release|Any CPU.ActiveCfg = Release|Any CPU {558F1B39-07E4-4FAB-BE7E-5B6104607064}.Release|Any CPU.Build.0 = Release|Any CPU - {BB9FBBDF-B112-44F2-ABB7-9C31CFB619FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB9FBBDF-B112-44F2-ABB7-9C31CFB619FE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB9FBBDF-B112-44F2-ABB7-9C31CFB619FE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB9FBBDF-B112-44F2-ABB7-9C31CFB619FE}.Release|Any CPU.Build.0 = Release|Any CPU - {06D885F3-6352-4BF6-B826-DEA742DFFBD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {06D885F3-6352-4BF6-B826-DEA742DFFBD7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {06D885F3-6352-4BF6-B826-DEA742DFFBD7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {06D885F3-6352-4BF6-B826-DEA742DFFBD7}.Release|Any CPU.Build.0 = Release|Any CPU {300A8113-8033-4184-BE28-FC48D8349CD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {300A8113-8033-4184-BE28-FC48D8349CD0}.Debug|Any CPU.Build.0 = Debug|Any CPU {300A8113-8033-4184-BE28-FC48D8349CD0}.Release|Any CPU.ActiveCfg = Release|Any CPU {300A8113-8033-4184-BE28-FC48D8349CD0}.Release|Any CPU.Build.0 = Release|Any CPU - {E4D54281-8812-4C4D-AAE6-1365F172020B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E4D54281-8812-4C4D-AAE6-1365F172020B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4D54281-8812-4C4D-AAE6-1365F172020B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4D54281-8812-4C4D-AAE6-1365F172020B}.Release|Any CPU.Build.0 = Release|Any CPU + {FC3D5C02-DED1-4A39-A8D9-A2EE1BE5A775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC3D5C02-DED1-4A39-A8D9-A2EE1BE5A775}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC3D5C02-DED1-4A39-A8D9-A2EE1BE5A775}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC3D5C02-DED1-4A39-A8D9-A2EE1BE5A775}.Release|Any CPU.Build.0 = Release|Any CPU + {52D318A2-F44E-4CB7-8DD4-483357D4333F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52D318A2-F44E-4CB7-8DD4-483357D4333F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52D318A2-F44E-4CB7-8DD4-483357D4333F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52D318A2-F44E-4CB7-8DD4-483357D4333F}.Release|Any CPU.Build.0 = Release|Any CPU + {17C9E9DC-E926-4C90-9025-3DAC55D7EDA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17C9E9DC-E926-4C90-9025-3DAC55D7EDA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17C9E9DC-E926-4C90-9025-3DAC55D7EDA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17C9E9DC-E926-4C90-9025-3DAC55D7EDA3}.Release|Any CPU.Build.0 = Release|Any CPU + {0B3265A9-6716-4D28-8648-C64D5E692ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B3265A9-6716-4D28-8648-C64D5E692ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B3265A9-6716-4D28-8648-C64D5E692ACA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B3265A9-6716-4D28-8648-C64D5E692ACA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {300A8113-8033-4184-BE28-FC48D8349CD0} = {EDA8901E-541E-4ADC-B71E-59697D5F9549} + {FC3D5C02-DED1-4A39-A8D9-A2EE1BE5A775} = {E2BD7D4D-9ED5-41CD-8401-C3FB26F203BB} + {52D318A2-F44E-4CB7-8DD4-483357D4333F} = {047A9723-9AAC-42E3-8C69-B3835F15FF96} + {17C9E9DC-E926-4C90-9025-3DAC55D7EDA3} = {A592C96A-4E44-4F2A-AC21-30683AF6C493} + {0B3265A9-6716-4D28-8648-C64D5E692ACA} = {047A9723-9AAC-42E3-8C69-B3835F15FF96} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB40D0C5-E3EA-4A9B-86C2-38F0BB33FC04}