| | | 1 | | using System.Collections.Concurrent; |
| | | 2 | | using Elsa.Common.Entities; |
| | | 3 | | |
| | | 4 | | namespace Elsa.Common.Services; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// A simple in-memory store for entities. |
| | | 8 | | /// </summary> |
| | | 9 | | /// <typeparam name="TEntity">The type of entity to store.</typeparam> |
| | | 10 | | public class MemoryStore<TEntity> |
| | | 11 | | { |
| | 0 | 12 | | private IDictionary<string, TEntity> Entities { get; set; } = new ConcurrentDictionary<string, TEntity>(); |
| | | 13 | | |
| | | 14 | | /// <summary> |
| | | 15 | | /// Gets a queryable of all entities. |
| | | 16 | | /// </summary> |
| | 0 | 17 | | public IQueryable<TEntity> Queryable => Entities.Values.AsQueryable(); |
| | | 18 | | |
| | | 19 | | /// <summary> |
| | | 20 | | /// Adds an entity. |
| | | 21 | | /// </summary> |
| | | 22 | | /// <param name="entity">The entity to add.</param> |
| | | 23 | | /// <param name="idAccessor">A function that returns the ID of the entity.</param> |
| | 0 | 24 | | public void Add(TEntity entity, Func<TEntity, string> idAccessor) => Entities.Add(idAccessor(entity), entity); |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// Adds many entities. |
| | | 28 | | /// </summary> |
| | | 29 | | /// <param name="entities">The entities to add.</param> |
| | | 30 | | /// <param name="idAccessor">A function that returns the ID of the entity.</param> |
| | | 31 | | public void AddMany(IEnumerable<TEntity> entities, Func<TEntity, string> idAccessor) |
| | | 32 | | { |
| | 0 | 33 | | foreach (var entity in entities) |
| | 0 | 34 | | Add(entity, idAccessor); |
| | 0 | 35 | | } |
| | | 36 | | |
| | | 37 | | /// <summary> |
| | | 38 | | /// Adds or updates an entity. |
| | | 39 | | /// </summary> |
| | | 40 | | /// <param name="entity">The entity to add or update.</param> |
| | | 41 | | /// <param name="idAccessor">A function that returns the ID of the entity.</param> |
| | 0 | 42 | | public void Save(TEntity entity, Func<TEntity, string> idAccessor) => Entities[idAccessor(entity)] = entity; |
| | | 43 | | |
| | | 44 | | /// <summary> |
| | | 45 | | /// Adds or updates many entities. |
| | | 46 | | /// </summary> |
| | | 47 | | /// <param name="entities">The entities to add or update.</param> |
| | | 48 | | /// <param name="idAccessor">A function that returns the ID of the entity.</param> |
| | | 49 | | public void SaveMany(IEnumerable<TEntity> entities, Func<TEntity, string> idAccessor) |
| | | 50 | | { |
| | 0 | 51 | | foreach (var entity in entities) |
| | 0 | 52 | | Save(entity, idAccessor); |
| | 0 | 53 | | } |
| | | 54 | | |
| | | 55 | | /// <summary> |
| | | 56 | | /// Updates an entity. |
| | | 57 | | /// </summary> |
| | | 58 | | /// <param name="entity">The entity to update.</param> |
| | | 59 | | /// <param name="idAccessor">A function that returns the ID of the entity.</param> |
| | 0 | 60 | | public void Update(TEntity entity, Func<TEntity, string> idAccessor) => Entities[idAccessor(entity)] = entity; |
| | | 61 | | |
| | | 62 | | /// <summary> |
| | | 63 | | /// Finds an entity matching the specified predicate. |
| | | 64 | | /// </summary> |
| | | 65 | | /// <param name="predicate">A predicate to match.</param> |
| | | 66 | | /// <returns>The matching entity or null if no match was found.</returns> |
| | 0 | 67 | | public TEntity? Find(Func<TEntity, bool> predicate) => Entities.Values.Where(predicate).FirstOrDefault(); |
| | | 68 | | |
| | | 69 | | /// <summary> |
| | | 70 | | /// Finds all entities matching the specified predicate. |
| | | 71 | | /// </summary> |
| | | 72 | | /// <param name="predicate">A predicate to match.</param> |
| | | 73 | | /// <returns>The matching entities.</returns> |
| | 0 | 74 | | public IEnumerable<TEntity> FindMany(Func<TEntity, bool> predicate) => Entities.Values.Where(predicate); |
| | | 75 | | |
| | | 76 | | /// <summary> |
| | | 77 | | /// Finds all entities matching the specified predicate and orders them by the specified key. |
| | | 78 | | /// </summary> |
| | | 79 | | /// <param name="predicate">The predicate to match.</param> |
| | | 80 | | /// <param name="orderBy">The key to order by.</param> |
| | | 81 | | /// <param name="orderDirection">The order direction.</param> |
| | | 82 | | /// <typeparam name="TKey">The type of the key.</typeparam> |
| | | 83 | | /// <returns>The matching entities.</returns> |
| | | 84 | | public IEnumerable<TEntity> FindMany<TKey>(Func<TEntity, bool> predicate, Func<TEntity, TKey> orderBy, OrderDirectio |
| | | 85 | | { |
| | 0 | 86 | | var query = Entities.Values.Where(predicate); |
| | | 87 | | |
| | 0 | 88 | | query = orderDirection switch |
| | 0 | 89 | | { |
| | 0 | 90 | | OrderDirection.Ascending => query.OrderBy(orderBy), |
| | 0 | 91 | | OrderDirection.Descending => query.OrderByDescending(orderBy), |
| | 0 | 92 | | _ => query.OrderBy(orderBy) |
| | 0 | 93 | | }; |
| | | 94 | | |
| | 0 | 95 | | return query; |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | /// <summary> |
| | | 99 | | /// Lists all entities. |
| | | 100 | | /// </summary> |
| | | 101 | | /// <returns>All entities.</returns> |
| | 0 | 102 | | public IEnumerable<TEntity> List() => Entities.Values; |
| | | 103 | | |
| | | 104 | | /// <summary> |
| | | 105 | | /// Deletes an entity by ID. |
| | | 106 | | /// </summary> |
| | | 107 | | /// <param name="id">The ID of the entity to delete.</param> |
| | | 108 | | /// <returns>True if the entity was deleted, otherwise false.</returns> |
| | 0 | 109 | | public bool Delete(string id) => Entities.Remove(id); |
| | | 110 | | |
| | | 111 | | /// <summary> |
| | | 112 | | /// Deletes all entities matching the specified predicate. |
| | | 113 | | /// </summary> |
| | | 114 | | /// <param name="predicate">The predicate to match.</param> |
| | | 115 | | /// <returns>The number of entities deleted.</returns> |
| | | 116 | | public long DeleteWhere(Func<TEntity, bool> predicate) |
| | | 117 | | { |
| | 0 | 118 | | var query = |
| | 0 | 119 | | from entry in Entities |
| | 0 | 120 | | where predicate(entry.Value) |
| | 0 | 121 | | select entry; |
| | | 122 | | |
| | 0 | 123 | | var entries = query.ToList(); |
| | 0 | 124 | | foreach (var entry in entries) |
| | 0 | 125 | | Entities.Remove(entry); |
| | | 126 | | |
| | 0 | 127 | | return entries.Count; |
| | | 128 | | } |
| | | 129 | | |
| | | 130 | | /// <summary> |
| | | 131 | | /// Deletes all entities matching the specified IDs. |
| | | 132 | | /// </summary> |
| | | 133 | | /// <param name="ids">The IDs of the entities to delete.</param> |
| | | 134 | | /// <returns>The number of entities deleted.</returns> |
| | | 135 | | public long DeleteMany(IEnumerable<string> ids) |
| | | 136 | | { |
| | 0 | 137 | | var count = 0; |
| | 0 | 138 | | foreach (var id in ids) |
| | | 139 | | { |
| | 0 | 140 | | count++; |
| | 0 | 141 | | Entities.Remove(id); |
| | | 142 | | } |
| | | 143 | | |
| | 0 | 144 | | return count; |
| | | 145 | | } |
| | | 146 | | |
| | | 147 | | /// <summary> |
| | | 148 | | /// Deletes the specified entities. |
| | | 149 | | /// </summary> |
| | | 150 | | /// <param name="entities">The entities to delete.</param> |
| | | 151 | | /// <param name="idAccessor">A function that returns the ID of the entity.</param> |
| | | 152 | | /// <returns></returns> |
| | | 153 | | public long DeleteMany(IEnumerable<TEntity> entities, Func<TEntity, string> idAccessor) |
| | | 154 | | { |
| | 0 | 155 | | var count = 0; |
| | 0 | 156 | | var list = entities.ToList(); |
| | | 157 | | |
| | 0 | 158 | | foreach (var entity in list) |
| | | 159 | | { |
| | 0 | 160 | | count++; |
| | 0 | 161 | | var id = idAccessor(entity); |
| | 0 | 162 | | Entities.Remove(id); |
| | | 163 | | } |
| | | 164 | | |
| | 0 | 165 | | return count; |
| | | 166 | | } |
| | | 167 | | |
| | | 168 | | /// <summary> |
| | | 169 | | /// Returns a queryable of all entities. |
| | | 170 | | /// </summary> |
| | | 171 | | /// <param name="query">A function that returns a queryable.</param> |
| | | 172 | | /// <returns>A queryable of all entities.</returns> |
| | | 173 | | public IEnumerable<TEntity> Query(Func<IQueryable<TEntity>, IQueryable<TEntity>> query) |
| | | 174 | | { |
| | 0 | 175 | | var queryable = Entities.Values.AsQueryable(); |
| | 0 | 176 | | return query(queryable); |
| | | 177 | | } |
| | | 178 | | |
| | | 179 | | /// <summary> |
| | | 180 | | /// Returns true if any entity matches the specified predicate. |
| | | 181 | | /// </summary> |
| | | 182 | | /// <param name="predicate">The predicate to match.</param> |
| | | 183 | | /// <returns>True if any entity matches the specified predicate, otherwise false.</returns> |
| | 0 | 184 | | public bool Any(Func<TEntity, bool> predicate) => Entities.Values.Any(predicate); |
| | | 185 | | |
| | | 186 | | /// <summary> |
| | | 187 | | /// Returns the number of entities matching the specified predicate. |
| | | 188 | | /// </summary> |
| | | 189 | | /// <param name="predicate">The predicate to match.</param> |
| | | 190 | | /// <param name="propertySelector">The property to distinct by.</param> |
| | | 191 | | /// <returns>True if any entity matches the specified predicate, otherwise false.</returns> |
| | | 192 | | public long Count<TProperty>(Func<TEntity, bool> predicate, Func<TEntity, TProperty> propertySelector) |
| | | 193 | | { |
| | 0 | 194 | | return Entities.Values |
| | 0 | 195 | | .DistinctBy(propertySelector) |
| | 0 | 196 | | .Count(predicate); |
| | | 197 | | } |
| | | 198 | | } |