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 : AutoController where TType : class where TRequest : class, IAutomappedAttribute, new() where TResponse : class, IAutomappedAttribute, new() { public AutoController(CommonDbContext appDb, IServiceProvider providers) : base(appDb, providers) { } } /// /// /// /// The underyling type /// The request type /// The response type [ApiController] public abstract partial class AutoController : AutoGetterController where TType : class where TGetAllResponse : class, IAutomappedAttribute, new() where TCreateRequest : class, IAutomappedAttribute, new() where TCreateResponse : class, IAutomappedAttribute, new() where TGetResponse : class, IAutomappedAttribute, new() where TUpdateRequest : class, IAutomappedAttribute, new() where TUpdateResponse : class, IAutomappedAttribute, 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), }; /// /// Creates the /// /// /// Success /// There was an error [HttpPost] public virtual async Task, CreatedAtRoute>> 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(providers, entity, true, (TCreateResponse)GetResponseType(RequestedResponseType.Create, entity)); } /// /// Delete company. /// /// /// Success /// There was an error [HttpDelete] [Route("{id}/")] public virtual async Task, Ok>> Delete([FromServices][ModelBinder(Name = "id")] TType entity) { db.Remove(entity!); return await db.ApiSaveChangesAsync(TypedResults.Ok()); } /// /// Update company. /// /// /// /// Success /// There was an error [HttpPut] [Route("{id}/")] public virtual async Task, Ok>> 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(providers, entity, true, (TUpdateResponse)GetResponseType(RequestedResponseType.Update, entity)); //use the private constructor thru reflection var ctor = typeof(Results, Ok>).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0]; return (Results, Ok>)ctor.Invoke(new object[] { res.Result }); } } [ApiController] public abstract class AutoGetterController : ControllerBase where TType : class where TGetAllResponse : IAutomappedAttribute, new() where TGetResponse : IAutomappedAttribute, new() { protected readonly CommonDbContext db; protected readonly IServiceProvider providers; public AutoGetterController(CommonDbContext appDb, IServiceProvider providers) : base() { db = appDb; this.providers = providers; } protected abstract IQueryable 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 ApplyDefaultOrdering(IQueryable query) { return query; } protected virtual Task> ApplyPagination(IQueryable query, Pagination pag, CancellationToken cancellationToken = default) { return query.ApplyPaginationRes(providers, pag, cancellationToken: cancellationToken); } /// /// Get all the /// /// Success /// There was an error [HttpGet] public virtual async Task, Ok>>> 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); } /// /// Gets the /// /// /// Success /// There was an error [HttpGet] [Route("{id}/")] public virtual async Task>> Get([FromServices][ModelBinder(Name = "id")] TType entity) { var dat = GetResponseType(RequestedResponseType.Get, entity).ApplyFrom(providers, entity); return TypedResults.Ok((TGetResponse)dat); } } }