< Summary

Information
Class: Elsa.Common.Services.MemoryStore<T>
Assembly: Elsa.Common
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Common/Services/MemoryStore.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 50
Coverable lines: 50
Total lines: 205
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 14
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Entities()100%210%
get_Sync()100%210%
get_Queryable()100%210%
Add(...)100%210%
AddMany(...)0%620%
Save(...)100%210%
SaveMany(...)0%620%
Update(...)100%210%
Find(...)100%210%
FindMany(...)100%210%
FindMany(...)0%2040%
List()100%210%
Delete(...)100%210%
DeleteWhere(...)0%620%
DeleteMany(...)0%620%
DeleteMany(...)0%620%
Query(...)100%210%
Any(...)100%210%
Count(...)100%210%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Common/Services/MemoryStore.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using Elsa.Common.Entities;
 3
 4namespace 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>
 10public class MemoryStore<TEntity>
 11{
 012    private IDictionary<string, TEntity> Entities { get; set; } = new ConcurrentDictionary<string, TEntity>();
 13
 14    /// <summary>
 15    /// One lock for every scoped wrapper that shares this store. Compare-and-swap
 16    /// load/match/write and the other Save/Delete paths take it so two scopes cannot
 17    /// race on the same in-memory set.
 18    /// </summary>
 019    public object Sync { get; } = new();
 20
 21    /// <summary>
 22    /// Gets a queryable of all entities.
 23    /// </summary>
 024    public IQueryable<TEntity> Queryable => Entities.Values.AsQueryable();
 25
 26    /// <summary>
 27    /// Adds an entity.
 28    /// </summary>
 29    /// <param name="entity">The entity to add.</param>
 30    /// <param name="idAccessor">A function that returns the ID of the entity.</param>
 031    public void Add(TEntity entity, Func<TEntity, string> idAccessor) => Entities.Add(idAccessor(entity), entity);
 32
 33    /// <summary>
 34    /// Adds many entities.
 35    /// </summary>
 36    /// <param name="entities">The entities to add.</param>
 37    /// <param name="idAccessor">A function that returns the ID of the entity.</param>
 38    public void AddMany(IEnumerable<TEntity> entities, Func<TEntity, string> idAccessor)
 39    {
 040        foreach (var entity in entities)
 041            Add(entity, idAccessor);
 042    }
 43
 44    /// <summary>
 45    /// Adds or updates an entity.
 46    /// </summary>
 47    /// <param name="entity">The entity to add or update.</param>
 48    /// <param name="idAccessor">A function that returns the ID of the entity.</param>
 049    public void Save(TEntity entity, Func<TEntity, string> idAccessor) => Entities[idAccessor(entity)] = entity;
 50
 51    /// <summary>
 52    /// Adds or updates many entities.
 53    /// </summary>
 54    /// <param name="entities">The entities to add or update.</param>
 55    /// <param name="idAccessor">A function that returns the ID of the entity.</param>
 56    public void SaveMany(IEnumerable<TEntity> entities, Func<TEntity, string> idAccessor)
 57    {
 058        foreach (var entity in entities)
 059            Save(entity, idAccessor);
 060    }
 61
 62    /// <summary>
 63    /// Updates an entity.
 64    /// </summary>
 65    /// <param name="entity">The entity to update.</param>
 66    /// <param name="idAccessor">A function that returns the ID of the entity.</param>
 067    public void Update(TEntity entity, Func<TEntity, string> idAccessor) => Entities[idAccessor(entity)] = entity;
 68
 69    /// <summary>
 70    /// Finds an entity matching the specified predicate.
 71    /// </summary>
 72    /// <param name="predicate">A predicate to match.</param>
 73    /// <returns>The matching entity or null if no match was found.</returns>
 074    public TEntity? Find(Func<TEntity, bool> predicate) => Entities.Values.Where(predicate).FirstOrDefault();
 75
 76    /// <summary>
 77    /// Finds all entities matching the specified predicate.
 78    /// </summary>
 79    /// <param name="predicate">A predicate to match.</param>
 80    /// <returns>The matching entities.</returns>
 081    public IEnumerable<TEntity> FindMany(Func<TEntity, bool> predicate) => Entities.Values.Where(predicate);
 82
 83    /// <summary>
 84    /// Finds all entities matching the specified predicate and orders them by the specified key.
 85    /// </summary>
 86    /// <param name="predicate">The predicate to match.</param>
 87    /// <param name="orderBy">The key to order by.</param>
 88    /// <param name="orderDirection">The order direction.</param>
 89    /// <typeparam name="TKey">The type of the key.</typeparam>
 90    /// <returns>The matching entities.</returns>
 91    public IEnumerable<TEntity> FindMany<TKey>(Func<TEntity, bool> predicate, Func<TEntity, TKey> orderBy, OrderDirectio
 92    {
 093        var query = Entities.Values.Where(predicate);
 94
 095        query = orderDirection switch
 096        {
 097            OrderDirection.Ascending => query.OrderBy(orderBy),
 098            OrderDirection.Descending => query.OrderByDescending(orderBy),
 099            _ => query.OrderBy(orderBy)
 0100        };
 101
 0102        return query;
 103    }
 104
 105    /// <summary>
 106    /// Lists all entities.
 107    /// </summary>
 108    /// <returns>All entities.</returns>
 0109    public IEnumerable<TEntity> List() => Entities.Values;
 110
 111    /// <summary>
 112    /// Deletes an entity by ID.
 113    /// </summary>
 114    /// <param name="id">The ID of the entity to delete.</param>
 115    /// <returns>True if the entity was deleted, otherwise false.</returns>
 0116    public bool Delete(string id) => Entities.Remove(id);
 117
 118    /// <summary>
 119    /// Deletes all entities matching the specified predicate.
 120    /// </summary>
 121    /// <param name="predicate">The predicate to match.</param>
 122    /// <returns>The number of entities deleted.</returns>
 123    public long DeleteWhere(Func<TEntity, bool> predicate)
 124    {
 0125        var query =
 0126            from entry in Entities
 0127            where predicate(entry.Value)
 0128            select entry;
 129
 0130        var entries = query.ToList();
 0131        foreach (var entry in entries)
 0132            Entities.Remove(entry);
 133
 0134        return entries.Count;
 135    }
 136
 137    /// <summary>
 138    /// Deletes all entities matching the specified IDs.
 139    /// </summary>
 140    /// <param name="ids">The IDs of the entities to delete.</param>
 141    /// <returns>The number of entities deleted.</returns>
 142    public long DeleteMany(IEnumerable<string> ids)
 143    {
 0144        var count = 0;
 0145        foreach (var id in ids)
 146        {
 0147            count++;
 0148            Entities.Remove(id);
 149        }
 150
 0151        return count;
 152    }
 153
 154    /// <summary>
 155    /// Deletes the specified entities.
 156    /// </summary>
 157    /// <param name="entities">The entities to delete.</param>
 158    /// <param name="idAccessor">A function that returns the ID of the entity.</param>
 159    /// <returns></returns>
 160    public long DeleteMany(IEnumerable<TEntity> entities, Func<TEntity, string> idAccessor)
 161    {
 0162        var count = 0;
 0163        var list = entities.ToList();
 164
 0165        foreach (var entity in list)
 166        {
 0167            count++;
 0168            var id = idAccessor(entity);
 0169            Entities.Remove(id);
 170        }
 171
 0172        return count;
 173    }
 174
 175    /// <summary>
 176    /// Returns a queryable of all entities.
 177    /// </summary>
 178    /// <param name="query">A function that returns a queryable.</param>
 179    /// <returns>A queryable of all entities.</returns>
 180    public IEnumerable<TEntity> Query(Func<IQueryable<TEntity>, IQueryable<TEntity>> query)
 181    {
 0182        var queryable = Entities.Values.AsQueryable();
 0183        return query(queryable);
 184    }
 185
 186    /// <summary>
 187    /// Returns true if any entity matches the specified predicate.
 188    /// </summary>
 189    /// <param name="predicate">The predicate to match.</param>
 190    /// <returns>True if any entity matches the specified predicate, otherwise false.</returns>
 0191    public bool Any(Func<TEntity, bool> predicate) => Entities.Values.Any(predicate);
 192
 193    /// <summary>
 194    /// Returns the number of entities matching the specified predicate.
 195    /// </summary>
 196    /// <param name="predicate">The predicate to match.</param>
 197    /// <param name="propertySelector">The property to distinct by.</param>
 198    /// <returns>True if any entity matches the specified predicate, otherwise false.</returns>
 199    public long Count<TProperty>(Func<TEntity, bool> predicate, Func<TEntity, TProperty> propertySelector)
 200    {
 0201        return Entities.Values
 0202            .DistinctBy(propertySelector)
 0203            .Count(predicate);
 204    }
 205}