IProvider.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. using InABox.Core;
  2. using IQueryProvider = InABox.Core.IQueryProvider;
  3. namespace InABox.Database;
  4. public delegate void LogEvent(LogType type, string message);
  5. public interface IProviderFactory
  6. {
  7. string URL { get; set; }
  8. Type[] Types { get; set; }
  9. void ForceRecreateViews();
  10. void Start();
  11. IProvider NewProvider(Logger logger);
  12. }
  13. public interface IProvider
  14. {
  15. Logger Logger { get; set; }
  16. IEnumerable<object[]> List<T>(Filter<T>? filter = null, Columns<T>? columns = null, SortOrder<T>? sort = null, CoreRange? range = null) where T : Entity, new();
  17. bool TableExists<T>();
  18. bool TableExists(Type t);
  19. bool TableExists(string name);
  20. CoreTable? GetTable<T>();
  21. CoreTable? GetTable(Type t);
  22. CoreTable? GetTable(string name);
  23. void DropTable<T>();
  24. void DropTable(Type t);
  25. void DropTable(string name);
  26. IEnumerable<object[]> List(string sql);
  27. CoreTable Query(string sql);
  28. int Update(string sql);
  29. string CreateSQL<T>(
  30. Filter<T>? filter,
  31. Columns<T>? columns,
  32. SortOrder<T>? sort = null,
  33. CoreRange? range = null,
  34. bool distinct = false);
  35. CoreTable Query<T>(Filter<T>? filter = null, Columns<T>? columns = null, SortOrder<T>? sort = null,
  36. CoreRange? range = null, bool log = false, bool distinct = false) where T : Entity, new();
  37. CoreTable Query(Type type, IFilter? filter = null, IColumns? columns = null, ISortOrder? sort = null,
  38. CoreRange? range = null, bool log = false, bool distinct = false);
  39. /// <summary>
  40. /// Same as <see cref="Query{T}(Filter{T}, Columns{T}, SortOrder{T}, CoreRange, bool, bool)"/>, but only for deleted items
  41. /// </summary>
  42. CoreTable QueryDeleted<T>(Deletion deletion, Filter<T>? filter = null, Columns<T>? columns = null, SortOrder<T>? sort = null, CoreRange? range = null, bool deleted = false) where T : Entity, new();
  43. T[] Load<T>(Filter<T>? filter = null, SortOrder<T>? sort = null, CoreRange? range = null) where T : Entity, new();
  44. void Save<T>(T entity) where T : Entity;
  45. void Save<T>(IEnumerable<T> entities) where T : Entity;
  46. void Save(Type type, Entity entity);
  47. void Save(Type type, IEnumerable<Entity> entities);
  48. void Delete<T>(T entity, string userID) where T : Entity, new();
  49. void Delete<T>(IEnumerable<T> entities, string userID) where T : Entity, new();
  50. void Purge<T>(T entity) where T : Entity;
  51. void Purge<T>(IEnumerable<T> entities) where T : Entity;
  52. void Purge(Deletion deletion);
  53. void Recover(Deletion deletion);
  54. List<Type> GetDeletionTypes(Deletion deletion);
  55. }
  56. public static class ProviderExtensions
  57. {
  58. public static void EnsureColumns<TEntity>(this IProvider provider, TEntity entity, Columns<TEntity> columns)
  59. where TEntity : Entity, new()
  60. {
  61. var newColumns = Columns.None<TEntity>()
  62. .AddRange(columns.Where(x => !entity.HasColumn(x.Property)));
  63. if (newColumns.Count > 0)
  64. {
  65. var row = provider.Query(new Filter<TEntity>(x => x.ID).IsEqualTo(entity.ID), newColumns).Rows.FirstOrDefault();
  66. row?.FillObject(entity);
  67. }
  68. }
  69. public static void EnsureColumns<TEntity>(this IProvider provider, ICollection<TEntity> entities, Columns<TEntity> columns)
  70. where TEntity : Entity, IRemotable, new()
  71. {
  72. var newColumns = Columns.None<TEntity>()
  73. .AddRange(columns.Where(x => entities.Any(entity => !entity.HasColumn(x.Property))));
  74. if (newColumns.Count > 0)
  75. {
  76. newColumns.Add(x => x.ID);
  77. var table = provider.Query(new Filter<TEntity>(x => x.ID).InList(entities.Select(x => x.ID).ToArray()), newColumns);
  78. foreach(var row in table.Rows)
  79. {
  80. var id = row.Get<TEntity, Guid>(x => x.ID);
  81. var entity = entities.FirstOrDefault(x => x.ID == id);
  82. if(entity is null)
  83. {
  84. // Shouldn't happen, but just in case.
  85. continue;
  86. }
  87. row?.FillObject(entity);
  88. }
  89. }
  90. }
  91. private class ProviderQueryProvider<TEntity>(IProvider provider, string userID) : IQueryProvider<TEntity>
  92. where TEntity : Entity, new()
  93. {
  94. public bool ExcludeCustomProperties { get; set; }
  95. private IProvider Provider = provider;
  96. private string UserID = userID;
  97. public CoreTable Query(Filter<TEntity>? filter = null, Columns<TEntity>? columns = null, SortOrder<TEntity>? sort = null, CoreRange? range = null)
  98. {
  99. return Provider.Query(filter, columns, sort, range);
  100. }
  101. public void Query(Filter<TEntity>? filter, Columns<TEntity>? columns, SortOrder<TEntity>? sort, CoreRange? range, Action<CoreTable?, Exception?> action)
  102. {
  103. Task.Run(() =>
  104. {
  105. try
  106. {
  107. var result = Provider.Query(filter, columns, sort, range);
  108. action(result, null);
  109. }
  110. catch(Exception e)
  111. {
  112. action(null, e);
  113. }
  114. });
  115. }
  116. public void Delete(TEntity entity, string auditNote)
  117. {
  118. Provider.Delete(entity, UserID);
  119. }
  120. public void Delete(IEnumerable<TEntity> entities, string auditNote)
  121. {
  122. Provider.Delete(entities, UserID);
  123. }
  124. public void Delete(TEntity entity, string auditnote, Action<TEntity, Exception?> callback)
  125. {
  126. Task.Run(() =>
  127. {
  128. try
  129. {
  130. Provider.Delete(entity, UserID);
  131. callback(entity, null);
  132. }
  133. catch (Exception e)
  134. {
  135. callback(entity, e);
  136. }
  137. });
  138. }
  139. public void Delete(IEnumerable<TEntity> entities, string auditnote, Action<IList<TEntity>, Exception?> callback)
  140. {
  141. var ents = entities.AsIList();
  142. Task.Run(() =>
  143. {
  144. try
  145. {
  146. Provider.Delete(ents, UserID);
  147. callback(ents, null);
  148. }
  149. catch (Exception e)
  150. {
  151. callback(ents, e);
  152. }
  153. });
  154. }
  155. public void Save(TEntity entity, string auditNote)
  156. {
  157. Provider.Save(entity);
  158. }
  159. public void Save(IEnumerable<TEntity> entities, string auditNote)
  160. {
  161. Provider.Save(entities);
  162. }
  163. public void Save(TEntity entity, string auditnote, Action<TEntity, Exception?> callback)
  164. {
  165. Task.Run(() =>
  166. {
  167. try
  168. {
  169. Provider.Save(entity);
  170. callback(entity, null);
  171. }
  172. catch (Exception e)
  173. {
  174. callback(entity, e);
  175. }
  176. });
  177. }
  178. public void Save(IEnumerable<TEntity> entities, string auditnote, Action<IEnumerable<TEntity>, Exception?> callback)
  179. {
  180. var ents = entities.AsIList();
  181. Task.Run(() =>
  182. {
  183. try
  184. {
  185. Provider.Save(ents);
  186. callback(ents, null);
  187. }
  188. catch (Exception e)
  189. {
  190. callback(ents, e);
  191. }
  192. });
  193. }
  194. #region Non-generics
  195. public CoreTable Query(IFilter? filter = null, IColumns? columns = null, ISortOrder? sort = null, CoreRange? range = null)
  196. {
  197. return Provider.Query(typeof(TEntity), filter, columns, sort, range);
  198. }
  199. #endregion
  200. }
  201. public static IQueryProvider<TEntity> QueryProvider<TEntity>(this IProvider provider, string userID) where TEntity : Entity, new()
  202. {
  203. return new ProviderQueryProvider<TEntity>(provider, userID);
  204. }
  205. public static IQueryProvider<TEntity> QueryProvider<TEntity>(this IStore store) where TEntity : Entity, new()
  206. {
  207. return new ProviderQueryProvider<TEntity>(store.Provider, store.UserID);
  208. }
  209. private class ProviderQueryProviderFactory(IProvider provider, string userID) : IQueryProviderFactory
  210. {
  211. public bool ExcludeCustomProperties => false;
  212. public IQueryProvider Create(Type T)
  213. {
  214. return (Activator.CreateInstance(typeof(ProviderQueryProvider<>).MakeGenericType(T), provider, userID) as IQueryProvider)!;
  215. }
  216. }
  217. public static IQueryProviderFactory GetQueryProviderFactory(this IProvider provider, string userID)
  218. {
  219. return new ProviderQueryProviderFactory(provider, userID);
  220. }
  221. public static IQueryProviderFactory GetQueryProviderFactory(this IStore store)
  222. {
  223. return new ProviderQueryProviderFactory(store.Provider, store.UserID);
  224. }
  225. }