added a notes file for important running stuff

Create working for tenant and added in efcore and data layer
This commit is contained in:
2026-04-02 09:05:53 +10:30
parent ad107753b5
commit 4ee8b9101d
22 changed files with 415 additions and 26 deletions
+31 -2
View File
@@ -1,7 +1,8 @@
using Microsoft.AspNetCore.Mvc; using Emergence.api.Models;
using Emergence.models; using Emergence.models;
using Emergence.services.Interface; using Emergence.services.Interface;
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Emergence.api.Controllers; namespace Emergence.api.Controllers;
@@ -17,7 +18,7 @@ public class TenantController : ControllerBase
} }
[HttpGet(Name = "GetTenants")] [HttpGet(Name = "GetTenants")]
public async Task<Results<Ok<IEnumerable<TenantModel>>, NotFound>> Get() public async Task<Results<Ok<IEnumerable<TenantModel>>, NotFound>> GetTenants()
{ {
var tenants = await _tenantService.GetAllAsync(); var tenants = await _tenantService.GetAllAsync();
if (tenants is null) if (tenants is null)
@@ -26,4 +27,32 @@ public class TenantController : ControllerBase
} }
return TypedResults.Ok(tenants); return TypedResults.Ok(tenants);
} }
[HttpGet("{id}", Name = "GetTenantById")]
public async Task<Results<Ok<TenantModel>, NotFound, BadRequest>> GetTenantById(Guid id)
{
var tenant = await _tenantService.GetByIdAsync(id);
if (tenant is null)
{
return TypedResults.NotFound();
}
return TypedResults.Ok(tenant);
}
[HttpPost(Name = "CreateTenant")]
public async Task<Results<Created<TenantModel>, Conflict<ErrorDetailResults>, BadRequest<ValidationProblemResult>, UnprocessableEntity<ErrorDetailResults>>> Create(TenantModel tenant)
{
if (tenant == null || !ModelState.IsValid)
return TypedResults.BadRequest<ValidationProblemResult>(new ValidationProblemResult(ModelState));
if (tenant.Id.HasValue && tenant.Id.Value != Guid.Empty && _tenantService.GetByIdAsync(tenant.Id.Value).Result != null)
return TypedResults.Conflict(new ErrorDetailResults($"Tenant already exists", $"Tenant with the id: {tenant.Id} already exists"));
var newTenant = await _tenantService.CreateAsync(tenant);
if (newTenant is null)
{
return TypedResults.UnprocessableEntity(new ErrorDetailResults($"Tenant failed to create", $"The new tenant failed to create. Please try again."));
}
return TypedResults.Created($"/tenant/{newTenant.Id}", newTenant);
}
} }
+4
View File
@@ -3,5 +3,9 @@
GET {{Emergence.api_HostAddress}}/tenant/ GET {{Emergence.api_HostAddress}}/tenant/
Accept: application/json Accept: application/json
###
GET {{Emergence.api_HostAddress}}/tenant/cc641668-862d-4101-8581-14574d58fd7f/
Accept: application/json
### ###
GET {{Emergence.api_HostAddress}}/openapi/v1.json GET {{Emergence.api_HostAddress}}/openapi/v1.json
@@ -0,0 +1,13 @@
namespace Emergence.api.Models;
public class ErrorDetailResults
{
public string Title { get; set; }
public string Description { get; set; }
public ErrorDetailResults(string Title, string Description)
{
this.Title = Title;
this.Description = Description;
}
}
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Emergence.api.Models;
public class ValidationProblemResult
{
public ModelStateDictionary ModelState { get; set; }
public ValidationProblemResult(ModelStateDictionary modelState)
{
ModelState = modelState;
}
}
+5
View File
@@ -0,0 +1,5 @@
To add a migration
Add-Migration <migrationname> -Project Emergence.data -Startup Emergence.data
To update the database
Update-Database -Project Emergence.data -Startup Emergence.data
+1 -1
View File
@@ -4,7 +4,7 @@ using Scalar.AspNetCore;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddEmergenceServices(); builder.Services.AddEmergenceServices(builder.Configuration);
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
+3
View File
@@ -1,4 +1,7 @@
{ {
"ConnectionStrings": {
"AdminConnection": "Data Source=10.1.2.240;Database=Emergence;Integrated Security=false;User Id=emergence;Password=Bed7432Rank!;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
},
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",
+33
View File
@@ -0,0 +1,33 @@
using Emergence.data.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Emergence.data.Contexts
{
public class AdminDbContext : DbContext
{
public AdminDbContext() : base()
{
}
public AdminDbContext(DbContextOptions<AdminDbContext> options) : base(options) {
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSeeding((context, _) =>
{
context.SaveChanges();
});
}
public DbSet<Tenant> Tenant { get; set; }
}
}
+24
View File
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.5" />
</ItemGroup>
</Project>
@@ -0,0 +1,19 @@
using Emergence.data.Contexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Emergence.data.Extensions;
public static class ServiceBuilderExtension
{
public static IServiceCollection AddDataServices(this IServiceCollection services, IConfigurationManager configuration)
{
var connectionString = configuration.GetConnectionString("AdminConnection") ?? throw new InvalidOperationException("Connection string 'AdminConnection' not found.");
services.AddDbContext<AdminDbContext>(options =>
options.UseSqlServer(connectionString, x => x.MigrationsAssembly(typeof(AdminDbContext).Assembly.FullName)));
return services;
}
}
@@ -0,0 +1,39 @@
using Emergence.data.Contexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using System;
using System.Collections.Generic;
using System.Text;
namespace Emergence.data.Factories;
public class AdminDbContextFactory : IDesignTimeDbContextFactory<AdminDbContext>
{
private readonly IConfiguration _configuration;
public AdminDbContextFactory()
{
}
public AdminDbContextFactory(IConfiguration configuration)
{
_configuration = configuration;
}
public AdminDbContext CreateDbContext(string[] args)
{
var dir = new DirectoryInfo(Environment.CurrentDirectory);
// throw new Exception($"{dir.Parent}\\Emergence.api\\appsettings.json");
ConfigurationManager config = new ConfigurationManager();
config.AddJsonFile($"{dir.Parent}\\Emergence.api\\appsettings.json");
var t = config.GetConnectionString("AdminConnection");
var optionsBuilder = new DbContextOptionsBuilder<AdminDbContext>();
optionsBuilder.UseSqlServer(t);
return new AdminDbContext(optionsBuilder.Options);
}
}
@@ -0,0 +1,52 @@
// <auto-generated />
using System;
using Emergence.data.Contexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Emergence.data.Migrations
{
[DbContext(typeof(AdminDbContext))]
[Migration("20260401053644_test")]
partial class test
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Emergence.data.Models.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("CompanyName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsInactive")
.HasColumnType("bit");
b.Property<string>("TenantCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Tenant");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,36 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Emergence.data.Migrations
{
/// <inheritdoc />
public partial class test : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Tenant",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
CompanyName = table.Column<string>(type: "nvarchar(max)", nullable: false),
IsInactive = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tenant", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Tenant");
}
}
}
@@ -0,0 +1,49 @@
// <auto-generated />
using System;
using Emergence.data.Contexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Emergence.data.Migrations
{
[DbContext(typeof(AdminDbContext))]
partial class AdminDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Emergence.data.Models.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("CompanyName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsInactive")
.HasColumnType("bit");
b.Property<string>("TenantCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Tenant");
});
#pragma warning restore 612, 618
}
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Emergence.data.Models
{
public class Tenant
{
[Key]
public Guid Id { get; set; }
[Required]
public required string TenantCode { get; set; }
[Required]
public required string CompanyName { get; set; }
[Required]
public required bool IsInactive { get; set; }
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Emergence.models;
public class TenantModel public class TenantModel
{ {
public Guid Id { get; set; } public Guid? Id { get; set; }
public string TenantCode { get; set; } public string TenantCode { get; set; }
public string CompanyName { get; set; } public string CompanyName { get; set; }
public bool IsInactive { get; set; } public bool IsInactive { get; set; }
@@ -11,6 +11,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Emergence.data\Emergence.data.csproj" />
<ProjectReference Include="..\Emergence.models\Emergence.models.csproj" /> <ProjectReference Include="..\Emergence.models\Emergence.models.csproj" />
</ItemGroup> </ItemGroup>
@@ -1,19 +1,19 @@
using Emergence.services.Interface; using Emergence.data.Extensions;
using Emergence.services.Interface;
using Emergence.services.Services; using Emergence.services.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using Microsoft.IdentityModel.Protocols;
using System.Collections.Generic;
using System.Text;
namespace Emergence.services.Extensions namespace Emergence.services.Extensions;
public static class ServiceBuilderExtension
{ {
public static class ServiceBuilderExtension public static IServiceCollection AddEmergenceServices(this IServiceCollection services, IConfigurationManager configuration)
{ {
public static IServiceCollection AddEmergenceServices(this IServiceCollection services) services.AddDataServices(configuration);
{ services = services.AddTransient<ITenantService, TenantService>();
services = services.AddTransient<ITenantService, TenantService>();
return services; return services;
}
} }
} }
@@ -8,5 +8,7 @@ namespace Emergence.services.Interface
{ {
public Task<IEnumerable<T>> GetAllAsync(); public Task<IEnumerable<T>> GetAllAsync();
public Task<T> GetByIdAsync(I id); public Task<T> GetByIdAsync(I id);
public Task<T> CreateAsync(T tenant);
} }
} }
+55 -11
View File
@@ -1,29 +1,73 @@
using Emergence.models; using Emergence.data.Contexts;
using Emergence.data.Models;
using Emergence.models;
using Emergence.services.Interface; using Emergence.services.Interface;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.Common;
using System.Text; using System.Text;
namespace Emergence.services.Services; namespace Emergence.services.Services;
public static class TenantExtension
{
public static TenantModel ConvertToTenantModel(this Tenant tenant)
{
TenantModel model = new TenantModel
{
Id = tenant.Id,
TenantCode = tenant.TenantCode,
CompanyName = tenant.CompanyName,
IsInactive = tenant.IsInactive,
};
return model;
}
public static Tenant ConvertToTenant(this TenantModel tenant)
{
Tenant model = new Tenant
{
Id = tenant.Id.HasValue ? tenant.Id.Value : Guid.NewGuid(),
TenantCode = tenant.TenantCode,
CompanyName = tenant.CompanyName,
IsInactive = tenant.IsInactive,
};
return model;
}
}
public class TenantService : ITenantService public class TenantService : ITenantService
{ {
private List<TenantModel> testList = [ private readonly AdminDbContext _dbContext;
new TenantModel {Id = Guid.NewGuid(), TenantCode = "FJP", CompanyName = "Fred J Potter", IsInactive = false }, public TenantService(AdminDbContext dbContext)
new TenantModel {Id = Guid.NewGuid(), TenantCode = "JAZZ", CompanyName = "Jazzbond", IsInactive = false }, {
new TenantModel {Id = Guid.NewGuid(), TenantCode = "TDT", CompanyName = "The Digital Tailor", IsInactive = false }, _dbContext = dbContext;
new TenantModel {Id = Guid.NewGuid(), TenantCode = "HBAS", CompanyName = "Hicks Building and Asbestos Services", IsInactive = false }, }
];
public async Task<IEnumerable<TenantModel>> GetAllAsync() public async Task<IEnumerable<TenantModel>> GetAllAsync()
{ {
return testList; return _dbContext.Tenant.Select(s => s.ConvertToTenantModel());
} }
public async Task<TenantModel> GetByIdAsync(Guid id) public async Task<TenantModel> GetByIdAsync(Guid id)
{ {
#pragma warning disable CS8603 // Possible null reference return. var entity = _dbContext.Tenant.Where(f => f.Id == id)?.Select(s => s.ConvertToTenantModel()).FirstOrDefault();
return testList.FirstOrDefault(f => f.Id == id); return entity;
#pragma warning restore CS8603 // Possible null reference return. }
public async Task<TenantModel> CreateAsync(TenantModel model)
{
if (model.Id == Guid.Empty || model.Id == null)
model.Id = Guid.NewGuid();
Tenant newTenant = model.ConvertToTenant();
try
{
_dbContext.Tenant.Add(newTenant);
await _dbContext.SaveChangesAsync();
return model;
}
catch (DbException ex)
{
return null;
}
} }
} }
+1
View File
@@ -1,5 +1,6 @@
<Solution> <Solution>
<Project Path="Emergence.api/Emergence.api.csproj" Id="fd02b97a-65d1-4e9f-91b1-347ec75c9f0a" /> <Project Path="Emergence.api/Emergence.api.csproj" Id="fd02b97a-65d1-4e9f-91b1-347ec75c9f0a" />
<Project Path="Emergence.data/Emergence.data.csproj" Id="3eaa9358-1ec9-4334-852a-33963789ef08" />
<Project Path="Emergence.models/Emergence.models.csproj" Id="289779a8-43b3-44ec-994b-2ac4fbc29327" /> <Project Path="Emergence.models/Emergence.models.csproj" Id="289779a8-43b3-44ec-994b-2ac4fbc29327" />
<Project Path="Emergence.services/Emergence.services.csproj" Id="4a98db16-1ee8-486a-bcb9-5c9fa705a616" /> <Project Path="Emergence.services/Emergence.services.csproj" Id="4a98db16-1ee8-486a-bcb9-5c9fa705a616" />
</Solution> </Solution>