105040 Add 权限服务,角色服务

This commit is contained in:
lihanbo 2024-11-01 16:46:18 +08:00
parent 2a91923955
commit 1f0cd62488
22 changed files with 702 additions and 25 deletions

View File

@ -16,6 +16,8 @@
<PackageReference Include="Serilog" Version="4.1.1-dev-02318" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.1-dev-10398" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="SQLite" Version="3.13.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.171-preview03" />

View File

@ -16,6 +16,7 @@ public static class Program
public static void Main(string[] args)
{
// 先用Serilog的BootstrapLogger
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
@ -23,6 +24,7 @@ public static class Program
.WriteTo.Console()
.CreateBootstrapLogger();
ConfigDllLoader();
var builder = WebApplication.CreateBuilder(args);
// 添加到Service中
builder.Services.AddSerilog((services, lc) => lc
@ -31,6 +33,10 @@ public static class Program
.Enrich.FromLogContext()
// .WriteTo.Console()
);
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddSerilog(Log.Logger, dispose: true);
});
builder.Services.AddResponseCompression();
builder.Services.AddResponseCaching();
@ -38,13 +44,6 @@ public static class Program
Log.Logger.Information("LoadSubService");
builder.Services.LoadSubService();
// 注册中间件
builder.Services.RegisterMiddlewares(App.SubServiceAssembly);
// 注册内部事件总线
builder.Services.AddInternalEventBus(App.SubServiceAssembly);
// 注册自身缓存器
builder.Services.AddSelfCache();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
@ -80,16 +79,25 @@ public static class Program
});
});
// 使用Serilog此处为了修复 `The logger is already frozen` 的问题重新配置Serilog
builder.Host.UseSerilog((context, services, config) =>
{
config.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext();
// .WriteTo.Console();
}, true);
// init App.service
//// 使用Serilog此处为了修复 `The logger is already frozen` 的问题重新配置Serilog
//builder.Host.UseSerilog((context, services, config) =>
//{
// config.ReadFrom.Configuration(context.Configuration)
// .ReadFrom.Services(services)
// .Enrich.FromLogContext();
// // .WriteTo.Console();
//}, true);
builder.Host.UseSerilog(Log.Logger);
// 注册中间件
builder.Services.RegisterMiddlewares(App.SubServiceAssembly);
// 注册内部事件总线
builder.Services.AddInternalEventBus(App.SubServiceAssembly);
// 注册自身缓存器
builder.Services.AddSelfCache();
// init App.service
App.services = builder.Services;
var app = builder.Build();
app.UseSerilogRequestLogging(options =>
@ -118,7 +126,7 @@ public static class Program
{
Log.Logger.Information("ApplicationStarted");
CodeFirst.InitTable();
await InternalEventBus.InternalEventBus.Publish("Init", "ApplicationStarted");
InternalEventBus.InternalEventBus.Publish("Init", "ApplicationStarted");
});
// 启用自定义中间件
app.UseLFlowMiddleware();
@ -225,5 +233,38 @@ public static class Program
}
public static void ConfigDllLoader()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var assemblyName = new AssemblyName(args.Name);
// 判断是否已经被加载
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.FullName == assemblyName.FullName);
if (loadedAssembly != null)
{
Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded.");
return loadedAssembly;
}
// 从当前路径下加载
var path = Path.Combine(AppContext.BaseDirectory, $"{assemblyName.Name}.dll");
if (File.Exists(path))
{
Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded for {path}.");
return Assembly.LoadFile(path);
}
// 从Service文件夹中加载
path = Path.Combine(AppContext.BaseDirectory, "Service", $"{assemblyName.Name}.dll");
if (File.Exists(path))
{
Log.Logger?.Information($"Assembly {assemblyName.Name} has been loaded for {path}.");
return Assembly.LoadFile(path);
}
Log.Logger?.Error($"Assembly {assemblyName.Name} not found.");
return null;
};
}
}

View File

