How to set up an in-memory repository

12,134

Solution 1

Basics are simple:

public class InMemoryRepository : IRepository
{
    private readonly IList<object> entities = new List<object>();

    public T Get<T>(object id)
    {
        return entities.OfType<T>.SingleOrDefault(e => e.ID == id);
    }

    public IList<T> GetAll<T>()
    {
        return entities.OfType<T>.ToList();
    }

    public IQueryable<T> Query<T>()
    {
        return GetAll<T>.AsQueryable();
    }
}

However, as soon as it comes to public IList<T> GetAll<T>(string queryName), things get complicated.

Potentially you can resort to an SQLite-based repository implementation for your tests.

Solution 2

I would go with NHibernate configured for in-memory SqlLite database. You can test then your real code and be sure that everything works correct. Writing mock for Repository can be hard and if you change IRepository interface you will have to reimplement you InMemoryRepository.

For me one of big benefits of having NHibernate is the possibility for using in memory database for testing.

Solution 3

With Anton's answer I was able to fix my own InMemoryRepository. I have modified it to match the class in the question:

private readonly ConcurrentDictionary<Type, List<object>> ObjectList = new ConcurrentDictionary<Type, List<object>>();

public int Add<T>(T obj) where T : IIdentifier
{
    // instantiate if list does not exist for this object type
    if (!ObjectList.ContainsKey(typeof (T)))
        ObjectList[typeof(T)] = new List<object>();

    // get id
    var id = GetId<T>() + 1;

    // add object to list
    obj.Id = id;
    ObjectList[typeof(T)].Add(obj);

    return id;
}

public void Attach<T>(T obj) {
    // do not need to do anything
}

public T Get<T>(int id) where T : class, IIdentifier
{
    // check list exist
    if (!ObjectList.ContainsKey(typeof (T)))
        return null;

    return ObjectList[typeof(T)].OfType<T>().FirstOrDefault(n => n.Id == id);
}

public List<T> GetAll<T>(Func<T, bool> predicate) where T : new()
{
    // check list exist
    if (!ObjectList.ContainsKey(typeof(T)))
        return null;

    return ObjectList[typeof(T)].OfType<T>().Where(predicate).ToList();
}

public List<T> GetAll<T>()
{
    return ObjectList[typeof(T)].OfType<T>.ToList();
}

public IQueryable<T> Query<T>()
{
    return GetAll<T>.AsQueryable();
}

public int Remove<T>(int id) where T : IIdentifier
{
    // check list exist
    if (!ObjectList.ContainsKey(typeof(T)))
        return 0;

    // find object with matching id
    for (var i = 0; i < ObjectList[typeof(T)].Count; i++)
        if (ObjectList[typeof(T)].OfType<T>().ToList()[i].Id == id)
        {
            ObjectList[typeof(T)].RemoveAt(i);
            return id;
        }

    // object not found
    return 0;
}

public int Save<T>(T obj) where T : IIdentifier
{
    // check list exist
    if (!ObjectList.ContainsKey(typeof(T)))
        return 0;

    // find object with matching id
    for (var i = 0; i < ObjectList[typeof(T)].Count; i++)
        if (ObjectList[typeof(T)].OfType<T>().ToList()[i].Id == obj.Id)
        {
            ObjectList[typeof (T)][i] = obj;
            return obj.Id;
        }

    // object not found
    return 0;
}

#region Helper methods

private int GetId<T>() where T : IIdentifier
{
    return ObjectList[typeof(T)].Count == 0 ? 0 : ObjectList[typeof(T)].OfType<T>().Last().Id;
}

#endregion

Solution 4

Here is an implementation of fake repository based on DbSet, including find by primary keys:

http://refactorthis.wordpress.com/2011/11/30/generic-repository-fake-idbset-implementation-update-find-method-identity-key/

Share:
12,134
Chris
Author by

Chris

Chris is a network engineer turned web developer, and now fulfills multiple duties for a small interactive development company just outside of St. Louis, MO. He current works in (and prefers) .NET development, but also dabbles in Flash, Silverlight, and other RIA technologies.

Updated on June 22, 2022

Comments

  • Chris
    Chris almost 2 years

    I have the following class:

    public class InMemoryRepository : IRepository
    {
        public void Add(object entity)
        {
            throw new NotImplementedException();
        }
    
        public void Attach(object Entity)
        {
            throw new NotImplementedException();
        }
    
        public T Get<T>(object id)
        {
            throw new NotImplementedException();
        }
    
        public IList<T> GetAll<T>(string queryName)
        {
            throw new NotImplementedException();
        }
    
        public IList<T> GetAll<T>()
        {
            throw new NotImplementedException();
        }
    
        public IQueryable<T> Query<T>()
        {
            throw new NotImplementedException();
        }
    
        public void Remove(object entity)
        {
            throw new NotImplementedException();
        }
    
        public void Save(object entity)
        {
            throw new NotImplementedException();
        }
    }
    

    Our default repository implementation uses NHibernate for the backing store, but I'd like to implement an in-memory version of it so I can prototype the domain objects without having to create a backing SQL database. Assuming the convention that all objects have an Id property as the primary key, how would you implement a generic memory store for this?

    Some key points I'm having a hard time addressing:

    • The repository methods themselves are generic, so I need some mechanism for automatically storing and referencing different types. Get<TestEntity>(object id) should be able to query all stored instances of TestEntity and find the one with the matching Id property, but I can't define a collection of TestEntity objects directly, as the repository won't know what types I'm feeding it until runtime.
    • I need to support LINQ to Objects for the Query() method. Assuming I can come up with a decent way to store the objects, this should be as simple as returning an array of stored objects AsQueryable().

    How would you store the objects to meet the above requirements?

    • Kieren Johnstone
      Kieren Johnstone about 13 years
      GetAll<T>(queryName) - can you give more information about that? What about the objects themselves - do they implement some way of uniquely identifying them? Is there a hierarchy / relationships / graph to think about?
    • Chris
      Chris about 13 years
      For the purposes of this class, ignore that method. That method exists for running a stored procedure ("named query" in NHibernate terms) that returns the result, rather than querying a table directly.
  • jrummell
    jrummell about 13 years
    The only change I would make is to use an IDictionary<object, object> instead of IList<object> for better performance.
  • ryber
    ryber about 13 years
    You also need to watch how the repository is created and used. Particularly for ac tests he may need to either make sure everyone is using the same instance of InMemoryRepository (via IOC perhaps). Or the entities field would need to be static.
  • ryber
    ryber about 13 years
    you have to make sure all your field types work across both types. Sometimes they don't. If he is using MySQL for DB it has it's own memory type that can be even easier to use than SQLLite
  • Rafal Spacjer
    Rafal Spacjer about 13 years
    Yes, you're right that there can be a problem with fields types. I haven't know about in memory MySQL db, but if there is such option then it sounds very good for me.