Compare commits

..

No commits in common. "df7c7e071780830d2bb0a3dbe7575e4f66223960" and "f14bf019f17ca33af59d211e20edbe0f04af3aa6" have entirely different histories.

76 changed files with 782 additions and 1337 deletions

View File

@ -1,7 +1,5 @@
namespace Fengling.Console.Controllers; namespace Fengling.Console.Controllers;
using Fengling.Console.Services;
/// <summary> /// <summary>
/// 租户管理控制器 /// 租户管理控制器
/// 提供租户的增删改查以及租户用户、角色、配置管理功能 /// 提供租户的增删改查以及租户用户、角色、配置管理功能
@ -12,13 +10,11 @@ using Fengling.Console.Services;
public class TenantsController : ControllerBase public class TenantsController : ControllerBase
{ {
private readonly ITenantService _tenantService; private readonly ITenantService _tenantService;
private readonly IH5LinkService _h5LinkService;
private readonly ILogger<TenantsController> _logger; private readonly ILogger<TenantsController> _logger;
public TenantsController(ITenantService tenantService, IH5LinkService h5LinkService, ILogger<TenantsController> logger) public TenantsController(ITenantService tenantService, ILogger<TenantsController> logger)
{ {
_tenantService = tenantService; _tenantService = tenantService;
_h5LinkService = h5LinkService;
_logger = logger; _logger = logger;
} }
@ -68,7 +64,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(typeof(TenantDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(TenantDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<TenantDto>> GetTenant(int id) public async Task<ActionResult<TenantDto>> GetTenant(long id)
{ {
try try
{ {
@ -100,7 +96,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(typeof(IEnumerable<UserDto>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(IEnumerable<UserDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<UserDto>>> GetTenantUsers(int tenantId) public async Task<ActionResult<IEnumerable<UserDto>>> GetTenantUsers(long tenantId)
{ {
try try
{ {
@ -132,7 +128,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(typeof(IEnumerable<object>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(IEnumerable<object>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<object>>> GetTenantRoles(int tenantId) public async Task<ActionResult<IEnumerable<object>>> GetTenantRoles(long tenantId)
{ {
try try
{ {
@ -164,7 +160,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(typeof(TenantSettingsDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(TenantSettingsDto), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<TenantSettingsDto>> GetTenantSettings(int id) public async Task<ActionResult<TenantSettingsDto>> GetTenantSettings(long id)
{ {
try try
{ {
@ -196,7 +192,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UpdateTenantSettings(int id, [FromBody] TenantSettingsDto settings) public async Task<IActionResult> UpdateTenantSettings(long id, [FromBody] TenantSettingsDto settings)
{ {
try try
{ {
@ -253,7 +249,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UpdateTenant(int id, [FromBody] UpdateTenantDto dto) public async Task<IActionResult> UpdateTenant(long id, [FromBody] UpdateTenantDto dto)
{ {
try try
{ {
@ -284,7 +280,7 @@ public class TenantsController : ControllerBase
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> DeleteTenant(int id) public async Task<IActionResult> DeleteTenant(long id)
{ {
try try
{ {
@ -302,36 +298,4 @@ public class TenantsController : ControllerBase
return StatusCode(500, new { message = ex.Message }); return StatusCode(500, new { message = ex.Message });
} }
} }
/// <summary>
/// 生成H5访问链接和二维码
/// </summary>
/// <param name="id">租户ID</param>
/// <returns>H5链接和二维码Base64</returns>
/// <response code="200">成功返回链接和二维码</response>
/// <response code="404">租户不存在</response>
/// <response code="500">服务器内部错误</response>
[HttpGet("{id}/h5-link")]
[Produces("application/json")]
[ProducesResponseType(typeof(H5LinkResult), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<H5LinkResult>> GetH5Link(int id)
{
try
{
var result = await _h5LinkService.GenerateH5LinkAsync(id);
return Ok(result);
}
catch (KeyNotFoundException ex)
{
_logger.LogWarning(ex, "Tenant not found: {TenantId}", id);
return NotFound(new { message = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error generating H5 link for tenant {TenantId}", id);
return StatusCode(500, new { message = "Failed to generate H5 link" });
}
}
} }

View File

@ -0,0 +1,85 @@
using Fengling.Console.Models.Entities;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Fengling.Console.Datas;
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<ApplicationUser, ApplicationRole, long>(options)
{
public DbSet<Tenant> Tenants { get; set; }
public DbSet<AccessLog> AccessLogs { get; set; }
public DbSet<AuditLog> AuditLogs { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(entity =>
{
entity.Property(e => e.RealName).HasMaxLength(100);
entity.Property(e => e.Phone).HasMaxLength(20);
entity.HasIndex(e => e.Phone).IsUnique();
entity.OwnsOne(e => e.TenantInfo, navigationBuilder =>
{
navigationBuilder.Property(e => e.Id).HasColumnName("TenantId");
navigationBuilder.Property(e => e.TenantId).HasColumnName("TenantCode");
navigationBuilder.Property(e => e.Name).HasColumnName("TenantName");
navigationBuilder.WithOwner();
});
});
builder.Entity<ApplicationRole>(entity => { entity.Property(e => e.Description).HasMaxLength(200); });
builder.Entity<Tenant>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.TenantId).IsUnique();
entity.Property(e => e.TenantId).HasMaxLength(50);
entity.Property(e => e.Name).HasMaxLength(100);
entity.Property(e => e.ContactName).HasMaxLength(50);
entity.Property(e => e.ContactEmail).HasMaxLength(100);
entity.Property(e => e.ContactPhone).HasMaxLength(20);
entity.Property(e => e.Status).HasMaxLength(20);
entity.Property(e => e.Description).HasMaxLength(500);
});
builder.Entity<AccessLog>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.CreatedAt);
entity.HasIndex(e => e.UserName);
entity.HasIndex(e => e.TenantId);
entity.HasIndex(e => e.Action);
entity.HasIndex(e => e.Status);
entity.Property(e => e.UserName).HasMaxLength(50);
entity.Property(e => e.TenantId).HasMaxLength(50);
entity.Property(e => e.Action).HasMaxLength(20);
entity.Property(e => e.Resource).HasMaxLength(200);
entity.Property(e => e.Method).HasMaxLength(10);
entity.Property(e => e.IpAddress).HasMaxLength(50);
entity.Property(e => e.UserAgent).HasMaxLength(500);
entity.Property(e => e.Status).HasMaxLength(20);
});
builder.Entity<AuditLog>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasIndex(e => e.CreatedAt);
entity.HasIndex(e => e.Operator);
entity.HasIndex(e => e.TenantId);
entity.HasIndex(e => e.Operation);
entity.HasIndex(e => e.Action);
entity.Property(e => e.Operator).HasMaxLength(50);
entity.Property(e => e.TenantId).HasMaxLength(50);
entity.Property(e => e.Operation).HasMaxLength(20);
entity.Property(e => e.Action).HasMaxLength(20);
entity.Property(e => e.TargetType).HasMaxLength(50);
entity.Property(e => e.TargetName).HasMaxLength(100);
entity.Property(e => e.IpAddress).HasMaxLength(50);
entity.Property(e => e.Description).HasMaxLength(500);
entity.Property(e => e.Status).HasMaxLength(20);
});
}
}

View File

@ -4,7 +4,6 @@
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@ -19,8 +18,6 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="NetCorePal.Extensions.AspNetCore" />
<PackageReference Include="NetCorePal.Extensions.DistributedLocks.Redis" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
<PackageReference Include="OpenIddict.Abstractions" /> <PackageReference Include="OpenIddict.Abstractions" />
<PackageReference Include="OpenIddict.AspNetCore" /> <PackageReference Include="OpenIddict.AspNetCore" />
@ -28,14 +25,11 @@
<PackageReference Include="OpenIddict.Server" /> <PackageReference Include="OpenIddict.Server" />
<PackageReference Include="OpenIddict.Server.AspNetCore" /> <PackageReference Include="OpenIddict.Server.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore" /> <PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="SkiaSharp" />
<PackageReference Include="QRCoder" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<!-- <ProjectReference Include="..\Fengling.AuthService\Fengling.AuthService.csproj" />--> <!-- <ProjectReference Include="..\Fengling.AuthService\Fengling.AuthService.csproj" />-->
<ProjectReference Include="..\YarpGateway\YarpGateway.csproj" /> <ProjectReference Include="..\YarpGateway\YarpGateway.csproj" />
<ProjectReference Include="..\Fengling.Platform\Fengling.Platform.Infrastructure\Fengling.Platform.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -5,7 +5,7 @@ namespace Fengling.Console.Models.Dtos;
public class CreateTenantDto public class CreateTenantDto
{ {
[Required] [Required]
public string TenantCode { get; set; } = string.Empty; public string TenantId { get; set; } = string.Empty;
[Required] [Required]
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
@ -26,16 +26,4 @@ public class CreateTenantDto
public string Status { get; set; } = "active"; public string Status { get; set; } = "active";
public DateTime? ExpiresAt { get; set; } public DateTime? ExpiresAt { get; set; }
[MaxLength(255)]
public string? CustomDomain { get; set; }
[MaxLength(50)]
public string? BasePath { get; set; }
[MaxLength(500)]
public string? Logo { get; set; }
[MaxLength(500)]
public string? H5BaseUrl { get; set; }
} }

View File

@ -14,7 +14,7 @@ public class CreateUserDto
[Required] [Required]
public string RealName { get; set; } = string.Empty; public string RealName { get; set; } = string.Empty;
public string? PhoneNumber { get; set; } public string? Phone { get; set; }
public long? TenantId { get; set; } public long? TenantId { get; set; }

View File

@ -10,5 +10,5 @@ public class RoleDto
public bool IsSystem { get; set; } public bool IsSystem { get; set; }
public List<string>? Permissions { get; set; } public List<string>? Permissions { get; set; }
public int UserCount { get; set; } public int UserCount { get; set; }
public DateTimeOffset CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
} }

View File

@ -1,23 +1,17 @@
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
namespace Fengling.Console.Models.Dtos; namespace Fengling.Console.Models.Dtos;
public class TenantDto public class TenantDto
{ {
public long Id { get; set; } public long Id { get; set; }
public string TenantCode { get; set; } = ""; public string TenantId { get; set; } = "";
public string Name { get; set; } = ""; public string Name { get; set; } = "";
public string ContactName { get; set; } = ""; public string ContactName { get; set; } = "";
public string ContactEmail { get; set; } = ""; public string ContactEmail { get; set; } = "";
public string? ContactPhone { get; set; } public string? ContactPhone { get; set; }
public int? MaxUsers { get; set; } public int? MaxUsers { get; set; }
public int UserCount { get; set; } public int UserCount { get; set; }
public TenantStatus Status { get; set; } = TenantStatus.Active; public string Status { get; set; } = "active";
public DateTime? ExpiresAt { get; set; } public DateTime? ExpiresAt { get; set; }
public string? Description { get; set; } public string? Description { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public string? CustomDomain { get; set; }
public string? BasePath { get; set; }
public string? Logo { get; set; }
public string? H5BaseUrl { get; set; }
} }

View File

@ -1,5 +1,3 @@
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
namespace Fengling.Console.Models.Dtos; namespace Fengling.Console.Models.Dtos;
public class TenantQueryDto : PaginationQueryDto public class TenantQueryDto : PaginationQueryDto
@ -8,5 +6,5 @@ public class TenantQueryDto : PaginationQueryDto
public string? TenantId { get; set; } public string? TenantId { get; set; }
public TenantStatus? Status { get; set; } public string? Status { get; set; }
} }

View File

@ -23,16 +23,4 @@ public class UpdateTenantDto
public string Status { get; set; } = "active"; public string Status { get; set; } = "active";
public DateTime? ExpiresAt { get; set; } public DateTime? ExpiresAt { get; set; }
[MaxLength(255)]
public string? CustomDomain { get; set; }
[MaxLength(50)]
public string? BasePath { get; set; }
[MaxLength(500)]
public string? Logo { get; set; }
[MaxLength(500)]
public string? H5BaseUrl { get; set; }
} }

View File

@ -11,7 +11,7 @@ public class UpdateUserDto
[Required] [Required]
public string RealName { get; set; } = string.Empty; public string RealName { get; set; } = string.Empty;
public string? PhoneNumber { get; set; } public string? Phone { get; set; }
public bool EmailConfirmed { get; set; } public bool EmailConfirmed { get; set; }

View File

@ -6,11 +6,11 @@ public class UserDto
public string? UserName { get; set; } public string? UserName { get; set; }
public string? Email { get; set; } public string? Email { get; set; }
public string? RealName { get; set; } public string? RealName { get; set; }
public string? PhoneNumber { get; set; } public string? Phone { get; set; }
public string? TenantCode { get; set; } = ""; public long TenantId { get; set; }
public string? TenantName { get; set; } = ""; public string TenantName { get; set; } = "";
public List<string> Roles { get; set; } = new(); public List<string> Roles { get; set; } = new();
public bool EmailConfirmed { get; set; } public bool EmailConfirmed { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
public DateTimeOffset CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
} }

View File

@ -0,0 +1,43 @@
using System.ComponentModel.DataAnnotations;
namespace Fengling.Console.Models.Entities;
public class AccessLog
{
[Key]
public long Id { get; set; }
[MaxLength(50)]
public string? UserName { get; set; }
[MaxLength(50)]
public string? TenantId { get; set; }
[MaxLength(20)]
public string Action { get; set; } = string.Empty;
[MaxLength(200)]
public string? Resource { get; set; }
[MaxLength(10)]
public string? Method { get; set; }
[MaxLength(50)]
public string? IpAddress { get; set; }
[MaxLength(500)]
public string? UserAgent { get; set; }
[MaxLength(20)]
public string Status { get; set; } = "success";
public int Duration { get; set; }
public string? RequestData { get; set; }
public string? ResponseData { get; set; }
public string? ErrorMessage { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View File

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Identity;
namespace Fengling.Console.Models.Entities;
public class ApplicationRole : IdentityRole<long>
{
public string? Description { get; set; }
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
public long? TenantId { get; set; }
public bool IsSystem { get; set; }
public string? DisplayName { get; set; }
public List<string>? Permissions { get; set; }
}

View File

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Identity;
namespace Fengling.Console.Models.Entities;
public class ApplicationUser : IdentityUser<long>
{
public string? RealName { get; set; }
public string? Phone { get; set; }
public TenantInfo TenantInfo { get; set; } = null!;
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedTime { get; set; }
public bool IsDeleted { get; set; }
}

View File

@ -0,0 +1,47 @@
using System.ComponentModel.DataAnnotations;
namespace Fengling.Console.Models.Entities;
public class AuditLog
{
[Key]
public long Id { get; set; }
[MaxLength(50)]
[Required]
public string Operator { get; set; } = string.Empty;
[MaxLength(50)]
public string? TenantId { get; set; }
[MaxLength(20)]
public string Operation { get; set; } = string.Empty;
[MaxLength(20)]
public string Action { get; set; } = string.Empty;
[MaxLength(50)]
public string? TargetType { get; set; }
public long? TargetId { get; set; }
[MaxLength(100)]
public string? TargetName { get; set; }
[MaxLength(50)]
public string IpAddress { get; set; } = string.Empty;
[MaxLength(500)]
public string? Description { get; set; }
public string? OldValue { get; set; }
public string? NewValue { get; set; }
public string? ErrorMessage { get; set; }
[MaxLength(20)]
public string Status { get; set; } = "success";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

63
Models/Entities/Tenant.cs Normal file
View File

@ -0,0 +1,63 @@
using System.ComponentModel.DataAnnotations;
namespace Fengling.Console.Models.Entities;
public class Tenant
{
private long _id;
private string _tenantId;
private string _name;
[Key]
public long Id
{
get => _id;
set => _id = value;
}
[MaxLength(50)]
[Required]
public string TenantId
{
get => _tenantId;
set => _tenantId = value;
}
[MaxLength(100)]
[Required]
public string Name
{
get => _name;
set => _name = value;
}
[MaxLength(50)]
[Required]
public string ContactName { get; set; } = string.Empty;
[MaxLength(100)]
[Required]
[EmailAddress]
public string ContactEmail { get; set; } = string.Empty;
[MaxLength(20)]
public string? ContactPhone { get; set; }
public int? MaxUsers { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
[MaxLength(500)]
public string? Description { get; set; }
[MaxLength(20)]
public string Status { get; set; } = "active";
public DateTime? ExpiresAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public bool IsDeleted { get; set; }
public TenantInfo Info => new(Id, TenantId, Name);
}

View File

@ -0,0 +1,3 @@
namespace Fengling.Console.Models.Entities;
public record TenantInfo(long Id, string TenantId, string Name);

View File

@ -1,15 +1,14 @@
using System.Reflection; using System.Reflection;
using Fengling.Console.Repositories;
using Fengling.Console.Services; using Fengling.Console.Services;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Infrastructure;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.Text; using System.Text;
using NetCorePal.Extensions.DependencyInjection; using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
using OpenIddict.Validation.AspNetCore; using OpenIddict.Validation.AspNetCore;
using YarpGateway.Data; using YarpGateway.Data;
@ -17,42 +16,35 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); builder.Services.AddControllers();
// Use PlatformDbContext for all identity builder.Services.AddDbContext<ApplicationDbContext>(options =>
builder.Services.AddDbContext<PlatformDbContext>(options =>
{ {
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")); options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
if (builder.Environment.IsDevelopment())
{
options.EnableSensitiveDataLogging();
}
options.EnableDetailedErrors();
}); });
builder.Services.AddDbContext<GatewayDbContext>(options => builder.Services.AddDbContext<GatewayDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("GatewayConnection"))); options.UseNpgsql(builder.Configuration.GetConnectionString("GatewayConnection")));
// Use Platform's identity
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>() builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<PlatformDbContext>() .AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders(); .AddDefaultTokenProviders();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
builder.Services.AddScoped<IOAuthClientService, OAuthClientService>(); builder.Services.AddScoped<IOAuthClientService, OAuthClientService>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Register Platform managers builder.Services.AddScoped<ITenantRepository, TenantRepository>();
builder.Services.AddScoped<ITenantStore, TenantStore>(); builder.Services.AddScoped<IRoleRepository, RoleRepository>();
builder.Services.AddScoped<ITenantManager, TenantManager>();
builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ITenantService, TenantService>(); builder.Services.AddScoped<ITenantService, TenantService>();
builder.Services.AddScoped<IRoleService, RoleService>(); builder.Services.AddScoped<IRoleService, RoleService>();
builder.Services.AddScoped<IGatewayService, GatewayService>();
builder.Services.AddScoped<IH5LinkService, H5LinkService>();
builder.Services.AddOpenIddict() builder.Services.AddOpenIddict()
.AddCore(options => { options.UseEntityFrameworkCore().UseDbContext<PlatformDbContext>(); }) .AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
})
.AddValidation(options => .AddValidation(options =>
{ {
options.SetIssuer("http://localhost:5132/"); options.SetIssuer("http://localhost:5132/");
@ -78,8 +70,8 @@ builder.Services.AddCors(options =>
options.AddPolicy("AllowAll", policy => options.AddPolicy("AllowAll", policy =>
{ {
policy.AllowAnyOrigin() policy.AllowAnyOrigin()
.AllowAnyMethod() .AllowAnyMethod()
.AllowAnyHeader(); .AllowAnyHeader();
}); });
}); });
@ -87,7 +79,6 @@ builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => builder.Services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("v1", new() { Title = "Fengling.Console API", Version = "v1" }); c.SwaggerDoc("v1", new() { Title = "Fengling.Console API", Version = "v1" });
c.CustomSchemaIds(type => type.FullName);
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath)) if (File.Exists(xmlPath))
@ -96,14 +87,13 @@ builder.Services.AddSwaggerGen(c =>
} }
}); });
builder.Services.AddRepositories(typeof(PlatformDbContext).Assembly);
var app = builder.Build(); var app = builder.Build();
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Fengling.Console API V1"); }); app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Fengling.Console API V1");
});
app.UseCors("AllowAll"); app.UseCors("AllowAll");

View File

@ -0,0 +1,15 @@
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Repositories;
public interface IRoleRepository
{
Task<ApplicationRole?> GetByIdAsync(long id);
Task<ApplicationRole?> GetByNameAsync(string name);
Task<IEnumerable<ApplicationRole>> GetAllAsync();
Task<IEnumerable<ApplicationRole>> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantId = null);
Task<int> CountAsync(string? name = null, string? tenantId = null);
Task AddAsync(ApplicationRole role);
Task UpdateAsync(ApplicationRole role);
Task DeleteAsync(ApplicationRole role);
}

View File

@ -0,0 +1,16 @@
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Repositories;
public interface ITenantRepository
{
Task<Tenant?> GetByIdAsync(long id);
Task<Tenant?> GetByTenantIdAsync(string tenantId);
Task<IEnumerable<Tenant>> GetAllAsync();
Task<IEnumerable<Tenant>> GetPagedAsync(int page, int pageSize, string? name = null, string? tenantId = null, string? status = null);
Task<int> CountAsync(string? name = null, string? tenantId = null, string? status = null);
Task AddAsync(Tenant tenant);
Task UpdateAsync(Tenant tenant);
Task DeleteAsync(Tenant tenant);
Task<int> GetUserCountAsync(long tenantId);
}

View File

@ -0,0 +1,15 @@
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Repositories;
public interface IUserRepository
{
Task<ApplicationUser?> GetByIdAsync(long id);
Task<ApplicationUser?> GetByUserNameAsync(string userName);
Task<IEnumerable<ApplicationUser>> GetAllAsync();
Task<IEnumerable<ApplicationUser>> GetPagedAsync(int page, int pageSize, string? userName = null, string? email = null, string? tenantId = null);
Task<int> CountAsync(string? userName = null, string? email = null, string? tenantId = null);
Task AddAsync(ApplicationUser user);
Task UpdateAsync(ApplicationUser user);
Task DeleteAsync(ApplicationUser user);
}

View File

@ -0,0 +1,80 @@
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
using Microsoft.EntityFrameworkCore;
namespace Fengling.Console.Repositories;
public class RoleRepository(ApplicationDbContext context) : IRoleRepository
{
public async Task<ApplicationRole?> GetByIdAsync(long id)
{
return await context.Roles.FindAsync(id);
}
public async Task<ApplicationRole?> GetByNameAsync(string name)
{
return await context.Roles.FirstOrDefaultAsync(r => r.Name == name);
}
public async Task<IEnumerable<ApplicationRole>> GetAllAsync()
{
return await context.Roles.ToListAsync();
}
public async Task<IEnumerable<ApplicationRole>> GetPagedAsync(int page, int pageSize, string? name = null,
string? tenantId = null)
{
var query = context.Roles.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(r => r.Name != null && r.Name.Contains(name));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(r => r.TenantId.ToString() == tenantId);
}
return await query
.OrderByDescending(r => r.CreatedTime)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<int> CountAsync(string? name = null, string? tenantId = null)
{
var query = context.Roles.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(r => r.Name != null && r.Name.Contains(name));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(r => r.TenantId.ToString() == tenantId);
}
return await query.CountAsync();
}
public async Task AddAsync(ApplicationRole role)
{
context.Roles.Add(role);
await context.SaveChangesAsync();
}
public async Task UpdateAsync(ApplicationRole role)
{
context.Roles.Update(role);
await context.SaveChangesAsync();
}
public async Task DeleteAsync(ApplicationRole role)
{
context.Roles.Remove(role);
await context.SaveChangesAsync();
}
}

View File

@ -0,0 +1,95 @@
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
using Microsoft.EntityFrameworkCore;
namespace Fengling.Console.Repositories;
public class TenantRepository(ApplicationDbContext context) : ITenantRepository
{
public async Task<Tenant?> GetByIdAsync(long id)
{
return await context.Tenants.FindAsync(id);
}
public async Task<Tenant?> GetByTenantIdAsync(string tenantId)
{
return await context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
}
public async Task<IEnumerable<Tenant>> GetAllAsync()
{
return await context.Tenants.ToListAsync();
}
public async Task<IEnumerable<Tenant>> GetPagedAsync(int page, int pageSize, string? name = null,
string? tenantId = null, string? status = null)
{
var query = context.Tenants.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(t => t.Name.Contains(name));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(t => t.TenantId.Contains(tenantId));
}
if (!string.IsNullOrEmpty(status))
{
query = query.Where(t => t.Status == status);
}
return await query
.OrderByDescending(t => t.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<int> CountAsync(string? name = null, string? tenantId = null, string? status = null)
{
var query = context.Tenants.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
query = query.Where(t => t.Name.Contains(name));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(t => t.TenantId.Contains(tenantId));
}
if (!string.IsNullOrEmpty(status))
{
query = query.Where(t => t.Status == status);
}
return await query.CountAsync();
}
public async Task AddAsync(Tenant tenant)
{
context.Tenants.Add(tenant);
await context.SaveChangesAsync();
}
public async Task UpdateAsync(Tenant tenant)
{
context.Tenants.Update(tenant);
await context.SaveChangesAsync();
}
public async Task DeleteAsync(Tenant tenant)
{
context.Tenants.Remove(tenant);
await context.SaveChangesAsync();
}
public async Task<int> GetUserCountAsync(long tenantId)
{
return await context.Users.CountAsync(u => u.TenantInfo.Id == tenantId && !u.IsDeleted);
}
}

View File

@ -0,0 +1,96 @@
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
using Microsoft.EntityFrameworkCore;
namespace Fengling.Console.Repositories;
public class UserRepository : IUserRepository
{
private readonly ApplicationDbContext _context;
public UserRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<ApplicationUser?> GetByIdAsync(long id)
{
return await _context.Users.FindAsync(id);
}
public async Task<ApplicationUser?> GetByUserNameAsync(string userName)
{
return await _context.Users.FirstOrDefaultAsync(u => u.UserName == userName);
}
public async Task<IEnumerable<ApplicationUser>> GetAllAsync()
{
return await _context.Users.ToListAsync();
}
public async Task<IEnumerable<ApplicationUser>> GetPagedAsync(int page, int pageSize, string? userName = null, string? email = null, string? tenantId = null)
{
var query = _context.Users.AsQueryable();
if (!string.IsNullOrEmpty(userName))
{
query = query.Where(u => u.UserName != null && u.UserName.Contains(userName));
}
if (!string.IsNullOrEmpty(email))
{
query = query.Where(u => u.Email != null && u.Email.Contains(email));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(u => u.TenantInfo.Id.ToString() == tenantId);
}
return await query
.OrderByDescending(u => u.CreatedTime)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<int> CountAsync(string? userName = null, string? email = null, string? tenantId = null)
{
var query = _context.Users.AsQueryable();
if (!string.IsNullOrEmpty(userName))
{
query = query.Where(u => u.UserName != null && u.UserName.Contains(userName));
}
if (!string.IsNullOrEmpty(email))
{
query = query.Where(u => u.Email != null && u.Email.Contains(email));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(u => u.TenantInfo.Id.ToString() == tenantId);
}
return await query.CountAsync();
}
public async Task AddAsync(ApplicationUser user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(ApplicationUser user)
{
_context.Users.Update(user);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(ApplicationUser user)
{
_context.Users.Remove(user);
await _context.SaveChangesAsync();
}
}

View File

@ -24,19 +24,21 @@ public interface IGatewayService
public class GatewayService : IGatewayService public class GatewayService : IGatewayService
{ {
private readonly GatewayDbContext _dbContext; private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
private readonly ILogger<GatewayService> _logger; private readonly ILogger<GatewayService> _logger;
public GatewayService(GatewayDbContext dbContext, ILogger<GatewayService> logger) public GatewayService(IDbContextFactory<GatewayDbContext> dbContextFactory, ILogger<GatewayService> logger)
{ {
_dbContext = dbContext; _dbContextFactory = dbContextFactory;
_logger = logger; _logger = logger;
} }
public async Task<GatewayStatisticsDto> GetStatisticsAsync() public async Task<GatewayStatisticsDto> GetStatisticsAsync()
{ {
var routes = await _dbContext.TenantRoutes.Where(r => !r.IsDeleted).ToListAsync(); await using var db = await _dbContextFactory.CreateDbContextAsync();
var instances = await _dbContext.ServiceInstances.Where(i => !i.IsDeleted).ToListAsync();
var routes = await db.TenantRoutes.Where(r => !r.IsDeleted).ToListAsync();
var instances = await db.ServiceInstances.Where(i => !i.IsDeleted).ToListAsync();
return new GatewayStatisticsDto return new GatewayStatisticsDto
{ {
@ -55,7 +57,9 @@ public class GatewayService : IGatewayService
public async Task<List<GatewayServiceDto>> GetServicesAsync(bool globalOnly = false, string? tenantCode = null) public async Task<List<GatewayServiceDto>> GetServicesAsync(bool globalOnly = false, string? tenantCode = null)
{ {
var query = _dbContext.TenantRoutes.Where(r => !r.IsDeleted); await using var db = await _dbContextFactory.CreateDbContextAsync();
var query = db.TenantRoutes.Where(r => !r.IsDeleted);
if (globalOnly) if (globalOnly)
query = query.Where(r => r.IsGlobal); query = query.Where(r => r.IsGlobal);
@ -65,7 +69,7 @@ public class GatewayService : IGatewayService
var routes = await query.OrderByDescending(r => r.CreatedTime).ToListAsync(); var routes = await query.OrderByDescending(r => r.CreatedTime).ToListAsync();
var clusters = routes.Select(r => r.ClusterId).Distinct().ToList(); var clusters = routes.Select(r => r.ClusterId).Distinct().ToList();
var instances = await _dbContext.ServiceInstances var instances = await db.ServiceInstances
.Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted) .Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted)
.GroupBy(i => i.ClusterId) .GroupBy(i => i.ClusterId)
.ToDictionaryAsync(g => g.Key, g => g.Count()); .ToDictionaryAsync(g => g.Key, g => g.Count());
@ -75,7 +79,9 @@ public class GatewayService : IGatewayService
public async Task<GatewayServiceDto?> GetServiceAsync(string serviceName, string? tenantCode = null) public async Task<GatewayServiceDto?> GetServiceAsync(string serviceName, string? tenantCode = null)
{ {
var route = await _dbContext.TenantRoutes await using var db = await _dbContextFactory.CreateDbContextAsync();
var route = await db.TenantRoutes
.FirstOrDefaultAsync(r => .FirstOrDefaultAsync(r =>
r.ServiceName == serviceName && r.ServiceName == serviceName &&
r.IsDeleted == false && r.IsDeleted == false &&
@ -83,7 +89,7 @@ public class GatewayService : IGatewayService
if (route == null) return null; if (route == null) return null;
var instances = await _dbContext.ServiceInstances var instances = await db.ServiceInstances
.CountAsync(i => i.ClusterId == route.ClusterId && !i.IsDeleted); .CountAsync(i => i.ClusterId == route.ClusterId && !i.IsDeleted);
return MapToServiceDto(route, instances); return MapToServiceDto(route, instances);
@ -91,6 +97,8 @@ public class GatewayService : IGatewayService
public async Task<GatewayServiceDto> RegisterServiceAsync(CreateGatewayServiceDto dto) public async Task<GatewayServiceDto> RegisterServiceAsync(CreateGatewayServiceDto dto)
{ {
await using var db = await _dbContextFactory.CreateDbContextAsync();
var clusterId = $"{dto.ServicePrefix}-service"; var clusterId = $"{dto.ServicePrefix}-service";
var pathPattern = $"/{dto.ServicePrefix}/{dto.Version}/{{**path}}"; var pathPattern = $"/{dto.ServicePrefix}/{dto.Version}/{{**path}}";
var destinationId = string.IsNullOrEmpty(dto.DestinationId) var destinationId = string.IsNullOrEmpty(dto.DestinationId)
@ -98,7 +106,7 @@ public class GatewayService : IGatewayService
: dto.DestinationId; : dto.DestinationId;
// Check if route already exists // Check if route already exists
var existingRoute = await _dbContext.TenantRoutes var existingRoute = await db.TenantRoutes
.FirstOrDefaultAsync(r => .FirstOrDefaultAsync(r =>
r.ServiceName == dto.ServicePrefix && r.ServiceName == dto.ServicePrefix &&
r.IsGlobal == dto.IsGlobal && r.IsGlobal == dto.IsGlobal &&
@ -122,7 +130,7 @@ public class GatewayService : IGatewayService
Status = 1, Status = 1,
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
}; };
await _dbContext.ServiceInstances.AddAsync(instance); await db.ServiceInstances.AddAsync(instance);
// Add route // Add route
var routeId = instanceId + 1; var routeId = instanceId + 1;
@ -138,9 +146,9 @@ public class GatewayService : IGatewayService
IsGlobal = dto.IsGlobal, IsGlobal = dto.IsGlobal,
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
}; };
await _dbContext.TenantRoutes.AddAsync(route); await db.TenantRoutes.AddAsync(route);
await _dbContext.SaveChangesAsync(); await db.SaveChangesAsync();
_logger.LogInformation("Registered service {Service} at {Address}", dto.ServicePrefix, dto.ServiceAddress); _logger.LogInformation("Registered service {Service} at {Address}", dto.ServicePrefix, dto.ServiceAddress);
@ -149,7 +157,9 @@ public class GatewayService : IGatewayService
public async Task<bool> UnregisterServiceAsync(string serviceName, string? tenantCode = null) public async Task<bool> UnregisterServiceAsync(string serviceName, string? tenantCode = null)
{ {
var route = await _dbContext.TenantRoutes await using var db = await _dbContextFactory.CreateDbContextAsync();
var route = await db.TenantRoutes
.FirstOrDefaultAsync(r => .FirstOrDefaultAsync(r =>
r.ServiceName == serviceName && r.ServiceName == serviceName &&
r.IsDeleted == false && r.IsDeleted == false &&
@ -162,7 +172,7 @@ public class GatewayService : IGatewayService
route.UpdatedTime = DateTime.UtcNow; route.UpdatedTime = DateTime.UtcNow;
// Soft delete instances // Soft delete instances
var instances = await _dbContext.ServiceInstances var instances = await db.ServiceInstances
.Where(i => i.ClusterId == route.ClusterId && !i.IsDeleted) .Where(i => i.ClusterId == route.ClusterId && !i.IsDeleted)
.ToListAsync(); .ToListAsync();
@ -172,7 +182,7 @@ public class GatewayService : IGatewayService
instance.UpdatedTime = DateTime.UtcNow; instance.UpdatedTime = DateTime.UtcNow;
} }
await _dbContext.SaveChangesAsync(); await db.SaveChangesAsync();
_logger.LogInformation("Unregistered service {Service}", serviceName); _logger.LogInformation("Unregistered service {Service}", serviceName);
@ -181,7 +191,9 @@ public class GatewayService : IGatewayService
public async Task<List<GatewayRouteDto>> GetRoutesAsync(bool globalOnly = false) public async Task<List<GatewayRouteDto>> GetRoutesAsync(bool globalOnly = false)
{ {
var query = _dbContext.TenantRoutes.Where(r => !r.IsDeleted); await using var db = await _dbContextFactory.CreateDbContextAsync();
var query = db.TenantRoutes.Where(r => !r.IsDeleted);
if (globalOnly) if (globalOnly)
query = query.Where(r => r.IsGlobal); query = query.Where(r => r.IsGlobal);
@ -189,7 +201,7 @@ public class GatewayService : IGatewayService
var routes = await query.OrderByDescending(r => r.Priority).ToListAsync(); var routes = await query.OrderByDescending(r => r.Priority).ToListAsync();
var clusters = routes.Select(r => r.ClusterId).Distinct().ToList(); var clusters = routes.Select(r => r.ClusterId).Distinct().ToList();
var instances = await _dbContext.ServiceInstances var instances = await db.ServiceInstances
.Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted) .Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted)
.GroupBy(i => i.ClusterId) .GroupBy(i => i.ClusterId)
.ToDictionaryAsync(g => g.Key, g => g.Count()); .ToDictionaryAsync(g => g.Key, g => g.Count());
@ -210,7 +222,9 @@ public class GatewayService : IGatewayService
public async Task<GatewayRouteDto> CreateRouteAsync(CreateGatewayRouteDto dto) public async Task<GatewayRouteDto> CreateRouteAsync(CreateGatewayRouteDto dto)
{ {
var existing = await _dbContext.TenantRoutes await using var db = await _dbContextFactory.CreateDbContextAsync();
var existing = await db.TenantRoutes
.FirstOrDefaultAsync(r => .FirstOrDefaultAsync(r =>
r.ServiceName == dto.ServiceName && r.ServiceName == dto.ServiceName &&
r.IsGlobal == dto.IsGlobal && r.IsGlobal == dto.IsGlobal &&
@ -234,8 +248,8 @@ public class GatewayService : IGatewayService
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
}; };
await _dbContext.TenantRoutes.AddAsync(route); await db.TenantRoutes.AddAsync(route);
await _dbContext.SaveChangesAsync(); await db.SaveChangesAsync();
return new GatewayRouteDto return new GatewayRouteDto
{ {
@ -253,7 +267,9 @@ public class GatewayService : IGatewayService
public async Task<List<GatewayInstanceDto>> GetInstancesAsync(string clusterId) public async Task<List<GatewayInstanceDto>> GetInstancesAsync(string clusterId)
{ {
var instances = await _dbContext.ServiceInstances await using var db = await _dbContextFactory.CreateDbContextAsync();
var instances = await db.ServiceInstances
.Where(i => i.ClusterId == clusterId && !i.IsDeleted) .Where(i => i.ClusterId == clusterId && !i.IsDeleted)
.OrderByDescending(i => i.Weight) .OrderByDescending(i => i.Weight)
.ToListAsync(); .ToListAsync();
@ -273,7 +289,9 @@ public class GatewayService : IGatewayService
public async Task<GatewayInstanceDto> AddInstanceAsync(CreateGatewayInstanceDto dto) public async Task<GatewayInstanceDto> AddInstanceAsync(CreateGatewayInstanceDto dto)
{ {
var existing = await _dbContext.ServiceInstances await using var db = await _dbContextFactory.CreateDbContextAsync();
var existing = await db.ServiceInstances
.FirstOrDefaultAsync(i => .FirstOrDefaultAsync(i =>
i.ClusterId == dto.ClusterId && i.ClusterId == dto.ClusterId &&
i.DestinationId == dto.DestinationId && i.DestinationId == dto.DestinationId &&
@ -296,8 +314,8 @@ public class GatewayService : IGatewayService
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
}; };
await _dbContext.ServiceInstances.AddAsync(instance); await db.ServiceInstances.AddAsync(instance);
await _dbContext.SaveChangesAsync(); await db.SaveChangesAsync();
return new GatewayInstanceDto return new GatewayInstanceDto
{ {
@ -314,25 +332,29 @@ public class GatewayService : IGatewayService
public async Task<bool> RemoveInstanceAsync(long instanceId) public async Task<bool> RemoveInstanceAsync(long instanceId)
{ {
var instance = await _dbContext.ServiceInstances.FindAsync(instanceId); await using var db = await _dbContextFactory.CreateDbContextAsync();
var instance = await db.ServiceInstances.FindAsync(instanceId);
if (instance == null) return false; if (instance == null) return false;
instance.IsDeleted = true; instance.IsDeleted = true;
instance.UpdatedTime = DateTime.UtcNow; instance.UpdatedTime = DateTime.UtcNow;
await _dbContext.SaveChangesAsync(); await db.SaveChangesAsync();
return true; return true;
} }
public async Task<bool> UpdateInstanceWeightAsync(long instanceId, int weight) public async Task<bool> UpdateInstanceWeightAsync(long instanceId, int weight)
{ {
var instance = await _dbContext.ServiceInstances.FindAsync(instanceId); await using var db = await _dbContextFactory.CreateDbContextAsync();
var instance = await db.ServiceInstances.FindAsync(instanceId);
if (instance == null) return false; if (instance == null) return false;
instance.Weight = weight; instance.Weight = weight;
instance.UpdatedTime = DateTime.UtcNow; instance.UpdatedTime = DateTime.UtcNow;
await _dbContext.SaveChangesAsync(); await db.SaveChangesAsync();
return true; return true;
} }

View File

@ -1,55 +0,0 @@
using Fengling.Platform.Infrastructure;
using Microsoft.Extensions.Configuration;
using QRCoder;
namespace Fengling.Console.Services;
public interface IH5LinkService
{
Task<H5LinkResult> GenerateH5LinkAsync(int tenantId);
}
public class H5LinkResult
{
public string Link { get; set; } = string.Empty;
public string QrCodeBase64 { get; set; } = string.Empty;
}
public class H5LinkService : IH5LinkService
{
private readonly ITenantManager _tenantManager;
private readonly IConfiguration _configuration;
public H5LinkService(ITenantManager tenantManager, IConfiguration configuration)
{
_tenantManager = tenantManager;
_configuration = configuration;
}
public async Task<H5LinkResult> GenerateH5LinkAsync(int tenantId)
{
var tenant = await _tenantManager.FindByIdAsync(tenantId)
?? throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
var link = GenerateLink(tenant);
var qrCodeBase64 = GenerateQrCode(link);
return new H5LinkResult
{
Link = link,
QrCodeBase64 = qrCodeBase64
};
}
private string GenerateLink(Fengling.Platform.Domain.AggregatesModel.TenantAggregate.Tenant tenant)
{
var baseUrl = _configuration["AppSettings:DefaultH5BaseUrl"] ?? "https://h5.example.com";
return $"{baseUrl.TrimEnd('/')}/{tenant.TenantCode}";
}
private string GenerateQrCode(string content)
{
byte[] qrCodeBytes = PngByteQRCodeHelper.GetQRCode(content, QRCodeGenerator.ECCLevel.M, 20);
return Convert.ToBase64String(qrCodeBytes);
}
}

View File

@ -45,10 +45,10 @@ public class OAuthClientService : IOAuthClientService
var clientIdValue = await _applicationManager.GetClientIdAsync(application); var clientIdValue = await _applicationManager.GetClientIdAsync(application);
var displayNameValue = await _applicationManager.GetDisplayNameAsync(application); var displayNameValue = await _applicationManager.GetDisplayNameAsync(application);
if (!string.IsNullOrEmpty(displayName) && (string.IsNullOrEmpty(displayNameValue) || !displayNameValue.Contains(displayName, StringComparison.OrdinalIgnoreCase))) if (!string.IsNullOrEmpty(displayName) && !displayNameValue.Contains(displayName, StringComparison.OrdinalIgnoreCase))
continue; continue;
if (!string.IsNullOrEmpty(clientId) && (string.IsNullOrEmpty(clientIdValue) || !clientIdValue.Contains(clientId, StringComparison.OrdinalIgnoreCase))) if (!string.IsNullOrEmpty(clientId) && !clientIdValue.Contains(clientId, StringComparison.OrdinalIgnoreCase))
continue; continue;
var clientType = await _applicationManager.GetClientTypeAsync(application); var clientType = await _applicationManager.GetClientTypeAsync(application);

View File

@ -1,10 +1,9 @@
using Fengling.Console.Models.Dtos; using Fengling.Console.Models.Dtos;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate; using Fengling.Console.Repositories;
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims; using System.Security.Claims;
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Services; namespace Fengling.Console.Services;
@ -24,20 +23,20 @@ public interface IRoleService
public class RoleService : IRoleService public class RoleService : IRoleService
{ {
private readonly ITenantManager _tenantManager; private readonly IRoleRepository _repository;
private readonly UserManager<ApplicationUser> _userManager; private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager; private readonly RoleManager<ApplicationRole> _roleManager;
private readonly PlatformDbContext _context; private readonly ApplicationDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
public RoleService( public RoleService(
ITenantManager tenantManager, IRoleRepository repository,
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager, RoleManager<ApplicationRole> roleManager,
PlatformDbContext context, ApplicationDbContext context,
IHttpContextAccessor httpContextAccessor) IHttpContextAccessor httpContextAccessor)
{ {
_tenantManager = tenantManager; _repository = repository;
_userManager = userManager; _userManager = userManager;
_roleManager = roleManager; _roleManager = roleManager;
_context = context; _context = context;
@ -47,24 +46,8 @@ public class RoleService : IRoleService
public async Task<(IEnumerable<RoleDto> Items, int TotalCount)> GetRolesAsync(int page, int pageSize, public async Task<(IEnumerable<RoleDto> Items, int TotalCount)> GetRolesAsync(int page, int pageSize,
string? name = null, string? tenantId = null) string? name = null, string? tenantId = null)
{ {
var query = _context.Roles.AsQueryable(); var roles = await _repository.GetPagedAsync(page, pageSize, name, tenantId);
var totalCount = await _repository.CountAsync(name, tenantId);
if (!string.IsNullOrEmpty(name))
{
query = query.Where(r => r.Name != null && r.Name.Contains(name));
}
if (!string.IsNullOrEmpty(tenantId))
{
query = query.Where(r => r.TenantId.HasValue && r.TenantId.Value.ToString().Contains(tenantId));
}
var totalCount = await query.CountAsync();
var roles = await query
.OrderByDescending(r => r.CreatedTime)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
var roleDtos = new List<RoleDto>(); var roleDtos = new List<RoleDto>();
foreach (var role in roles) foreach (var role in roles)
@ -89,7 +72,7 @@ public class RoleService : IRoleService
public async Task<RoleDto?> GetRoleAsync(long id) public async Task<RoleDto?> GetRoleAsync(long id)
{ {
var role = await _context.Roles.FindAsync(id); var role = await _repository.GetByIdAsync(id);
if (role == null) return null; if (role == null) return null;
return new RoleDto return new RoleDto
@ -108,7 +91,7 @@ public class RoleService : IRoleService
public async Task<IEnumerable<UserDto>> GetRoleUsersAsync(long id) public async Task<IEnumerable<UserDto>> GetRoleUsersAsync(long id)
{ {
var role = await _context.Roles.FindAsync(id); var role = await _repository.GetByIdAsync(id);
if (role == null) if (role == null)
{ {
throw new KeyNotFoundException($"Role with ID {id} not found"); throw new KeyNotFoundException($"Role with ID {id} not found");
@ -120,15 +103,14 @@ public class RoleService : IRoleService
foreach (var user in users) foreach (var user in users)
{ {
var roles = await _userManager.GetRolesAsync(user); var roles = await _userManager.GetRolesAsync(user);
var tenant = user.TenantInfo?.TenantId > 0 ? await _tenantManager.FindByIdAsync(user.TenantInfo.TenantId) : null;
userDtos.Add(new UserDto userDtos.Add(new UserDto
{ {
Id = user.Id, Id = user.Id,
UserName = user.UserName, UserName = user.UserName,
Email = user.Email, Email = user.Email,
RealName = user.RealName, RealName = user.RealName,
TenantCode = user.TenantInfo?.TenantCode, TenantId = user.TenantInfo.Id,
TenantName = tenant?.Name ?? "", TenantName = user.TenantInfo.Name,
Roles = roles.ToList(), Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed, EmailConfirmed = user.EmailConfirmed,
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -146,7 +128,7 @@ public class RoleService : IRoleService
Name = dto.Name, Name = dto.Name,
DisplayName = dto.DisplayName, DisplayName = dto.DisplayName,
Description = dto.Description, Description = dto.Description,
TenantId = dto.TenantId.HasValue ? dto.TenantId.Value : null, TenantId = dto.TenantId,
Permissions = dto.Permissions, Permissions = dto.Permissions,
IsSystem = false, IsSystem = false,
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
@ -177,7 +159,7 @@ public class RoleService : IRoleService
public async Task<RoleDto> UpdateRoleAsync(long id, UpdateRoleDto dto) public async Task<RoleDto> UpdateRoleAsync(long id, UpdateRoleDto dto)
{ {
var role = await _context.Roles.FindAsync(id); var role = await _repository.GetByIdAsync(id);
if (role == null) if (role == null)
{ {
throw new KeyNotFoundException($"Role with ID {id} not found"); throw new KeyNotFoundException($"Role with ID {id} not found");
@ -194,7 +176,7 @@ public class RoleService : IRoleService
role.Description = dto.Description; role.Description = dto.Description;
role.Permissions = dto.Permissions; role.Permissions = dto.Permissions;
await _roleManager.UpdateAsync(role); await _repository.UpdateAsync(role);
await CreateAuditLog("role", "update", "Role", role.Id, role.DisplayName, oldValue, await CreateAuditLog("role", "update", "Role", role.Id, role.DisplayName, oldValue,
System.Text.Json.JsonSerializer.Serialize(role)); System.Text.Json.JsonSerializer.Serialize(role));
@ -216,7 +198,7 @@ public class RoleService : IRoleService
public async Task DeleteRoleAsync(long id) public async Task DeleteRoleAsync(long id)
{ {
var role = await _context.Roles.FindAsync(id); var role = await _repository.GetByIdAsync(id);
if (role == null) if (role == null)
{ {
throw new KeyNotFoundException($"Role with ID {id} not found"); throw new KeyNotFoundException($"Role with ID {id} not found");
@ -235,14 +217,14 @@ public class RoleService : IRoleService
await _userManager.RemoveFromRoleAsync(user, role.Name!); await _userManager.RemoveFromRoleAsync(user, role.Name!);
} }
await _roleManager.DeleteAsync(role); await _repository.DeleteAsync(role);
await CreateAuditLog("role", "delete", "Role", role.Id, role.DisplayName, oldValue); await CreateAuditLog("role", "delete", "Role", role.Id, role.DisplayName, oldValue);
} }
public async Task AddUserToRoleAsync(long roleId, long userId) public async Task AddUserToRoleAsync(long roleId, long userId)
{ {
var role = await _context.Roles.FindAsync(roleId); var role = await _repository.GetByIdAsync(roleId);
if (role == null) if (role == null)
{ {
throw new KeyNotFoundException($"Role with ID {roleId} not found"); throw new KeyNotFoundException($"Role with ID {roleId} not found");
@ -265,7 +247,7 @@ public class RoleService : IRoleService
public async Task RemoveUserFromRoleAsync(long roleId, long userId) public async Task RemoveUserFromRoleAsync(long roleId, long userId)
{ {
var role = await _context.Roles.FindAsync(roleId); var role = await _repository.GetByIdAsync(roleId);
if (role == null) if (role == null)
{ {
throw new KeyNotFoundException($"Role with ID {roleId} not found"); throw new KeyNotFoundException($"Role with ID {roleId} not found");

View File

@ -1,19 +1,15 @@
using Fengling.Console.Models.Dtos; using Fengling.Console.Models.Dtos;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate; using Fengling.Console.Repositories;
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims; using System.Security.Claims;
using TenantStatus = Fengling.Platform.Domain.AggregatesModel.TenantAggregate.TenantStatus; using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Services; namespace Fengling.Console.Services;
public interface ITenantService public interface ITenantService
{ {
Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync(int page, int pageSize, string? name = null, Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync(int page, int pageSize, string? name = null, string? tenantId = null, string? status = null);
string? tenantCode = null, TenantStatus? status = null);
Task<TenantDto?> GetTenantAsync(long id); Task<TenantDto?> GetTenantAsync(long id);
Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId); Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId);
Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId); Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId);
@ -25,27 +21,27 @@ public interface ITenantService
} }
public class TenantService( public class TenantService(
ITenantManager tenantManager, ITenantRepository repository,
IUserRepository userRepository,
IRoleRepository roleRepository,
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager, ApplicationDbContext context,
PlatformDbContext context,
IHttpContextAccessor httpContextAccessor) IHttpContextAccessor httpContextAccessor)
: ITenantService : ITenantService
{ {
public async Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync public async Task<(IEnumerable<TenantDto> Items, int TotalCount)> GetTenantsAsync(int page, int pageSize, string? name = null, string? tenantId = null, string? status = null)
(int page, int pageSize, string? name = null, string? tenantCode = null, TenantStatus? status = null)
{ {
var tenants = await tenantManager.GetPagedAsync(page, pageSize, name, tenantCode, status); var tenants = await repository.GetPagedAsync(page, pageSize, name, tenantId, status);
var totalCount = await tenantManager.GetCountAsync(name, tenantCode, status); var totalCount = await repository.CountAsync(name, tenantId, status);
var tenantDtos = new List<TenantDto>(); var tenantDtos = new List<TenantDto>();
foreach (var tenant in tenants) foreach (var tenant in tenants)
{ {
var userCount = await tenantManager.GetUserCountAsync(tenant.Id); var userCount = await repository.GetUserCountAsync(tenant.Id);
tenantDtos.Add(new TenantDto tenantDtos.Add(new TenantDto
{ {
Id = tenant.Id, Id = tenant.Id,
TenantCode = tenant.TenantCode, TenantId = tenant.TenantId,
Name = tenant.Name, Name = tenant.Name,
ContactName = tenant.ContactName, ContactName = tenant.ContactName,
ContactEmail = tenant.ContactEmail, ContactEmail = tenant.ContactEmail,
@ -64,19 +60,19 @@ public class TenantService(
public async Task<TenantDto?> GetTenantAsync(long id) public async Task<TenantDto?> GetTenantAsync(long id)
{ {
var tenant = await tenantManager.FindByIdAsync(id); var tenant = await repository.GetByIdAsync(id);
if (tenant == null) return null; if (tenant == null) return null;
return new TenantDto return new TenantDto
{ {
Id = tenant.Id, Id = tenant.Id,
TenantCode = tenant.TenantCode, TenantId = tenant.TenantId,
Name = tenant.Name, Name = tenant.Name,
ContactName = tenant.ContactName, ContactName = tenant.ContactName,
ContactEmail = tenant.ContactEmail, ContactEmail = tenant.ContactEmail,
ContactPhone = tenant.ContactPhone, ContactPhone = tenant.ContactPhone,
MaxUsers = tenant.MaxUsers, MaxUsers = tenant.MaxUsers,
UserCount = await tenantManager.GetUserCountAsync(tenant.Id), UserCount = await repository.GetUserCountAsync(tenant.Id),
Status = tenant.Status, Status = tenant.Status,
ExpiresAt = tenant.ExpiresAt, ExpiresAt = tenant.ExpiresAt,
Description = tenant.Description, Description = tenant.Description,
@ -86,16 +82,13 @@ public class TenantService(
public async Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId) public async Task<IEnumerable<UserDto>> GetTenantUsersAsync(long tenantId)
{ {
var tenant = await tenantManager.FindByIdAsync(tenantId); var tenant = await repository.GetByIdAsync(tenantId);
if (tenant == null) if (tenant == null)
{ {
throw new KeyNotFoundException($"Tenant with ID {tenantId} not found"); throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
} }
var users = await context.Users var users = await userRepository.GetPagedAsync(1, int.MaxValue, null, null, tenantId.ToString());
.Where(u => u.TenantInfo!.TenantId == tenantId)
.ToListAsync();
var userDtos = new List<UserDto>(); var userDtos = new List<UserDto>();
foreach (var user in users) foreach (var user in users)
@ -107,8 +100,8 @@ public class TenantService(
UserName = user.UserName, UserName = user.UserName,
Email = user.Email, Email = user.Email,
RealName = user.RealName, RealName = user.RealName,
TenantCode = user.TenantInfo.TenantId.ToString(), TenantId = user.TenantInfo.Id,
TenantName = tenant?.Name ?? "", TenantName = user.TenantInfo.Name,
Roles = roles.ToList(), Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed, EmailConfirmed = user.EmailConfirmed,
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -121,16 +114,13 @@ public class TenantService(
public async Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId) public async Task<IEnumerable<object>> GetTenantRolesAsync(long tenantId)
{ {
var tenant = await tenantManager.FindByIdAsync(tenantId); var tenant = await repository.GetByIdAsync(tenantId);
if (tenant == null) if (tenant == null)
{ {
throw new KeyNotFoundException($"Tenant with ID {tenantId} not found"); throw new KeyNotFoundException($"Tenant with ID {tenantId} not found");
} }
var roles = await context.Roles var roles = await roleRepository.GetPagedAsync(1, int.MaxValue, null, tenantId.ToString());
.Where(r => r.TenantId == tenantId)
.ToListAsync();
return roles.Select(r => new return roles.Select(r => new
{ {
id = r.Id, id = r.Id,
@ -141,7 +131,7 @@ public class TenantService(
public async Task<TenantSettingsDto> GetTenantSettingsAsync(long id) public async Task<TenantSettingsDto> GetTenantSettingsAsync(long id)
{ {
var tenant = await tenantManager.FindByIdAsync(id); var tenant = await repository.GetByIdAsync(id);
if (tenant == null) if (tenant == null)
{ {
throw new KeyNotFoundException($"Tenant with ID {id} not found"); throw new KeyNotFoundException($"Tenant with ID {id} not found");
@ -160,46 +150,39 @@ public class TenantService(
public async Task UpdateTenantSettingsAsync(long id, TenantSettingsDto settings) public async Task UpdateTenantSettingsAsync(long id, TenantSettingsDto settings)
{ {
var tenant = await tenantManager.FindByIdAsync(id); var tenant = await repository.GetByIdAsync(id);
if (tenant == null) if (tenant == null)
{ {
throw new KeyNotFoundException($"Tenant with ID {id} not found"); throw new KeyNotFoundException($"Tenant with ID {id} not found");
} }
await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.Name, null, System.Text.Json.JsonSerializer.Serialize(settings)); await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.TenantId, null, System.Text.Json.JsonSerializer.Serialize(settings));
} }
public async Task<TenantDto> CreateTenantAsync(CreateTenantDto dto) public async Task<TenantDto> CreateTenantAsync(CreateTenantDto dto)
{ {
var tenant = new Tenant var tenant = new Tenant
{ {
TenantCode = dto.TenantCode, TenantId = dto.TenantId,
Name = dto.Name, Name = dto.Name,
ContactName = dto.ContactName, ContactName = dto.ContactName,
ContactEmail = dto.ContactEmail, ContactEmail = dto.ContactEmail,
ContactPhone = dto.ContactPhone, ContactPhone = dto.ContactPhone,
MaxUsers = dto.MaxUsers, MaxUsers = dto.MaxUsers,
Description = dto.Description, Description = dto.Description,
Status = dto.Status,
ExpiresAt = dto.ExpiresAt, ExpiresAt = dto.ExpiresAt,
Status = TenantStatus.Active,
CreatedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}; };
var result = await tenantManager.CreateAsync(tenant); await repository.AddAsync(tenant);
if (!result.Succeeded) await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantId, null, System.Text.Json.JsonSerializer.Serialize(dto));
{
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
throw new InvalidOperationException($"Failed to create tenant: {errors}");
}
await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.Name, null,
System.Text.Json.JsonSerializer.Serialize(dto));
return new TenantDto return new TenantDto
{ {
Id = tenant.Id, Id = tenant.Id,
TenantCode = tenant.TenantCode, TenantId = tenant.TenantId,
Name = tenant.Name, Name = tenant.Name,
ContactName = tenant.ContactName, ContactName = tenant.ContactName,
ContactEmail = tenant.ContactEmail, ContactEmail = tenant.ContactEmail,
@ -215,7 +198,7 @@ public class TenantService(
public async Task<TenantDto> UpdateTenantAsync(long id, UpdateTenantDto dto) public async Task<TenantDto> UpdateTenantAsync(long id, UpdateTenantDto dto)
{ {
var tenant = await tenantManager.FindByIdAsync(id); var tenant = await repository.GetByIdAsync(id);
if (tenant == null) if (tenant == null)
{ {
throw new KeyNotFoundException($"Tenant with ID {id} not found"); throw new KeyNotFoundException($"Tenant with ID {id} not found");
@ -227,29 +210,26 @@ public class TenantService(
tenant.ContactName = dto.ContactName; tenant.ContactName = dto.ContactName;
tenant.ContactEmail = dto.ContactEmail; tenant.ContactEmail = dto.ContactEmail;
tenant.ContactPhone = dto.ContactPhone; tenant.ContactPhone = dto.ContactPhone;
tenant.MaxUsers = dto.MaxUsers;
tenant.Description = dto.Description;
tenant.Status = dto.Status;
tenant.ExpiresAt = dto.ExpiresAt;
tenant.UpdatedAt = DateTime.UtcNow; tenant.UpdatedAt = DateTime.UtcNow;
var result = await tenantManager.UpdateAsync(tenant); await repository.UpdateAsync(tenant);
if (!result.Succeeded) await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.TenantId, oldValue, System.Text.Json.JsonSerializer.Serialize(tenant));
{
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
throw new InvalidOperationException($"Failed to update tenant: {errors}");
}
await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.Name, oldValue,
System.Text.Json.JsonSerializer.Serialize(tenant));
return new TenantDto return new TenantDto
{ {
Id = tenant.Id, Id = tenant.Id,
TenantCode = tenant.TenantCode, TenantId = tenant.TenantId,
Name = tenant.Name, Name = tenant.Name,
ContactName = tenant.ContactName, ContactName = tenant.ContactName,
ContactEmail = tenant.ContactEmail, ContactEmail = tenant.ContactEmail,
ContactPhone = tenant.ContactPhone, ContactPhone = tenant.ContactPhone,
MaxUsers = tenant.MaxUsers, MaxUsers = tenant.MaxUsers,
UserCount = await tenantManager.GetUserCountAsync(tenant.Id), UserCount = await repository.GetUserCountAsync(tenant.Id),
Status = tenant.Status, Status = tenant.Status,
ExpiresAt = tenant.ExpiresAt, ExpiresAt = tenant.ExpiresAt,
Description = tenant.Description, Description = tenant.Description,
@ -259,7 +239,7 @@ public class TenantService(
public async Task DeleteTenantAsync(long id) public async Task DeleteTenantAsync(long id)
{ {
var tenant = await tenantManager.FindByIdAsync(id); var tenant = await repository.GetByIdAsync(id);
if (tenant == null) if (tenant == null)
{ {
throw new KeyNotFoundException($"Tenant with ID {id} not found"); throw new KeyNotFoundException($"Tenant with ID {id} not found");
@ -267,39 +247,30 @@ public class TenantService(
var oldValue = System.Text.Json.JsonSerializer.Serialize(tenant); var oldValue = System.Text.Json.JsonSerializer.Serialize(tenant);
var users = await context.Users var users = await userRepository.GetPagedAsync(1, int.MaxValue, null, null, id.ToString());
.Where(u => u.TenantInfo.TenantId == id)
.ToListAsync();
foreach (var user in users) foreach (var user in users)
{ {
user.IsDeleted = true; user.IsDeleted = true;
user.UpdatedTime = DateTime.UtcNow; user.UpdatedTime = DateTime.UtcNow;
await context.SaveChangesAsync();
} }
await context.SaveChangesAsync();
tenant.IsDeleted = true; tenant.IsDeleted = true;
var result = await tenantManager.UpdateAsync(tenant); await repository.UpdateAsync(tenant);
if (!result.Succeeded) await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.TenantId, oldValue);
{
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
throw new InvalidOperationException($"Failed to delete tenant: {errors}");
}
await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.Name, oldValue);
} }
private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null) private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
{ {
var httpContext = httpContextAccessor.HttpContext; var httpContext = httpContextAccessor.HttpContext;
var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? httpContext?.User?.Identity?.Name ?? "system"; var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? httpContext?.User?.Identity?.Name ?? "system";
var tenantIdClaim = httpContext?.User?.FindFirstValue("TenantId"); var tenantId = httpContext?.User?.FindFirstValue("TenantId");
var log = new AuditLog var log = new AuditLog
{ {
Operator = userName, Operator = userName,
TenantId = tenantIdClaim, TenantId = tenantId,
Operation = operation, Operation = operation,
Action = action, Action = action,
TargetType = targetType, TargetType = targetType,

View File

@ -1,19 +1,15 @@
using Fengling.Console.Models.Dtos; using Fengling.Console.Models.Dtos;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate; using Fengling.Console.Repositories;
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims; using System.Security.Claims;
using Fengling.Console.Datas;
using Fengling.Console.Models.Entities;
namespace Fengling.Console.Services; namespace Fengling.Console.Services;
public interface IUserService public interface IUserService
{ {
Task<(IEnumerable<UserDto> Items, int TotalCount)> GetUsersAsync(int page, int pageSize, string? userName = null, Task<(IEnumerable<UserDto> Items, int TotalCount)> GetUsersAsync(int page, int pageSize, string? userName = null, string? email = null, string? tenantId = null);
string? email = null, string? tenantCode = null);
Task<UserDto?> GetUserAsync(long id); Task<UserDto?> GetUserAsync(long id);
Task<UserDto> CreateUserAsync(CreateUserDto dto); Task<UserDto> CreateUserAsync(CreateUserDto dto);
Task<UserDto> UpdateUserAsync(long id, UpdateUserDto dto); Task<UserDto> UpdateUserAsync(long id, UpdateUserDto dto);
@ -22,59 +18,32 @@ public interface IUserService
} }
public class UserService( public class UserService(
ITenantManager tenantManager, IUserRepository repository,
ITenantRepository tenantRepository,
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager, RoleManager<ApplicationRole> roleManager,
PlatformDbContext context, ApplicationDbContext context,
IHttpContextAccessor httpContextAccessor) IHttpContextAccessor httpContextAccessor)
: IUserService : IUserService
{ {
public async Task<(IEnumerable<UserDto> Items, int TotalCount)> GetUsersAsync(int page, int pageSize, public async Task<(IEnumerable<UserDto> Items, int TotalCount)> GetUsersAsync(int page, int pageSize, string? userName = null, string? email = null, string? tenantId = null)
string? userName = null, string? email = null,
string? tenantCode = null)
{ {
var query = context.Users.AsQueryable(); var users = await repository.GetPagedAsync(page, pageSize, userName, email, tenantId);
var totalCount = await repository.CountAsync(userName, email, tenantId);
if (!string.IsNullOrEmpty(userName))
{
query = query.Where(u => u.UserName != null && u.UserName.Contains(userName));
}
if (!string.IsNullOrEmpty(email))
{
query = query.Where(u => u.Email != null && u.Email.Contains(email));
}
if (!string.IsNullOrEmpty(tenantCode))
{
query = query.Where(u => u.TenantInfo != null &&
u.TenantInfo.TenantCode != null &&
u.TenantInfo.TenantCode.Contains(tenantCode));
}
var totalCount = await query.CountAsync();
var users = await query
.OrderByDescending(u => u.CreatedTime)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
var userDtos = new List<UserDto>(); var userDtos = new List<UserDto>();
foreach (var user in users) foreach (var user in users)
{ {
var roles = await userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user);
var tenant = user.TenantInfo?.TenantId > 0
? await tenantManager.FindByIdAsync(user.TenantInfo.TenantId)
: null;
userDtos.Add(new UserDto userDtos.Add(new UserDto
{ {
Id = user.Id, Id = user.Id,
UserName = user.UserName, UserName = user.UserName,
Email = user.Email, Email = user.Email,
RealName = user.RealName, RealName = user.RealName,
PhoneNumber = user.PhoneNumber, Phone = user.Phone,
TenantCode = user.TenantInfo?.TenantCode, TenantId = user.TenantInfo.Id,
TenantName = tenant?.Name ?? "", TenantName = user.TenantInfo.Name,
Roles = roles.ToList(), Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed, EmailConfirmed = user.EmailConfirmed,
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -87,20 +56,19 @@ public class UserService(
public async Task<UserDto?> GetUserAsync(long id) public async Task<UserDto?> GetUserAsync(long id)
{ {
var user = await context.Users.FindAsync(id); var user = await repository.GetByIdAsync(id);
if (user == null) return null; if (user == null) return null;
var roles = await userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user);
var tenant = user.TenantInfo?.TenantId > 0 ? await tenantManager.FindByIdAsync(user.TenantInfo.TenantId) : null;
return new UserDto return new UserDto
{ {
Id = user.Id, Id = user.Id,
UserName = user.UserName, UserName = user.UserName,
Email = user.Email, Email = user.Email,
RealName = user.RealName, RealName = user.RealName,
PhoneNumber = user.PhoneNumber, Phone = user.Phone,
TenantCode = user.TenantInfo?.TenantCode, TenantId = user.TenantInfo.Id,
TenantName = tenant?.Name ?? "", TenantName = user.TenantInfo.Name,
Roles = roles.ToList(), Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed, EmailConfirmed = user.EmailConfirmed,
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -110,28 +78,25 @@ public class UserService(
public async Task<UserDto> CreateUserAsync(CreateUserDto dto) public async Task<UserDto> CreateUserAsync(CreateUserDto dto)
{ {
var tenantId = dto.TenantId ?? 0;
Tenant? tenant = null; Tenant? tenant = null;
if (dto.TenantId.HasValue && dto.TenantId.Value > 0) if (tenantId != 0)
{ {
tenant = await tenantManager.FindByIdAsync(dto.TenantId.Value); tenant = await tenantRepository.GetByIdAsync(tenantId);
if (tenant == null) if (tenant == null)
{ {
throw new InvalidOperationException("Invalid tenant ID"); throw new InvalidOperationException("Invalid tenant ID");
} }
} }
var tenantInfo = tenant != null
? new TenantInfo(tenant.Id, tenant.TenantCode, tenant.Name)
: new TenantInfo(0, "", "");
var user = new ApplicationUser var user = new ApplicationUser
{ {
UserName = dto.UserName, UserName = dto.UserName,
Email = dto.Email, Email = dto.Email,
RealName = dto.RealName, RealName = dto.RealName,
PhoneNumber = dto.PhoneNumber, Phone = dto.Phone,
TenantInfo = tenantInfo, TenantInfo = new TenantInfo(tenantId, tenant?.TenantId ?? "default", tenant?.Name ?? "默认租户"),
EmailConfirmed = dto.EmailConfirmed, EmailConfirmed = dto.EmailConfirmed,
CreatedTime = DateTime.UtcNow CreatedTime = DateTime.UtcNow
}; };
@ -142,7 +107,7 @@ public class UserService(
throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description))); throw new InvalidOperationException(string.Join(", ", result.Errors.Select(e => e.Description)));
} }
if (dto.RoleIds.Count != 0) if (dto.RoleIds != null && dto.RoleIds.Any())
{ {
foreach (var roleId in dto.RoleIds) foreach (var roleId in dto.RoleIds)
{ {
@ -160,8 +125,7 @@ public class UserService(
await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
} }
await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, System.Text.Json.JsonSerializer.Serialize(dto));
System.Text.Json.JsonSerializer.Serialize(dto));
var roles = await userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user);
return new UserDto return new UserDto
@ -170,9 +134,9 @@ public class UserService(
UserName = user.UserName, UserName = user.UserName,
Email = user.Email, Email = user.Email,
RealName = user.RealName, RealName = user.RealName,
PhoneNumber = user.PhoneNumber, Phone = user.Phone,
TenantCode = user.TenantInfo.TenantId.ToString(), TenantId = user.TenantInfo.Id,
TenantName = tenant?.Name ?? "", TenantName = user.TenantInfo.Name,
Roles = roles.ToList(), Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed, EmailConfirmed = user.EmailConfirmed,
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -182,7 +146,7 @@ public class UserService(
public async Task<UserDto> UpdateUserAsync(long id, UpdateUserDto dto) public async Task<UserDto> UpdateUserAsync(long id, UpdateUserDto dto)
{ {
var user = await context.Users.FindAsync(id); var user = await repository.GetByIdAsync(id);
if (user == null) if (user == null)
{ {
throw new KeyNotFoundException($"User with ID {id} not found"); throw new KeyNotFoundException($"User with ID {id} not found");
@ -192,12 +156,10 @@ public class UserService(
user.Email = dto.Email; user.Email = dto.Email;
user.RealName = dto.RealName; user.RealName = dto.RealName;
user.PhoneNumber = dto.PhoneNumber; user.Phone = dto.Phone;
user.EmailConfirmed = dto.EmailConfirmed; user.EmailConfirmed = dto.EmailConfirmed;
user.UpdatedTime = DateTime.UtcNow; user.UpdatedTime = DateTime.UtcNow;
await userManager.UpdateAsync(user);
if (dto.IsActive) if (dto.IsActive)
{ {
await userManager.SetLockoutEnabledAsync(user, false); await userManager.SetLockoutEnabledAsync(user, false);
@ -209,10 +171,9 @@ public class UserService(
await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue); await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
} }
var tenant = user.TenantInfo?.TenantId > 0 ? await tenantManager.FindByIdAsync(user.TenantInfo.TenantId) : null; await context.SaveChangesAsync();
await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, System.Text.Json.JsonSerializer.Serialize(user));
System.Text.Json.JsonSerializer.Serialize(user));
var roles = await userManager.GetRolesAsync(user); var roles = await userManager.GetRolesAsync(user);
return new UserDto return new UserDto
@ -221,9 +182,9 @@ public class UserService(
UserName = user.UserName, UserName = user.UserName,
Email = user.Email, Email = user.Email,
RealName = user.RealName, RealName = user.RealName,
PhoneNumber = user.PhoneNumber, Phone = user.Phone,
TenantCode = user.TenantInfo?.TenantCode, TenantId = user.TenantInfo.Id,
TenantName = tenant?.Name ?? "", TenantName = user.TenantInfo.Name,
Roles = roles.ToList(), Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed, EmailConfirmed = user.EmailConfirmed,
IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow, IsActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
@ -252,7 +213,7 @@ public class UserService(
public async Task DeleteUserAsync(long id) public async Task DeleteUserAsync(long id)
{ {
var user = await context.Users.FindAsync(id); var user = await repository.GetByIdAsync(id);
if (user == null) if (user == null)
{ {
throw new KeyNotFoundException($"User with ID {id} not found"); throw new KeyNotFoundException($"User with ID {id} not found");
@ -266,18 +227,16 @@ public class UserService(
await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue); await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue);
} }
private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
string? targetName, string? oldValue = null, string? newValue = null)
{ {
var httpContext = httpContextAccessor.HttpContext; var httpContext = httpContextAccessor.HttpContext;
var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? var userName = httpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? httpContext?.User?.Identity?.Name ?? "system";
httpContext?.User?.Identity?.Name ?? "system"; var tenantId = httpContext?.User?.FindFirstValue("TenantId");
var tenantIdClaim = httpContext?.User?.FindFirstValue("TenantId");
var log = new AuditLog var log = new AuditLog
{ {
Operator = userName, Operator = userName,
TenantId = tenantIdClaim, TenantId = tenantId,
Operation = operation, Operation = operation,
Action = action, Action = action,
TargetType = targetType, TargetType = targetType,

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Utilities.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Tasks.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.IO.Redist" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.1" newVersion="6.0.0.1" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -1,260 +0,0 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost/4.14.0-3.25262.10": {
"dependencies": {
"Microsoft.Build.Locator": "1.6.10",
"Microsoft.CodeAnalysis.NetAnalyzers": "8.0.0-preview.23468.1",
"Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers": "3.3.4-beta1.22504.1",
"Microsoft.DotNet.XliffTasks": "9.0.0-beta.25255.5",
"Microsoft.VisualStudio.Threading.Analyzers": "17.13.2",
"Newtonsoft.Json": "13.0.3",
"Roslyn.Diagnostics.Analyzers": "3.11.0-beta1.24081.1",
"System.Collections.Immutable": "9.0.0",
"System.CommandLine": "2.0.0-beta4.24528.1"
},
"runtime": {
"Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll": {}
},
"resources": {
"cs/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "cs"
},
"de/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "de"
},
"es/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "es"
},
"fr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "fr"
},
"it/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "it"
},
"ja/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "ja"
},
"ko/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "ko"
},
"pl/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "pl"
},
"pt-BR/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "pt-BR"
},
"ru/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "ru"
},
"tr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "tr"
},
"zh-Hans/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "zh-Hans"
},
"zh-Hant/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.Build.Locator/1.6.10": {
"runtime": {
"lib/net6.0/Microsoft.Build.Locator.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.6.10.57384"
}
}
},
"Microsoft.CodeAnalysis.BannedApiAnalyzers/3.11.0-beta1.24081.1": {},
"Microsoft.CodeAnalysis.NetAnalyzers/8.0.0-preview.23468.1": {},
"Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers/3.3.4-beta1.22504.1": {},
"Microsoft.CodeAnalysis.PublicApiAnalyzers/3.11.0-beta1.24081.1": {},
"Microsoft.DotNet.XliffTasks/9.0.0-beta.25255.5": {},
"Microsoft.VisualStudio.Threading.Analyzers/17.13.2": {},
"Newtonsoft.Json/13.0.3": {
"runtime": {
"lib/net6.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.3.27908"
}
}
},
"Roslyn.Diagnostics.Analyzers/3.11.0-beta1.24081.1": {
"dependencies": {
"Microsoft.CodeAnalysis.BannedApiAnalyzers": "3.11.0-beta1.24081.1",
"Microsoft.CodeAnalysis.PublicApiAnalyzers": "3.11.0-beta1.24081.1"
}
},
"System.Collections.Immutable/9.0.0": {
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/netstandard2.0/System.Collections.Immutable.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52809"
}
}
},
"System.CommandLine/2.0.0-beta4.24528.1": {
"dependencies": {
"System.Memory": "4.5.5"
},
"runtime": {
"lib/netstandard2.0/System.CommandLine.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.0.24.52801"
}
},
"resources": {
"lib/netstandard2.0/cs/System.CommandLine.resources.dll": {
"locale": "cs"
},
"lib/netstandard2.0/de/System.CommandLine.resources.dll": {
"locale": "de"
},
"lib/netstandard2.0/es/System.CommandLine.resources.dll": {
"locale": "es"
},
"lib/netstandard2.0/fr/System.CommandLine.resources.dll": {
"locale": "fr"
},
"lib/netstandard2.0/it/System.CommandLine.resources.dll": {
"locale": "it"
},
"lib/netstandard2.0/ja/System.CommandLine.resources.dll": {
"locale": "ja"
},
"lib/netstandard2.0/ko/System.CommandLine.resources.dll": {
"locale": "ko"
},
"lib/netstandard2.0/pl/System.CommandLine.resources.dll": {
"locale": "pl"
},
"lib/netstandard2.0/pt-BR/System.CommandLine.resources.dll": {
"locale": "pt-BR"
},
"lib/netstandard2.0/ru/System.CommandLine.resources.dll": {
"locale": "ru"
},
"lib/netstandard2.0/tr/System.CommandLine.resources.dll": {
"locale": "tr"
},
"lib/netstandard2.0/zh-Hans/System.CommandLine.resources.dll": {
"locale": "zh-Hans"
},
"lib/netstandard2.0/zh-Hant/System.CommandLine.resources.dll": {
"locale": "zh-Hant"
}
}
},
"System.Memory/4.5.5": {},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {}
}
},
"libraries": {
"Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost/4.14.0-3.25262.10": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Microsoft.Build.Locator/1.6.10": {
"type": "package",
"serviceable": true,
"sha512": "sha512-DJhCkTGqy1LMJzEmG/2qxRTMHwdPc3WdVoGQI5o5mKHVo4dsHrCMLIyruwU/NSvPNSdvONlaf7jdFXnAMuxAuA==",
"path": "microsoft.build.locator/1.6.10",
"hashPath": "microsoft.build.locator.1.6.10.nupkg.sha512"
},
"Microsoft.CodeAnalysis.BannedApiAnalyzers/3.11.0-beta1.24081.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-DH6L3rsbjppLrHM2l2/NKbnMaYd0NFHx2pjZaFdrVcRkONrV3i9FHv6Id8Dp6/TmjhXQsJVJJFbhhjkpuP1xxg==",
"path": "microsoft.codeanalysis.bannedapianalyzers/3.11.0-beta1.24081.1",
"hashPath": "microsoft.codeanalysis.bannedapianalyzers.3.11.0-beta1.24081.1.nupkg.sha512"
},
"Microsoft.CodeAnalysis.NetAnalyzers/8.0.0-preview.23468.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZhIvyxmUCqb8OiU/VQfxfuAmIB4lQsjqhMVYKeoyxzSI+d7uR5Pzx3ZKoaIhPizQ15wa4lnyD6wg3TnSJ6P4LA==",
"path": "microsoft.codeanalysis.netanalyzers/8.0.0-preview.23468.1",
"hashPath": "microsoft.codeanalysis.netanalyzers.8.0.0-preview.23468.1.nupkg.sha512"
},
"Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers/3.3.4-beta1.22504.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-2XRlqPAzVke7Sb80+UqaC7o57OwfK+tIr+aIOxrx41RWDMeR2SBUW7kL4sd6hfLFfBNsLo3W5PT+UwfvwPaOzA==",
"path": "microsoft.codeanalysis.performancesensitiveanalyzers/3.3.4-beta1.22504.1",
"hashPath": "microsoft.codeanalysis.performancesensitiveanalyzers.3.3.4-beta1.22504.1.nupkg.sha512"
},
"Microsoft.CodeAnalysis.PublicApiAnalyzers/3.11.0-beta1.24081.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3bYGBihvoNO0rhCOG1U9O50/4Q8suZ+glHqQLIAcKvnodSnSW+dYWYzTNb1UbS8pUS8nAUfxSFMwuMup/G5DtQ==",
"path": "microsoft.codeanalysis.publicapianalyzers/3.11.0-beta1.24081.1",
"hashPath": "microsoft.codeanalysis.publicapianalyzers.3.11.0-beta1.24081.1.nupkg.sha512"
},
"Microsoft.DotNet.XliffTasks/9.0.0-beta.25255.5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bb0fZB5ViPscdfYeWlmtyXJMzNkgcpkV5RWmXktfV9lwIUZgNZmFotUXrdcTyZzrN7v1tQK/Y6BGnbkP9gEsXg==",
"path": "microsoft.dotnet.xlifftasks/9.0.0-beta.25255.5",
"hashPath": "microsoft.dotnet.xlifftasks.9.0.0-beta.25255.5.nupkg.sha512"
},
"Microsoft.VisualStudio.Threading.Analyzers/17.13.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Qcd8IlaTXZVq3wolBnzby1P7kWihdWaExtD8riumiKuG1sHa8EgjV/o70TMjTaeUMhomBbhfdC9OPwAHoZfnjQ==",
"path": "microsoft.visualstudio.threading.analyzers/17.13.2",
"hashPath": "microsoft.visualstudio.threading.analyzers.17.13.2.nupkg.sha512"
},
"Newtonsoft.Json/13.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==",
"path": "newtonsoft.json/13.0.3",
"hashPath": "newtonsoft.json.13.0.3.nupkg.sha512"
},
"Roslyn.Diagnostics.Analyzers/3.11.0-beta1.24081.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-reHqZCDKifA+DURcL8jUfYkMGL4FpgNt5LI0uWTS6IpM8kKVbu/kO8byZsqfhBu4wUzT3MBDcoMfzhZPdENIpg==",
"path": "roslyn.diagnostics.analyzers/3.11.0-beta1.24081.1",
"hashPath": "roslyn.diagnostics.analyzers.3.11.0-beta1.24081.1.nupkg.sha512"
},
"System.Collections.Immutable/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==",
"path": "system.collections.immutable/9.0.0",
"hashPath": "system.collections.immutable.9.0.0.nupkg.sha512"
},
"System.CommandLine/2.0.0-beta4.24528.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Xt8tsSU8yd0ZpbT9gl5DAwkMYWLo8PV1fq2R/belrUbHVVOIKqhLfbWksbdknUDpmzMHZenBtD6AGAp9uJTa2w==",
"path": "system.commandline/2.0.0-beta4.24528.1",
"hashPath": "system.commandline.2.0.0-beta4.24528.1.nupkg.sha512"
},
"System.Memory/4.5.5": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
"path": "system.memory/4.5.5",
"hashPath": "system.memory.4.5.5.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
"path": "system.runtime.compilerservices.unsafe/6.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
}
}
}

View File

@ -1,605 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Utilities.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Build.Tasks.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.VisualBasic.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Win32.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Win32.Registry" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.Concurrent" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.NonGeneric" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections.Specialized" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Collections" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Annotations" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.EventBasedAsync" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel.TypeConverter" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ComponentModel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Console" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Data.Common" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Contracts" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.FileVersionInfo" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Process" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.StackTrace" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.TextWriterTraceListener" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.TraceSource" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.Tracing" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Drawing.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Compression.ZipFile" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.AccessControl" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.DriveInfo" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.Watcher" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.IsolatedStorage" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.MemoryMappedFiles" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Pipes.AccessControl" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.Pipes" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq.Expressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq.Parallel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq.Queryable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Linq" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.HttpListener" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Mail" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.NameResolution" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.NetworkInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Ping" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Requests" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Security" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.ServicePoint" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Sockets" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.WebClient" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.WebHeaderCollection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.WebProxy" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.WebSockets.Client" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.WebSockets" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ObjectModel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Emit.ILGeneration" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Emit.Lightweight" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Emit" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Resources.Writer" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.VisualC" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices.RuntimeInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Numerics" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Formatters" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Json" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Xml" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.AccessControl" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Claims" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Cng" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Csp" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Encoding" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.X509Certificates" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.Encoding.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.RegularExpressions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Overlapped" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Parallel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Thread" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.ThreadPool" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Transactions.Local" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.HttpUtility" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.ReaderWriter" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XPath.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XPath" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Xml.XmlSerializer" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="netstandard" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Configuration.ConfigurationManager" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Xml" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.CodeDom" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -1,13 +0,0 @@
{
"runtimeOptions": {
"tfm": "net6.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
},
"rollForward": "Major",
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false
}
}
}