In memory data access for CSV files that may have changed I'm reading data from CSV files and store the result in a simple DataContext instance that gets injected throughout my application.

public class DataContext
{
    private static readonly IDictionary&lt;Type, IEnumerable&lt;BaseEntity&gt;&gt; EntityMap = 
        Assembly.GetExecutingAssembly().GetTypes()
        .Where(t =&gt; t.IsClass &amp;&amp; !t.IsAbstract &amp;&amp; t.IsSubclassOf(typeof(BaseEntity)))
        .ToDictionary(t =&gt; t, t =&gt; Enumerable.Empty&lt;BaseEntity&gt;());

    public IEnumerable&lt;T&gt; Get&lt;T&gt;() where T : BaseEntity
    {
        return EntityMap[typeof(T)].Cast&lt;T&gt;();
    }

    public void Set&lt;T&gt;(IEnumerable&lt;BaseEntity&gt; value) where T : BaseEntity
    {
        EntityMap[typeof(T)] = value.ToList();
    }
}


I listen for changes in a folder. As soon as a CSV file gets updated, I'll update the corresponding list. Something like this:

void OnFileChange(string fileName)
{
    // handle in a separate thread to decrease the load of FileSystemWatcher
    Task.Run(() =&gt; {
        var parsedFileData = parser.Parse&lt;DataType&gt;(fileName);
        dataContext.Set&lt;DataType&gt;(parsedFileData);
    });
}


I never do a delta update of entities. It's always a complete replacement of the collection, which is by intention.

Some thoughts:


What do you think of this in memory approach with variables vs something like Redis?
Any other comments?
It's always a good idea to try to design LINQ compatible classes. Your  DataContext can be designed this way.


first you need to un-static-fy the field, it shouldn't be static (rarely anything has to be)
implement the ILookup interface as it behaves exactly as your data-context, this is, return an empty collection if the key hasn't been found. Internally you can use a dictionary
there is no need to scan the types and fill the dictionary with empty collections


Example:

public class DataContext : ILookup&lt;Type, BaseEntity&gt;
{
    private readonly IDictionary&lt;Type, BaseEntityGroup&gt; _items = new Dictionary&lt;Type, BaseEntityGroup&gt;();

    public bool Contains(Type type) =&gt; _items.ContainsKey(type);
    public int Count =&gt; _items.Count;
    public IEnumerable&lt;BaseEntity&gt; this[Type type]
    {
        get =&gt; _items.TryGetValue(type, out BaseEntityGroup result) ? result : Enumerable.Empty&lt;BaseEntity&gt;();
        set =&gt; _items.Add(type, new BaseEntityGroup(type, value));
    }
    public IEnumerator&lt;IGrouping&lt;Type, BaseEntity&gt;&gt; GetEnumerator() =&gt; null;
    IEnumerator IEnumerable.GetEnumerator() =&gt; GetEnumerator();
}


This requries another helper class for storing the items, so let's create one. It needs to implement the IGrouping interface:

public class BaseEntityGroup : List&lt;BaseEntity&gt;, IGrouping&lt;Type, BaseEntity&gt;
{
    public BaseEntityGroup(Type type, IEnumerable&lt;BaseEntity&gt; items) : base(items) =&gt; Key = type;
    public Type Key { get; }
}


Now you can work with it like this:

var dc = new DataContext();
dc[typeof(string)] = new BaseEntity[] 
{
    new BaseEntity2(),
    new BaseEntity2()
};


but you want to use some generics? So why not create a two extensions for it:

public static DataContext Add&lt;T&gt;(this DataContext context, IEnumerable&lt;BaseEntity&gt; items) where T : BaseEntity
{
    context[typeof(T)] = items;
    return context;
}

public static IEnumerable&lt;T&gt; Get&lt;T&gt;(this DataContext context) where T : BaseEntity
{
    return context[typeof(T)].Cast&lt;T&gt;();
}


Should you now need to query your data-context you can easily do it with LINQ.