@ -4,9 +4,9 @@
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft.AspNetCore.Mvc": "Information",
"Microsoft.AspNetCore.Routing": "Information",
"Microsoft.AspNetCore.Hosting": "Information"
"Microsoft.AspNetCore.Mvc": "Debug",
"Microsoft.AspNetCore.Routing": "Debug",
"Microsoft.AspNetCore.Hosting": "Debug"
}
},
"WriteTo": [

View File

@ -34,6 +34,7 @@ namespace LFlow.Cache.Cacher
public Task RemoveAsync(string key)
{
if (CacheProvider.TryGetValue(key, out _))
CacheProvider.Remove(key);
return Task.CompletedTask;
}

View File

@ -0,0 +1,45 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Permission.Model;
using LFlow.Permission.Service;
using Microsoft.AspNetCore.Mvc;
using Serilog;
namespace LFlow.Permission.Controller
{
/// <summary>
/// 权限控制
/// </summary>
/// <param name="service"></param>
/// <param name="logger"></param>
public class PermissionController : BaseController
{
private IPermissionService service;
private ILogger logger;
public PermissionController(IPermissionService service, ILogger logger)
{
this.service = service;
this.logger = logger;
}
/// <summary>
/// 查询权限
/// </summary>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <returns></returns>
[HttpGet]
public async Task<PagedApiResult<List<PermissionDto>>> GetAll(int pageSize, int pageIndex)
{
var total = 0;
var list = await service.GetPermissionListAsync(pageIndex, pageSize, ref total);
return new PagedApiResult<List<PermissionDto>>
{
Data = list,
TotalCount = total
};
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/Services/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,56 @@
using LFlow.Base.Interfaces;
namespace LFlow.Permission.Model
{
public class PermissionDto : IModel
{
/// <summary>
/// ID
/// </summary>
public string? ID
{
get;
set;
}
/// <summary>
/// 角色ID
/// </summary>
public string? RoleId
{
get;
set;
}
/// <summary>
/// 权限对应程序ID
/// </summary>
public string? PermissionProgID
{
get;
set;
}
/// <summary>
/// 权限对应程序名称
/// </summary>
public string? PermissionProgName
{
get;
set;
}
/// <summary>
/// 权限名称
/// </summary>
public string? PermissionName
{
get;
set;
}
/// <summary>
/// 权限接口
/// </summary>
public string? PermissionAction
{
get;
set;
}
}
}

View File

@ -0,0 +1,55 @@
using LFlow.Base.Interfaces;
using SqlSugar;
namespace LFlow.Permission.Model
{
[SugarTable("T_U_USERINFO")]
public class PermissionModel : IDataModel
{
[SugarColumn(IsPrimaryKey = true)]
public string ID
{
get;
set;
}
public string RoleId
{
get;
set;
}
/// <summary>
/// 权限对应程序ID
/// </summary>
public string PermissionProgID
{
get;
set;
}
/// <summary>
/// 权限对应程序名称
/// </summary>
public string PermissionProgName
{
get;
set;
}
/// <summary>
/// 权限名称
/// </summary>
public string PermissionName
{
get;
set;
}
/// <summary>
/// 权限接口
/// </summary>
public string PermissionAction
{
get;
set;
}
}
}

View File

@ -0,0 +1,23 @@
using System.Reflection;
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
namespace LFlow.Permission;
public class PermissionModule : IModule
{
public void ConfigureModule(IServiceCollection services)
{
CodeFirst.AddType(typeof(PermissionModule));
var assembly = Assembly.GetAssembly(typeof(PermissionModule))!;
var types = assembly.GetTypes().ToList();
RegisterModule.RegisterAllService(types, services);
RegisterModule.RegisterAllRepo(types, services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("UserManegementModule ConfigureModule done");
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Default;
using LFlow.Permission.Model;
using SqlSugar;
namespace LFlow.Permission.Repository
{
internal class RoleRepo(ISqlSugarClient client) : DefaultCurdRepo<PermissionModel, string>(client)
{
}
}

View File

@ -0,0 +1,21 @@
using LFlow.Base.Interfaces;
using LFlow.Permission.Model;
namespace LFlow.Permission.Service
{
public interface IPermissionService : IService//<VersionDto>
{
Task<List<PermissionDto>> GetPermissionListAsync(int pageIndex, int pageSize, ref int total);
Task<PermissionDto> GetPermissionAsync(string id);
Task<PermissionDto> AddPermissionAsync(PermissionDto model);
Task<PermissionDto> UpdatePermissionAsync(PermissionDto model);
Task<int> DeletePermissionAsync(string id);
/// <summary>
/// 获取程序权限列表
/// </summary>
/// <param name="progID"></param>
/// <returns></returns>
Task<List<PermissionDto>> GetProgPerminssionListAsync(string progID);
}
}

View File

@ -0,0 +1,100 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.Permission.Model;
using Mapster;
using SqlSugar;
namespace LFlow.Permission.Service
{
/// <summary>
/// 角色服务
/// </summary>
public class PermissionService(IRepo<PermissionModel, string> repo, ISelfCache cacher) : IPermissionService
{
/// <summary>
/// 添加权限项
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public Task<PermissionDto> AddPermissionAsync(PermissionDto model)
{
var savedModel = repo.SaveOrUpdate(model.Adapt<PermissionModel>(), isUpdate: false);
var result = savedModel?.MapTo<PermissionDto>() ?? throw new InvalidOperationException("Failed to add the permission model.");
return Task.FromResult(result);
}
/// <summary>
/// 删除权限项并清理缓存
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Task<int> DeletePermissionAsync(string id)
{
cacher.RemoveAsync(id);
return Task.FromResult(repo.DeleteById(id));
}
/// <summary>
/// 根据ID获取权限
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<PermissionDto> GetPermissionAsync(string id)
{
var cachedPermission = await cacher.GetAsync<PermissionModel>(id);
if (cachedPermission != null)
{
return cachedPermission!.MapTo<PermissionDto>()!;
}
var permission = await Task.FromResult(repo.Get(id));
if (permission != null)
{
await cacher.SetAsync(id, permission);
}
return permission?.MapTo<PermissionDto>() ?? new PermissionDto();
}
/// <summary>
/// 获取权限列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="total"></param>
/// <returns></returns>
public Task<List<PermissionDto>> GetPermissionListAsync(int pageIndex, int pageSize, ref int total)
{
return Task.FromResult(repo.GetAll(pageIndex, pageSize, ref total).Adapt<List<PermissionDto>>());
}
/// <summary>
/// 获取程序权限列表
/// </summary>
/// <param name="progID"></param>
/// <returns></returns>
public Task<List<PermissionDto>> GetProgPerminssionListAsync(string progID)
{
return Task.FromResult(repo.Search(new PermissionModel
{
PermissionProgID = progID
}).Adapt<List<PermissionDto>>());
}
/// <summary>
/// 更新权限项
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public Task<PermissionDto> UpdatePermissionAsync(PermissionDto model)
{
if (model != null && !string.IsNullOrEmpty(model.ID))
{
cacher.RemoveAsync(model.ID);
}
if (model != null)
{
var permissionModel = model?.Adapt<PermissionModel>() ?? throw new ArgumentNullException(nameof(model));
var savedModel = repo.SaveOrUpdate(permissionModel, isUpdate: true);
return Task.FromResult(savedModel?.MapTo<PermissionDto>() ?? throw new InvalidOperationException("Failed to update the permission model."));
}
throw new ArgumentNullException(nameof(model));
}
}
}

View File

@ -0,0 +1,11 @@
using LFlow.Base.Interfaces;
using LFlow.Role.Service;
using Serilog;
namespace LFlow.Role.Controller
{
public class RoleController(IRoleService service, ILogger logger) : BaseController
{
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputPath>../LFlow_Bin/Services/</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LFlow.Base\LFlow.Base.csproj" />
<ProjectReference Include="..\LFlow.Permission\LFlow.Permission.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
using LFlow.Base.Interfaces;
using LFlow.Permission.Model;
namespace LFlow.Role.Model
{
public class RoleDto : IModel
{
public string? ID
{
get;
set;
}
public string? RoleName
{
get;
set;
}
public string? RoleDesc
{
get;
set;
}
public string? RoleType
{
get;
set;
}
public string? RoleStatus
{
get;
set;
}
public bool? IsDefault
{
get;
set;
}
public bool? IsEnable
{
get;
set;
}
/// <summary>
/// 权限列表
/// </summary>
public List<PermissionDto> Permissions { get; set; } = [];
}
}

View File

@ -0,0 +1,52 @@
using LFlow.Base.Interfaces;
using SqlSugar;
namespace LFlow.Role.Model
{
[SugarTable("T_U_USERINFO")]
public class RoleModel : IDataModel
{
[SugarColumn(IsPrimaryKey = true)]
public string ID
{
get;
set;
}
public string RoleName
{
get;
set;
}
public string RoleDesc
{
get;
set;
}
public string RoleType
{
get;
set;
}
public string RoleStatus
{
get;
set;
}
public bool IsDefault
{
get;
set;
}
public bool IsEnable
{
get;
set;
}
}
}

View File

@ -0,0 +1,10 @@
using LFlow.Base.Default;
using LFlow.Role.Model;
using SqlSugar;
namespace LFlow.Role.Repository
{
internal class RoleRepo(ISqlSugarClient client) : DefaultCurdRepo<RoleModel, string>(client)
{
}
}

24
LFlow.Role/RoleModule.cs Normal file
View File

@ -0,0 +1,24 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Role.Model;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System.Reflection;
namespace LFlow.Role
{
public class RoleModule : IModule
{
public void ConfigureModule(IServiceCollection services)
{
CodeFirst.AddType(typeof(RoleModel));
var assembly = Assembly.GetAssembly(typeof(RoleModule))!;
var types = assembly.GetTypes().ToList();
RegisterModule.RegisterAllService(types, services);
RegisterModule.RegisterAllRepo(types, services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("UserManegementModule ConfigureModule done");
}
}
}

View File

@ -0,0 +1,21 @@
using LFlow.Base.Interfaces;
using LFlow.Role.Model;
namespace LFlow.Role.Service
{
public interface IRoleService : IService//<VersionDto>
{
Task<List<RoleDto>> GetRoleListAsync(int pageIndex, int pageSize, ref int total);
Task<RoleDto> GetRoleAsync(string id);
Task<RoleDto> AddRoleAsync(RoleDto model);
Task<RoleDto> UpdateRoleAsync(RoleDto model);
Task<int> DeleteRoleAsync(string id);
/// <summary>
/// 获取角色权限列表
/// </summary>
/// <param name="progID"></param>
/// <returns></returns>
Task<RoleDto> GetRolePerminssionListAsync(string roleId);
}
}

View File

@ -0,0 +1,103 @@
using LFlow.Base.Interfaces;
using LFlow.Base.Utils;
using LFlow.Cache.Interface;
using LFlow.Permission.Service;
using LFlow.Role.Model;
using Mapster;
namespace LFlow.Role.Service
{
/// <summary>
/// 角色服务
/// </summary>
public class RoleService(IRepo<RoleModel, string> repo, IPermissionService permissionService, ISelfCache cacher) : IRoleService
{
/// <summary>
/// 添加角色
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public Task<RoleDto> AddRoleAsync(RoleDto model)
{
var savedRole = repo.SaveOrUpdate(model.Adapt<RoleModel>(), isUpdate: false);
if (savedRole == null)
{
throw new InvalidOperationException("Failed to save or update the role.");
}
var roleDto = savedRole.MapTo<RoleDto>();
if (roleDto == null)
{
throw new InvalidOperationException("Failed to map the saved role to RoleDto.");
}
return Task.FromResult(roleDto);
}
/// <summary>
/// 删除角色并清理缓存
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Task<int> DeleteRoleAsync(string id)
{
cacher.RemoveAsync(id);
return Task.FromResult(repo.DeleteById(id));
}
public async Task<RoleDto> GetRolePerminssionListAsync(string roleId)
{
var roleDto = GetRoleAsync(roleId).Result;
if (roleDto == null)
{
return new RoleDto();
}
var permissions = await permissionService.GetProgPerminssionListAsync(roleId);
if (permissions != null && permissions.Count >= 0)
{
roleDto.Permissions = permissions!;
return roleDto;
}
else
{
return new RoleDto();
}
}
/// <summary>
/// 根据ID获取角色
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<RoleDto> GetRoleAsync(string id)
{
var cachedRole = await cacher.GetAsync<RoleDto>(id);
if (cachedRole != null)
{
return cachedRole;
}
var role = await Task.FromResult(repo.Get(id));
if (role != null)
{
await cacher.SetAsync(id, role);
}
return role?.MapTo<RoleDto>() ?? new RoleDto();
}
/// <summary>
/// 获取角色列表
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="total"></param>
/// <returns></returns>
public Task<List<RoleDto>> GetRoleListAsync(int pageIndex, int pageSize, ref int total)
{
var roleModels = repo.GetAll(pageIndex, pageSize, ref total);
var roleDtos = roleModels?.Select(role => role.MapTo<RoleDto>()).ToList() ?? [];
return Task.FromResult(roleDtos ?? [])!;
}
public Task<RoleDto> UpdateRoleAsync(RoleDto model)
{
throw new NotImplementedException();
}
}
}

View File

@ -16,7 +16,7 @@ namespace LFlow.UserManagement
var types = assembly.GetTypes().ToList();
RegisterModule.RegisterAllService(types, services);
RegisterModule.RegisterAllRepo(types, services);
RegisterModule.RegisterAllModel(types, services);
//RegisterModule.RegisterAllModel(types, services);
services.AddControllers().AddApplicationPart(assembly);
Log.Logger?.Information("UserManegementModule ConfigureModule done");
}

View File

@ -11,11 +11,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.OnlineManegement", "L
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.UserManagement", "LFlow.UserManagement\LFlow.UserManagement.csproj", "{DA68CE22-AC53-40BD-AB2A-5C52DFDDD548}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.Middleware", "LFlow.Middleware\LFlow.Middleware.csproj", "{5BFD207E-28B3-40B8-94DF-1723C6A4424B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.Middleware", "LFlow.Middleware\LFlow.Middleware.csproj", "{5BFD207E-28B3-40B8-94DF-1723C6A4424B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.InternalEventBus", "LFlow.InternalEventBus\LFlow.InternalEventBus.csproj", "{72CB0C72-9725-43A5-882D-3E93FD1F8706}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.InternalEventBus", "LFlow.InternalEventBus\LFlow.InternalEventBus.csproj", "{72CB0C72-9725-43A5-882D-3E93FD1F8706}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.Cache", "LFlow.Cache\LFlow.Cache.csproj", "{97D56496-BE2A-4431-A882-7DEA20B72EB0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LFlow.Cache", "LFlow.Cache\LFlow.Cache.csproj", "{97D56496-BE2A-4431-A882-7DEA20B72EB0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.Role", "LFlow.Role\LFlow.Role.csproj", "{05CEB068-167F-4B27-A59A-EFBF0A656C43}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LFlow.Permission", "LFlow.Permission\LFlow.Permission.csproj", "{E7C112C2-534C-4212-959F-FBB4E21A41C1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -51,6 +55,14 @@ Global
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97D56496-BE2A-4431-A882-7DEA20B72EB0}.Release|Any CPU.Build.0 = Release|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05CEB068-167F-4B27-A59A-EFBF0A656C43}.Release|Any CPU.Build.0 = Release|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7C112C2-534C-4212-959F-FBB4E21A41C1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE