using InABox.Core; namespace InABox.Database; public delegate void LogEvent(LogType type, string message); public interface IProviderFactory { string URL { get; set; } Type[] Types { get; set; } void ForceRecreateViews(); void Start(); IProvider NewProvider(Logger logger); } public interface IProvider { Logger Logger { get; set; } IEnumerable List(Filter? filter = null, Columns? columns = null, SortOrder? sort = null, CoreRange? range = null) where T : Entity, new(); bool TableExists(); bool TableExists(Type t); bool TableExists(string name); CoreTable? GetTable(); CoreTable? GetTable(Type t); CoreTable? GetTable(string name); void DropTable(); void DropTable(Type t); void DropTable(string name); IEnumerable List(string sql); CoreTable Query(string sql); int Update(string sql); CoreTable Query(Filter? filter = null, Columns? columns = null, SortOrder? sort = null, CoreRange? range = null, bool log = false, bool distinct = false) where T : Entity, new(); CoreTable Query(Type type, IFilter? filter = null, IColumns? columns = null, ISortOrder? sort = null, CoreRange? range = null, bool log = false, bool distinct = false); /// /// Same as , but only for deleted items /// CoreTable QueryDeleted(Deletion deletion, Filter? filter = null, Columns? columns = null, SortOrder? sort = null, CoreRange? range = null, bool deleted = false) where T : Entity, new(); T[] Load(Filter? filter = null, SortOrder? sort = null, CoreRange? range = null) where T : Entity, new(); void Save(T entity) where T : Entity; void Save(IEnumerable entities) where T : Entity; void Save(Type type, Entity entity); void Save(Type type, IEnumerable entities); void Delete(T entity, string userID) where T : Entity, new(); void Delete(IEnumerable entities, string userID) where T : Entity, new(); void Purge(T entity) where T : Entity; void Purge(IEnumerable entities) where T : Entity; void Purge(Deletion deletion); void Recover(Deletion deletion); List GetDeletionTypes(Deletion deletion); } public static class ProviderExtensions { public static void EnsureColumns(this IProvider provider, TEntity entity, Columns columns) where TEntity : Entity, new() { var newColumns = Columns.None() .AddRange(columns.Where(x => !entity.HasColumn(x.Property))); if (newColumns.Count > 0) { var row = provider.Query(new Filter(x => x.ID).IsEqualTo(entity.ID), newColumns).Rows.FirstOrDefault(); row?.FillObject(entity); } } public static void EnsureColumns(this IProvider provider, ICollection entities, Columns columns) where TEntity : Entity, IRemotable, new() { var newColumns = Columns.None() .AddRange(columns.Where(x => entities.Any(entity => !entity.HasColumn(x.Property)))); if (newColumns.Count > 0) { newColumns.Add(x => x.ID); var table = provider.Query(new Filter(x => x.ID).InList(entities.Select(x => x.ID).ToArray()), newColumns); foreach(var row in table.Rows) { var id = row.Get(x => x.ID); var entity = entities.FirstOrDefault(x => x.ID == id); if(entity is null) { // Shouldn't happen, but just in case. continue; } row?.FillObject(entity); } } } private class ProviderQueryProvider(IProvider provider, string userID) : IQueryProvider where TEntity : Entity, new() { public bool ExcludeCustomProperties { get; set; } private IProvider Provider = provider; private string UserID = userID; public CoreTable Query(Filter? filter = null, Columns? columns = null, SortOrder? sort = null, CoreRange? range = null) { return Provider.Query(filter, columns, sort, range); } public void Query(Filter? filter, Columns? columns, SortOrder? sort, CoreRange? range, Action action) { Task.Run(() => { try { var result = Provider.Query(filter, columns, sort, range); action(result, null); } catch(Exception e) { action(null, e); } }); } public void Delete(TEntity entity, string auditNote) { Provider.Delete(entity, UserID); } public void Delete(IEnumerable entities, string auditNote) { Provider.Delete(entities, UserID); } public void Delete(TEntity entity, string auditnote, Action callback) { Task.Run(() => { try { Provider.Delete(entity, UserID); callback(entity, null); } catch (Exception e) { callback(entity, e); } }); } public void Delete(IEnumerable entities, string auditnote, Action, Exception?> callback) { var ents = entities.AsIList(); Task.Run(() => { try { Provider.Delete(ents, UserID); callback(ents, null); } catch (Exception e) { callback(ents, e); } }); } public void Save(TEntity entity, string auditNote) { Provider.Save(entity); } public void Save(IEnumerable entities, string auditNote) { Provider.Save(entities); } public void Save(TEntity entity, string auditnote, Action callback) { Task.Run(() => { try { Provider.Save(entity); callback(entity, null); } catch (Exception e) { callback(entity, e); } }); } public void Save(IEnumerable entities, string auditnote, Action, Exception?> callback) { var ents = entities.AsIList(); Task.Run(() => { try { Provider.Save(ents); callback(ents, null); } catch (Exception e) { callback(ents, e); } }); } #region Non-generics public CoreTable Query(IFilter? filter = null, IColumns? columns = null, ISortOrder? sort = null, CoreRange? range = null) { return Provider.Query(typeof(TEntity), filter, columns, sort, range); } #endregion } public static IQueryProvider QueryProvider(this IProvider provider, string userID) where TEntity : Entity, new() { return new ProviderQueryProvider(provider, userID); } public static IQueryProvider QueryProvider(this IStore store) where TEntity : Entity, new() { return new ProviderQueryProvider(store.Provider, store.UserID); } }