2017875139
3 years ago
15 changed files with 337 additions and 0 deletions
@ -0,0 +1,14 @@ |
|||||
|
namespace Tenant.Api.DDD.Domain.Consts; |
||||
|
|
||||
|
public static class TenantConnectionStringConsts |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default value: 64
|
||||
|
/// </summary>
|
||||
|
public static int MaxNameLength { get; set; } = 64; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 1024
|
||||
|
/// </summary>
|
||||
|
public static int MaxValueLength { get; set; } = 1024; |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
namespace Tenant.Api.DDD.Domain.Consts; |
||||
|
|
||||
|
public static class TenantConsts |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default value: 64
|
||||
|
/// </summary>
|
||||
|
public static int MaxNameLength { get; set; } = 64; |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Tenant.Api.DDD.Domain.Shared; |
||||
|
|
||||
|
[Serializable] |
||||
|
public class TenantEto |
||||
|
{ |
||||
|
public Guid Id { get; set; } |
||||
|
|
||||
|
public string Name { get; set; } |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
namespace Tenant.Api.DDD.Domain.TenantAggregate; |
||||
|
|
||||
|
public interface ITenantManager |
||||
|
{ |
||||
|
Task<Tenant> CreateAsync(string name); |
||||
|
|
||||
|
Task ChangeNameAsync(Tenant tenant, string name); |
||||
|
} |
@ -0,0 +1,56 @@ |
|||||
|
using Easy; |
||||
|
using Easy.DDD.Domain.Entities; |
||||
|
using Tenant.Api.DDD.Domain.Consts; |
||||
|
|
||||
|
namespace Tenant.Api.DDD.Domain.TenantAggregate; |
||||
|
|
||||
|
public class Tenant : AggregateRoot<Guid> |
||||
|
{ |
||||
|
public virtual string Name { get; protected set; } |
||||
|
|
||||
|
public virtual List<TenantConnectionString> ConnectionStrings { get; protected set; } |
||||
|
|
||||
|
protected Tenant() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected internal Tenant(Guid id, string name) |
||||
|
: base(id) |
||||
|
{ |
||||
|
SetName(name); |
||||
|
|
||||
|
ConnectionStrings = new List<TenantConnectionString>(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public virtual void SetConnectionString(string name, string connectionString) |
||||
|
{ |
||||
|
var tenantConnectionString = ConnectionStrings.FirstOrDefault(x => x.Name == name); |
||||
|
|
||||
|
if (tenantConnectionString != null) |
||||
|
{ |
||||
|
tenantConnectionString.SetValue(connectionString); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ConnectionStrings.Add(new TenantConnectionString(Id, name, connectionString)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public virtual void RemoveConnectionString(string name) |
||||
|
{ |
||||
|
var tenantConnectionString = ConnectionStrings.FirstOrDefault(x => x.Name == name); |
||||
|
|
||||
|
if (tenantConnectionString != null) |
||||
|
{ |
||||
|
ConnectionStrings.Remove(tenantConnectionString); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected internal virtual void SetName(string name) |
||||
|
{ |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConsts.MaxNameLength); |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
using Easy; |
||||
|
using Easy.DDD.Domain.Entities; |
||||
|
using Tenant.Api.DDD.Domain.Consts; |
||||
|
|
||||
|
namespace Tenant.Api.DDD.Domain.TenantAggregate; |
||||
|
|
||||
|
public class TenantConnectionString : Entity |
||||
|
{ |
||||
|
public virtual Guid TenantId { get; protected set; } |
||||
|
|
||||
|
public virtual string Name { get; protected set; } |
||||
|
|
||||
|
public virtual string Value { get; protected set; } |
||||
|
|
||||
|
protected TenantConnectionString() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public TenantConnectionString(Guid tenantId, string name, string value) |
||||
|
{ |
||||
|
TenantId = tenantId; |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name), TenantConnectionStringConsts.MaxNameLength); |
||||
|
SetValue(value); |
||||
|
} |
||||
|
|
||||
|
public virtual void SetValue(string value) |
||||
|
{ |
||||
|
Value = Check.NotNullOrWhiteSpace(value, nameof(value), TenantConnectionStringConsts.MaxValueLength); |
||||
|
} |
||||
|
|
||||
|
public override object[] GetKeys() |
||||
|
{ |
||||
|
return new object[] { TenantId, Name }; |
||||
|
} |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
using Easy; |
||||
|
using Easy.DDD.Domain.Repositories; |
||||
|
using Easy.Guids; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
|
||||
|
namespace Tenant.Api.DDD.Domain.TenantAggregate; |
||||
|
|
||||
|
public class TenantManager |
||||
|
{ |
||||
|
public IRepository<Tenant> TenantRepository { get; } |
||||
|
protected IGuidGenerator GuidGenerator { get; } |
||||
|
public TenantManager(IRepository<Tenant> tenantRepository) |
||||
|
{ |
||||
|
TenantRepository = tenantRepository; |
||||
|
} |
||||
|
public virtual async Task<Tenant> CreateAsync(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
await ValidateNameAsync(name); |
||||
|
return new Tenant(GuidGenerator.Create(), name); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task ChangeNameAsync(Tenant tenant, string name) |
||||
|
{ |
||||
|
Check.NotNull(tenant, nameof(tenant)); |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
await ValidateNameAsync(name, tenant.Id); |
||||
|
tenant.SetName(name); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task ValidateNameAsync(string name, Guid? expectedId = null) |
||||
|
{ |
||||
|
var tenant = await TenantRepository |
||||
|
.Where(o => o.Name == name) |
||||
|
.Include(x => x.ConnectionStrings) |
||||
|
.OrderBy(t => t.Id) |
||||
|
.FirstOrDefaultAsync(); |
||||
|
|
||||
|
When.Is(tenant != null && tenant.Id != expectedId, "重复的租户名称: " + name); |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
using AutoMapper; |
||||
|
using Tenant.Api.DDD.Domain.Shared; |
||||
|
|
||||
|
namespace Tenant.Api.DDD.Domain; |
||||
|
|
||||
|
public class TenantManagementDomainMappingProfile : Profile |
||||
|
{ |
||||
|
public TenantManagementDomainMappingProfile() |
||||
|
{ |
||||
|
|
||||
|
CreateMap<TenantAggregate.Tenant, TenantEto>(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
var builder = WebApplication.CreateBuilder(args); |
||||
|
|
||||
|
// Add services to the container.
|
||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
|
builder.Services.AddEndpointsApiExplorer(); |
||||
|
builder.Services.AddSwaggerGen(); |
||||
|
|
||||
|
var app = builder.Build(); |
||||
|
|
||||
|
// Configure the HTTP request pipeline.
|
||||
|
if (app.Environment.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseSwagger(); |
||||
|
app.UseSwaggerUI(); |
||||
|
} |
||||
|
|
||||
|
var summaries = new[] |
||||
|
{ |
||||
|
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" |
||||
|
}; |
||||
|
|
||||
|
app.MapGet("/weatherforecast", () => |
||||
|
{ |
||||
|
var forecast = Enumerable.Range(1, 5).Select(index => |
||||
|
new WeatherForecast |
||||
|
( |
||||
|
DateTime.Now.AddDays(index), |
||||
|
Random.Shared.Next(-20, 55), |
||||
|
summaries[Random.Shared.Next(summaries.Length)] |
||||
|
)) |
||||
|
.ToArray(); |
||||
|
return forecast; |
||||
|
}) |
||||
|
.WithName("GetWeatherForecast"); |
||||
|
|
||||
|
app.Run(); |
||||
|
|
||||
|
internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary) |
||||
|
{ |
||||
|
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
{ |
||||
|
"$schema": "https://json.schemastore.org/launchsettings.json", |
||||
|
"iisSettings": { |
||||
|
"windowsAuthentication": false, |
||||
|
"anonymousAuthentication": true, |
||||
|
"iisExpress": { |
||||
|
"applicationUrl": "http://localhost:60261", |
||||
|
"sslPort": 0 |
||||
|
} |
||||
|
}, |
||||
|
"profiles": { |
||||
|
"Tenant.Api": { |
||||
|
"commandName": "Project", |
||||
|
"dotnetRunMessages": true, |
||||
|
"launchBrowser": true, |
||||
|
"launchUrl": "swagger", |
||||
|
"applicationUrl": "http://localhost:5275", |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
}, |
||||
|
"IIS Express": { |
||||
|
"commandName": "IISExpress", |
||||
|
"launchBrowser": true, |
||||
|
"launchUrl": "swagger", |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk.Web"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net6.0</TargetFramework> |
||||
|
<!--<Nullable>enable</Nullable>--> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="AutoMapper" Version="11.0.1" /> |
||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Easy.Api\Easy.DDD.Application.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Easy.DDD.Domain\Easy.DDD.Domain.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Easy.Guids\Easy.Guids.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Easy.Result\Easy.Result.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Easy.Specifications\Easy.Specifications.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Easy.Uow\Easy.Uow.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<Folder Include="DDD\Application\Dtos\" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft.AspNetCore": "Warning" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Information", |
||||
|
"Microsoft.AspNetCore": "Warning" |
||||
|
} |
||||
|
}, |
||||
|
"AllowedHosts": "*" |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Easy.DDD.Domain.Entities; |
||||
|
|
||||
|
public abstract class AggregateRoot : Entity, IAggregateRoot |
||||
|
{ |
||||
|
} |
||||
|
public abstract class AggregateRoot<TKey> : Entity<TKey>, IAggregateRoot<TKey> |
||||
|
{ |
||||
|
protected AggregateRoot() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected AggregateRoot(TKey id) |
||||
|
: base(id) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
Loading…
Reference in new issue