282 lines
8.7 KiB
C#
282 lines
8.7 KiB
C#
|
|
|
|
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<bool> 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<Error> SaveError = TypedResults.BadRequest(new Error
|
|
{
|
|
Message = "Error saving data to database"
|
|
});
|
|
|
|
public async Task<Results<BadRequest<Error>, T1>> ApiSaveChangesAsync<T1>(T1 value) where T1 : IResult
|
|
{
|
|
var res = await ApiSaveChangesAsync();
|
|
|
|
if (res)
|
|
return value;
|
|
else
|
|
return SaveError;
|
|
}
|
|
public async Task<Results<BadRequest<Error>, CreatedAtRoute<T2>>> ApiSaveChangesAsyncCreate<T1, T2>(IServiceProvider providers, T1 value, bool apply = true, T2? response = null) where T2 : class, IAutomappedAttribute<T1, T2>, 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<Results<BadRequest<Error>, Ok<T2>>> ApiSaveChangesAsyncOk<T1, T2>(IServiceProvider providers, T1 value, bool apply = true, T2? response = null) where T2 : class, IAutomappedAttribute<T1, T2>, 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<Results<BadRequest<Error>, Ok<List<T2>>>> ApiSaveChangesAsyncOk<T1, T2>(IServiceProvider providers, IEnumerable<T1> value, bool apply = true, T2? response = null) where T2 : class, IAutomappedAttribute<T1, T2>, 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<Results<BadRequest<Error>, Ok>> ApiSaveChangesAsyncOk()
|
|
{
|
|
var res = await ApiSaveChangesAsync();
|
|
|
|
if (res)
|
|
return TypedResults.Ok();
|
|
else
|
|
return SaveError;
|
|
}
|
|
public async Task<T> FindOrCreateAsync<T>(Expression<Func<T, bool>> predicate, Func<T> factory) where T : class
|
|
{
|
|
var entity = ChangeTracker.Entries<T>().Select(x => x.Entity).FirstOrDefault(predicate.Compile());
|
|
|
|
//find in currentDb
|
|
if (entity == null)
|
|
{
|
|
entity = await Set<T>().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<T>().FirstOrDefaultAsync(predicate);
|
|
}
|
|
|
|
//find in up-to-date db
|
|
if (entity == null)
|
|
{
|
|
var newAppDB = CreateCopy();
|
|
|
|
entity = await newAppDB.Set<T>().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<T>(Expression<Func<T, bool>> predicate, Func<T> factory) where T : class
|
|
{
|
|
var entity = ChangeTracker.Entries<T>().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<T>().FirstOrDefault(predicate);
|
|
}
|
|
|
|
//find in up-to-date db
|
|
if (entity == null)
|
|
{
|
|
var newAppDB = CreateCopy();
|
|
|
|
entity = newAppDB.Set<T>().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<T, TParent>(TParent parentI, string navigationName, Action<T> 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<IStructuralTypeMaterializerSource>();
|
|
|
|
var materializer =
|
|
source.GetEmptyMaterializer(entityT);
|
|
|
|
var materializationContext =
|
|
new MaterializationContext(
|
|
ValueBuffer.Empty,
|
|
this);
|
|
|
|
var val = (T)materializer(materializationContext);
|
|
*/
|
|
|
|
var factory = this.GetService<IProxyFactory>();
|
|
|
|
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<TParent>()
|
|
.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<T>(entityT.Name).CreateProxy(constructorArguments);
|
|
|
|
config?.Invoke(entity);
|
|
this.Add(entity);
|
|
this.ChangeTracker.DetectChanges();
|
|
return entity;
|
|
}
|
|
public T Create<T>(Action<T> config = null, params object[] constructorArguments)
|
|
{
|
|
var entity = this.CreateProxy<T>(constructorArguments);
|
|
|
|
config?.Invoke(entity);
|
|
this.Add(entity);
|
|
this.ChangeTracker.DetectChanges();
|
|
return entity;
|
|
}
|
|
public object Create(Type entityType, Action<object> 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();
|
|
}
|
|
} |