NejCommon.NET/Controllers/AutoController.cs
2025-03-04 00:42:52 +01:00

196 lines
8.2 KiB
C#

using System.Linq.Expressions;
using System.Reflection;
using ApiSourceGeneratorHelper;
using AutoMapPropertyHelper;
using EntityFrameworkCore.Projectables;
using EntityFrameworkCore.Projectables.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NejCommon.Utils;
using Swashbuckle.AspNetCore.Annotations;
using NejCommon.Models;
using System.Runtime.CompilerServices;
namespace NejCommon.Controllers
{
public abstract class AutoController<TOwner, TType, TRequest, TResponse> : AutoController<TOwner, TType, TResponse, TRequest, TResponse, TResponse, TRequest, TResponse>
where TType : class
where TRequest : class, IAutomappedAttribute<TType, TRequest>, new()
where TResponse : class, IAutomappedAttribute<TType, TResponse>, new()
{
public AutoController(CommonDbContext appDb, IServiceProvider providers) : base(appDb, providers)
{
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="TType">The underyling type</typeparam>
/// <typeparam name="TRequest">The request type</typeparam>
/// <typeparam name="TResponse">The response type</typeparam>
[ApiController]
public abstract partial class AutoController<TOwner, TType, TGetAllResponse, TCreateRequest, TCreateResponse, TGetResponse, TUpdateRequest, TUpdateResponse> : AutoGetterController<TOwner, TType, TGetAllResponse, TGetResponse>
where TType : class
where TGetAllResponse : class, IAutomappedAttribute<TType, TGetAllResponse>, new()
where TCreateRequest : class, IAutomappedAttribute<TType, TCreateRequest>, new()
where TCreateResponse : class, IAutomappedAttribute<TType, TCreateResponse>, new()
where TGetResponse : class, IAutomappedAttribute<TType, TGetResponse>, new()
where TUpdateRequest : class, IAutomappedAttribute<TType, TUpdateRequest>, new()
where TUpdateResponse : class, IAutomappedAttribute<TType, TUpdateResponse>, new()
{
public AutoController(CommonDbContext appDb, IServiceProvider providers) : base(appDb, providers)
{
}
protected virtual TType AssociateWithParent(TType entity, TOwner comp)
{
var props = typeof(TType).GetProperty(HelperMainName);
if (props == null)
{
//not implemented
throw new NotImplementedException("Special parent association not implemented");
}
props.SetValue(entity, comp);
return entity;
}
protected override IAutomappedAttribute GetResponseType(RequestedResponseType type, TType entity) => type switch
{
RequestedResponseType.Create => new TCreateResponse(),
RequestedResponseType.Update => new TUpdateResponse(),
_ => base.GetResponseType(type, entity),
};
/// <summary>
/// Creates the <typeparamref name="TType"/>
/// </summary>
/// <param name="body"></param>
/// <response code="201">Success</response>
/// <response code="0">There was an error</response>
[HttpPost]
public virtual async Task<Results<BadRequest<Error>, CreatedAtRoute<TCreateResponse>>> Create([FromServices] TOwner company, [FromBody] TCreateRequest body)
{
var type = body.GetSourceType();
var entity = (TType)db.Create(type);
entity = AssociateWithParent(entity, company);
await db.AddAsync(entity);
body.ApplyTo(providers, (object)entity);
return await db.ApiSaveChangesAsyncCreate<TType, TCreateResponse>(providers, entity, true, (TCreateResponse)GetResponseType(RequestedResponseType.Create, entity));
}
/// <summary>
/// Delete company.
/// </summary>
/// <param name="entity"></param>
/// <response code="200">Success</response>
/// <response code="0">There was an error</response>
[HttpDelete]
[Route("{id}/")]
public virtual async Task<Results<BadRequest<Error>, Ok>> Delete([FromServices][ModelBinder(Name = "id")] TType entity)
{
db.Remove(entity!);
return await db.ApiSaveChangesAsync(TypedResults.Ok());
}
/// <summary>
/// Update company.
/// </summary>
/// <param name="entity"></param>
/// <param name="body"></param>
/// <response code="200">Success</response>
/// <response code="0">There was an error</response>
[HttpPut]
[Route("{id}/")]
public virtual async Task<Results<NotFound, BadRequest<Error>, Ok<TUpdateResponse>>> Update([FromServices][ModelBinder(Name = "id")] TType entity, [FromBody] TUpdateRequest body)
{
body.ApplyTo(providers, (object)entity);
var dat = new TUpdateResponse().ApplyFrom(providers, entity);
var res = await db.ApiSaveChangesAsyncOk<TType, TUpdateResponse>(providers, entity, true, (TUpdateResponse)GetResponseType(RequestedResponseType.Update, entity));
//use the private constructor thru reflection
var ctor = typeof(Results<NotFound, BadRequest<Error>, Ok<TUpdateResponse>>).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
return (Results<NotFound, BadRequest<Error>, Ok<TUpdateResponse>>)ctor.Invoke(new object[] { res.Result });
}
}
[ApiController]
public abstract class AutoGetterController<TOwner, TType, TGetAllResponse, TGetResponse> : ControllerBase
where TType : class
where TGetAllResponse : IAutomappedAttribute<TType, TGetAllResponse>, new()
where TGetResponse : IAutomappedAttribute<TType, TGetResponse>, new()
{
protected readonly CommonDbContext db;
protected readonly IServiceProvider providers;
public AutoGetterController(CommonDbContext appDb, IServiceProvider providers) : base()
{
db = appDb;
this.providers = providers;
}
protected abstract IQueryable<TType> GetQuery(TOwner comp);
public enum RequestedResponseType
{
Get,
Create,
Update
}
protected virtual IAutomappedAttribute GetResponseType(RequestedResponseType type, TType entity) => type switch
{
RequestedResponseType.Get => new TGetResponse(),
_ => throw new InvalidOperationException("Not implemented"),
};
protected virtual IQueryable<TType> ApplyDefaultOrdering(IQueryable<TType> query)
{
return query;
}
protected virtual Task<PaginationResponse<TGetAllResponse>> ApplyPagination(IQueryable<TType> query, Pagination pag, CancellationToken cancellationToken = default)
{
return query.ApplyPaginationRes<TType, TGetAllResponse>(providers, pag, cancellationToken: cancellationToken);
}
/// <summary>
/// Get all the <typeparamref name="TType"/>
/// </summary>
/// <response code="200">Success</response>
/// <response code="0">There was an error</response>
[HttpGet]
public virtual async Task<Results<BadRequest<Error>, Ok<PaginationResponse<TGetAllResponse>>>> GetAll([FromServices] TOwner company, [FromQuery] Pagination pag, CancellationToken cancellationToken)
{
var data = await ApplyPagination(ApplyDefaultOrdering(GetQuery(company)).AsNoTrackingWithIdentityResolution(), pag, cancellationToken);
//Console.Writeline(data.Data);
return TypedResults.Ok(data);
}
/// <summary>
/// Gets the <typeparamref name="TType"/>
/// </summary>
/// <param name="entity"></param>
/// <response code="200">Success</response>
/// <response code="0">There was an error</response>
[HttpGet]
[Route("{id}/")]
public virtual async Task<Results<NotFound, Ok<TGetResponse>>> Get([FromServices][ModelBinder(Name = "id")] TType entity)
{
var dat = GetResponseType(RequestedResponseType.Get, entity).ApplyFrom(providers, entity);
return TypedResults.Ok((TGetResponse)dat);
}
}
}