using System.Globalization; using System.Linq.Expressions; using System.Reflection; using AutoMapPropertyHelper; using EntityFrameworkCore.Projectables; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Proxies.Internal; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Storage; namespace NejCommon.Models; public abstract class CommonDbContext : DbContext { public CommonDbContext() : base() { } public CommonDbContext(DbContextOptions options) : base(options) { } public abstract CommonDbContext CreateCopy(); public async Task ApiSaveChangesAsync() { try { await SaveChangesAsync(); return true; } catch (Exception ex) { Console.WriteLine("Error saving db: " + ex.Message); Console.WriteLine(ex.StackTrace); return false; } } public static BadRequest SaveError = TypedResults.BadRequest(new Error { Message = "Error saving data to database" }); public async Task, T1>> ApiSaveChangesAsync(T1 value) where T1 : IResult { var res = await ApiSaveChangesAsync(); if (res) return value; else return SaveError; } public async Task, CreatedAtRoute>> ApiSaveChangesAsyncCreate(IServiceProvider providers, T1 value, bool apply = true, T2? response = null) where T2 : class, IAutomappedAttribute, new() { if (response == null) response = new T2(); if (!apply) return TypedResults.CreatedAtRoute((T2)response.ApplyFrom(providers, (object)value)); var res = await ApiSaveChangesAsync(); if (res) return TypedResults.CreatedAtRoute((T2)response.ApplyFrom(providers, (object)value)); else return SaveError; } public async Task, Ok>> ApiSaveChangesAsyncOk(IServiceProvider providers, T1 value, bool apply = true, T2? response = null) where T2 : class, IAutomappedAttribute, new() { if (response == null) response = new T2(); if (!apply) return TypedResults.Ok((T2)response.ApplyFrom(providers, (object)value)); var res = await ApiSaveChangesAsync(); if (res) return TypedResults.Ok((T2)response.ApplyFrom(providers, (object)value)); else return SaveError; } public async Task, Ok>>> ApiSaveChangesAsyncOk(IServiceProvider providers, IEnumerable value, bool apply = true, T2? response = null) where T2 : class, IAutomappedAttribute, new() { if (response == null) response = new T2(); if (!apply) return TypedResults.Ok(value.Select(x => new T2().ApplyFrom(providers, x)).ToList()); var res = await ApiSaveChangesAsync(); if (res) return TypedResults.Ok(value.Select(x => new T2().ApplyFrom(providers, x)).ToList()); else return SaveError; } public async Task, Ok>> ApiSaveChangesAsyncOk() { var res = await ApiSaveChangesAsync(); if (res) return TypedResults.Ok(); else return SaveError; } public async Task FindOrCreateAsync(Expression> predicate, Func factory) where T : class { var entity = ChangeTracker.Entries().Select(x => x.Entity).FirstOrDefault(predicate.Compile()); //find in currentDb if (entity == null) { entity = await Set().FirstOrDefaultAsync(predicate); } //find in change tracker if (entity != null && Entry(entity).State == EntityState.Deleted) { /* Entry(entity).State = EntityState.Modified; Attach(entity);*/ Entry(entity).State = EntityState.Detached; entity = await Set().FirstOrDefaultAsync(predicate); } //find in up-to-date db if (entity == null) { var newAppDB = CreateCopy(); entity = await newAppDB.Set().FirstOrDefaultAsync(predicate); //track the entity if it's not null and not already being tracked if (entity != null && this.Entry(entity).State == EntityState.Detached) Attach(entity); } //create if not found if (entity == null) { var newEntity = factory(); await this.AddAsync(newEntity); entity = newEntity; } return entity; } public T FindOrCreate(Expression> predicate, Func factory) where T : class { var entity = ChangeTracker.Entries().Where(e => e.State != EntityState.Deleted).Select(x => x.Entity).FirstOrDefault(predicate.Compile()); //find in change tracker if (entity != null && Entry(entity).State == EntityState.Deleted) { Entry(entity).State = EntityState.Modified; } //find in currentDb if (entity == null) { entity = Set().FirstOrDefault(predicate); } //find in up-to-date db if (entity == null) { var newAppDB = CreateCopy(); entity = newAppDB.Set().FirstOrDefault(predicate); if (entity != null) Attach(entity); } //create if not found if (entity == null) { var newEntity = factory(); this.Add(newEntity); entity = newEntity; } return entity; } public T Create(TParent parentI, string navigationName, Action config = null, params object[] constructorArguments) where T : class, new() where TParent : class { var candidates = Model .GetEntityTypes() .Where(x => x.ClrType == typeof(TParent)) .FirstOrDefault(); var nav = candidates?.FindNavigation(navigationName); var entityT = nav?.TargetEntityType; if (entityT == null || entityT.IsOwned()) { /* var source = this.GetService(); var materializer = source.GetEmptyMaterializer(entityT); var materializationContext = new MaterializationContext( ValueBuffer.Empty, this); var val = (T)materializer(materializationContext); */ var factory = this.GetService(); var val = (T)factory.CreateProxy( this, entityT, constructorArguments); if (!nav.IsCollection) { var refVal = Entry(parentI).Reference(navigationName); refVal.CurrentValue = val; if (refVal.TargetEntry == null) throw new Exception(); refVal.TargetEntry.State = EntityState.Added; } //this.ChangeTracker.DetectChanges(); /* var refVal = this.Set() .Entry(parentI) .Reference(navigationName); refVal.CurrentValue = val; Attach(val); if (refVal.TargetEntry == null) throw new Exception("test"); */ config?.Invoke(val); return val; } var entity = this.Set(entityT.Name).CreateProxy(constructorArguments); config?.Invoke(entity); this.Add(entity); this.ChangeTracker.DetectChanges(); return entity; } public T Create(Action config = null, params object[] constructorArguments) { var entity = this.CreateProxy(constructorArguments); config?.Invoke(entity); this.Add(entity); this.ChangeTracker.DetectChanges(); return entity; } public object Create(Type entityType, Action config = null, params object[] constructorArguments) { var entity = this.CreateProxy(entityType, constructorArguments); config?.Invoke(entity); this.Add(entity); this.ChangeTracker.DetectChanges(); return entity; } public void ApplyRelationships() { this.ChangeTracker.DetectChanges(); } }