I tried below Repository Pattern implementation
interface IRepository<T>
{
IQueryable<T> All { get; }
T Find(object id);
void Insert(T model);
}
Then i have IAdminRepository defined below
interface IAdminRepository : IRpository<Role>, IRepository<User>
{
}
public class AdminRepository:IAdminRepository
{
IQueryable<User> IRepository<User>.All
{
get { throw new NotImplementedException(); }
}
User IRepository<User>.Find(object id)
{
throw new NotImplementedException();
}
void IRepository<User>.Insert(User model)
{
throw new NotImplementedException();
}
IQueryable<Role> IRepository<Role>.All
{
get { throw new NotImplementedException(); }
}
Role IRepository<Role>.Find(object id)
{
throw new NotImplementedException();
}
void IRepository<Role>.Insert(Role model)
{
throw new NotImplementedException();
}
}
In my business layer i use Interface based calling.
public interface IAdminService
{
bool CreateUser(User user);
List<User> GetAllUsers();
}
public class AdminService : IAdminService
{
private readonly IAdminRepository AdminRepository;
public AdminService(IAdminRepository _adminRepository)
{
AdminRepository = _adminRepository;
}
public bool CreateUser(User user)
{
AdminRepository.Insert(user);
return true;
}
public List<User> GetAllUsers()
{
return AdminRepository.All; // Here is error
}
}
Error: Ambiguity between IRepository.All &
IRepository.All.
How to resolve this? What is the problem with my approach of using Repository Pattern in this way?
I guess this line
return AdminRepository.All; // Here is error
should be
return ((IRepository<User>)AdminRepository).All.ToList();
You probably noticed that you would not have been able to declare .All without explicitly writing which interface you were implementing. That's because, for a given class, two properties with the same name can not have different return types.
It is the same when invoking. You have to tell exactly which property you are invoking. This is done by casting the object to the desired interface.
Anyway, it seems you will end-up implementing the repositories for all of your entity types. You should just implement IRepository<T> once for entity types which can be retrieved from the same mechanism.
If you want your repository to apply only on some classes, you may, for example, tag these classes with an interface. Let's say IEntity.
public interface IEntity
{
}
then
public interface IRepository<T> where T:IEntity
{
IQueryable<T> All { get; }
T Find(object id);
void Insert(T model);
}
You can even have db repositories which only apply to entities you would have tagged as being db entities, like this :
public interface IDbEntity: IEntity
{
}
public class DbRepository<T> : IRepository<T> where T:IDbEntity
{
public IQueryable<T> All { get; private set; }
public T Find(object id)
{
throw new NotImplementedException();
}
public void Insert(T model)
{
throw new NotImplementedException();
}
}
A simple way to disambiguate the call is to create aliasing methods:
public class AdminRepository : IAdminRepository {
public IQueryable<User> AllUsers {
get { throw new NotImplementedException(); }
}
public IQueryable<Role> AllRoles {
get { throw new NotImplementedException(); }
}
IQueryable<User> IRepository<User>.All {
get { return AllUsers; }
}
IQueryable<Role> IRepository<Role>.All {
get { return AllRoles; }
}
...
}
Related
I want to write an interface which allows freedom in the implementation. Basically I don't know the return types neither param types of the methods. I just want derived classes to implement this contract with same method names and param numbers.
So I can do like this:
public interface IImageRecognitionEngine<TFoo0, TFoo1, TFoo2, TFoo3>
{
TFoo0 Learn(TFoo1 param);
TFoo2 Recognize(TFoo3 param);
}
public class FooImageRecognitionEngine : IImageRecognitionEngine<byte[], string, List<double>, string>
{
public byte[] Learn(string param)
{
throw new NotImplementedException();
}
public List<double> Recognize(string param)
{
throw new NotImplementedException();
}
}
But I would much more prefer generic methods instead of the whole interface. But I don't understand why I can do this:
public interface IImageRecognitionEngine2
{
TFoo0 Learn<TFoo0, TFoo1>(TFoo1 param);
TFoo2 Recognize<TFoo2, TFoo3>(TFoo3 param);
}
public class FooExampleClass
{
}
public class FooExampleClass2
{
}
public class Foo1ImageRecognitionEngine2 : IImageRecognitionEngine2
{
public FooExampleClass Learn<FooExampleClass, FooExampleClass2>(FooExampleClass2 param)
{
throw new NotImplementedException();
}
public FooExampleClass Recognize<FooExampleClass, FooExampleClass2>(FooExampleClass2 param)
{
throw new NotImplementedException();
}
}
But with primitive types the compiler gives me errors:
public class Foo2ImageRecognitionEngine2 : IImageRecognitionEngine2
{
public byte[] Learn<byte[], string>(string param)
{
throw new NotImplementedException();
}
public List<double> Recognize<List<double>, string>(string param)
{
throw new NotImplementedException();
}
}
I don't want to be able to chose what types to use when I instantiate an object of the class implementation. For example, I don't want to write an implementation like this:
public class Foo2ImageRecognitionEngine2 : IImageRecognitionEngine2
{
public TFoo0 Learn<TFoo0, TFoo1>(TFoo1 param)
{
throw new NotImplementedException();
}
public TFoo2 Recognize<TFoo2, TFoo3>(TFoo3 param)
{
throw new NotImplementedException();
}
}
And being able to do this:
var fooEngine = new Foo2ImageRecognitionEngine2();
fooEngine.Learn<string, int>(52);
And this doesn't work also:
public interface IImageRecognitionEngine3
{
object Learn(object param);
object Recognize(object param);
}
public class Foo1ImageRecognitionEngine3 : IImageRecognitionEngine3
{
public byte[] Learn(string param)
{
throw new NotImplementedException();
}
public List<double> Recognize(string param)
{
throw new NotImplementedException();
}
}
Thank you
public FooExampleClass Learn<FooExampleClass, FooExampleClass2>(FooExampleClass2 param)
{
throw new NotImplementedException();
}
does not do what you think - FooExampleClass and FooExampleClass2 are not types, but type parameters which can be instantiated to any class. This means a client could do:
var e = new Foo1ImageRecognitionEngine2();
e.Learn<string, object>(new object());
Your code only appears to work because it does nothing.
Generic methods like this have to work for any parameters the client chooses so there's no way to restrict the parameters to work for particular parameters. If you want to do that you need to move them to the interface definition which you have in your first example.
I have an interface that is implemented by all of my classes which are responsible for exporting data to different formats.
Sample code:
public interface IExport
{
string Exporter();
}
public class ExcelExport : IExport
{
public string Exporter()
{
return "excel";
}
}
public class PdfExport : IExport
{
public string Exporter()
{
return "pdf";
}
}
I want to get a specific type in runtime, so I know I have to use abstract factory, but don't I know how tp in this example.
Exports are handled by the manager class:
public interface IExportManager
{
IExport GetExportProvider(ExportType type);
}
public interface IExportFactory
{
IExport CreateExport(ExportType type);
}
public class ExportManager : IExportManager
{
private IExportFactory exportFactory;
public ExportManager(IExportFactory exportFactory)
{
this.exportFactory = exportFactory;
}
public IExport GetExportProvider(ExportType type)
{
return exportFactory.CreateExport(type);
}
}
public enum ExportType
{
PDF,
XLSX
}
How can I get the right object instance depending on the type parameter using the GetExportProvider methods?
This is my Ninject module:
public class NinModule : NinjectModule
{
public override void Load()
{
this.Bind<IExportFactory>().ToFactory();
this.Bind<IExportManager>().To<ExportManager>();
this.Bind<IExport>().To<ExcelExport>();//.WhenInjectedInto<IExportManager>().WithPropertyValue("type", ExportType.XLSX);
this.Bind<IExport>().To<PdfExport>();//.WhenInjectedInto<IExportManager>().WithPropertyValue("type", ExportType.PDF);
}
}
And the code used to test it:
static void Main(string[] args)
{
IKernel k = new StandardKernel(new NinModule());
IExportManager r = k.Get<IExportManager>();
var pdf = r.GetExportProvider(ExportType.PDF);
Console.WriteLine(pdf.Exporter());
Console.Read();
}
Thanks in advance for your help.
I don't know anything about Ninject, but strictly from a C# perspective, why wouldn't this work...
public class ExportFactory : IExportFactory
{
public IExport CreateExport(ExportType type)
{
switch(type)
{
case ExportType.PDF:
return new PdfExport();
case ExportType.XLSX
return new ExcelExport();
}
}
}
I think you need to create dynamic binding in ExportFactory class.
Something like this:
if (exportType == ExportType.PDF)
{
Bind<IExport>().To<PdfExport>().InRequestScope();
}
else if (exportType == ExportType.XLSX)
{
Bind<IExport>().To<ExcelExport>().InRequestScope();
}
Alright, finally I got the solution.
One possibility is to create a custom provider:
public class ExportProvider : Provider<IExport>
{
protected override IExport CreateInstance(IContext context)
{
ExportType type = (ExportType)context.Parameters.First().GetValue(context, null);
switch (type)
{
case ExportType.PDF: return new PdfExport();
case ExportType.XLSX: return new ExcelExport();
}
return null;
}
}
And config: this.Bind<IExport>().ToProvider<ExportProvider>();
Second possibility I have found:
this.Bind<IExport>().To<ExcelExport>().When((q) =>
{
ExportType type = (ExportType)q.Parameters.First().GetValue(new DummyContext(), null);
return type == ExportType.XLSX;
});
this.Bind<IExport>().To<PdfExport>().When((q) =>
{
ExportType type = (ExportType)q.Parameters.First().GetValue(new DummyContext(), null);
return type == ExportType.PDF;
});
The GetValue of IParameter requires not null IContext provided. In the When clause we can't get any, but we can use diffrent one, because this parameter is not checked in the method as it look like. So we can create a simple class that implements the IContext interface:
public class DummyContext : IContext
{
public Ninject.Planning.Bindings.IBinding Binding
{
get { throw new NotImplementedException(); }
}
public Type[] GenericArguments
{
get { throw new NotImplementedException(); }
}
public IProvider GetProvider()
{
throw new NotImplementedException();
}
public object GetScope()
{
throw new NotImplementedException();
}
public bool HasInferredGenericArguments
{
get { throw new NotImplementedException(); }
}
public IKernel Kernel
{
get { throw new NotImplementedException(); }
}
public ICollection<IParameter> Parameters
{
get { throw new NotImplementedException(); }
}
public Ninject.Planning.IPlan Plan
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public IRequest Request
{
get { throw new NotImplementedException(); }
}
public object Resolve()
{
throw new NotImplementedException();
}
}
I'm trying to create a unit test for my service with a mocked DbContext. I created an interface IDbContext with the following functions:
public interface IDbContext : IDisposable
{
IDbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
int SaveChanges();
}
My real context implements this interface IDbContext and DbContext.
Now I'm trying to mock the IDbSet<T> in the context, so it returns a List<User> instead.
[TestMethod]
public void TestGetAllUsers()
{
// Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new List<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
I always get this error on .Returns:
The best overloaded method match for
'Moq.Language.IReturns<AuthAPI.Repositories.IDbContext,System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>.Returns(System.Func<System.Data.Entity.IDbSet<AuthAPI.Models.Entities.User>>)'
has some invalid arguments
I managed to solve it by creating a FakeDbSet<T> class that implements IDbSet<T>
public class FakeDbSet<T> : IDbSet<T> where T : class
{
ObservableCollection<T> _data;
IQueryable _query;
public FakeDbSet()
{
_data = new ObservableCollection<T>();
_query = _data.AsQueryable();
}
public virtual T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public T Add(T item)
{
_data.Add(item);
return item;
}
public T Remove(T item)
{
_data.Remove(item);
return item;
}
public T Attach(T item)
{
_data.Add(item);
return item;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public ObservableCollection<T> Local
{
get { return _data; }
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
Now my test looks like this:
[TestMethod]
public void TestGetAllUsers()
{
//Arrange
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<User>())
.Returns(new FakeDbSet<User>
{
new User { ID = 1 }
});
UserService userService = new UserService(mock.Object);
// Act
var allUsers = userService.GetAllUsers();
// Assert
Assert.AreEqual(1, allUsers.Count());
}
In case anyone is still interested, I was having the same problem and found this article very helpful:
Entity Framework Testing with a Mocking Framework (EF6 onwards)
It only applies to Entity Framework 6 or newer, but it covers everything from simple SaveChanges tests to async query testing all using Moq (and a few of manual classes).
Thank you Gaui for your great idea =)
I did add some improvements to your solution and want to share it.
My FakeDbSet also inherents from DbSet to get additional methods
like AddRange()
I replaced the ObservableCollection<T> with List<T> to pass all
the already implemented methods in List<> up to my FakeDbSet
My FakeDbSet:
public class FakeDbSet<T> : DbSet<T>, IDbSet<T> where T : class {
List<T> _data;
public FakeDbSet() {
_data = new List<T>();
}
public override T Find(params object[] keyValues) {
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public override T Add(T item) {
_data.Add(item);
return item;
}
public override T Remove(T item) {
_data.Remove(item);
return item;
}
public override T Attach(T item) {
return null;
}
public T Detach(T item) {
_data.Remove(item);
return item;
}
public override T Create() {
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T {
return Activator.CreateInstance<TDerivedEntity>();
}
public List<T> Local {
get { return _data; }
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities) {
_data.AddRange(entities);
return _data;
}
public override IEnumerable<T> RemoveRange(IEnumerable<T> entities) {
for (int i = entities.Count() - 1; i >= 0; i--) {
T entity = entities.ElementAt(i);
if (_data.Contains(entity)) {
Remove(entity);
}
}
return this;
}
Type IQueryable.ElementType {
get { return _data.AsQueryable().ElementType; }
}
Expression IQueryable.Expression {
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider {
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator() {
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() {
return _data.GetEnumerator();
}
}
It is very easy to modify the dbSet and Mock the EF Context Object:
var userDbSet = new FakeDbSet<User>();
userDbSet.Add(new User());
userDbSet.Add(new User());
var contextMock = new Mock<MySuperCoolDbContext>();
contextMock.Setup(dbContext => dbContext.Users).Returns(userDbSet);
Now it is possible to execute Linq queries, but be a aware that foreign key references may not be created automatically:
var user = contextMock.Object.Users.SingeOrDefault(userItem => userItem.Id == 42);
Because the context object is mocked the Context.SaveChanges() won't do anything and property changes of your entites might not be populated to your dbSet. I solved this by mocking my SetModifed() method to populate the changes.
Based on this MSDN article, I've created my own libraries for mocking DbContext and DbSet:
EntityFrameworkMock - GitHub
EntityFrameworkMockCore - GitHub
Both available on NuGet and GitHub.
The reason I've created these libraries is because I wanted to emulate the SaveChanges behavior, throw a DbUpdateException when inserting models with the same primary key and support multi-column/auto-increment primary keys in the models.
In addition, since both DbSetMock and DbContextMock inherit from Mock<DbSet> and Mock<DbContext>, you can use all features of the Moq framework.
Next to Moq, there also is an NSubstitute implementation.
Usage with the Moq version looks like this:
public class User
{
[Key, Column(Order = 0)]
public Guid Id { get; set; }
public string FullName { get; set; }
}
public class TestDbContext : DbContext
{
public TestDbContext(string connectionString)
: base(connectionString)
{
}
public virtual DbSet<User> Users { get; set; }
}
[TestFixture]
public class MyTests
{
var initialEntities = new[]
{
new User { Id = Guid.NewGuid(), FullName = "Eric Cartoon" },
new User { Id = Guid.NewGuid(), FullName = "Billy Jewel" },
};
var dbContextMock = new DbContextMock<TestDbContext>("fake connectionstring");
var usersDbSetMock = dbContextMock.CreateDbSetMock(x => x.Users, initialEntities);
// Pass dbContextMock.Object to the class/method you want to test
// Query dbContextMock.Object.Users to see if certain users were added or removed
// or use Mock Verify functionality to verify if certain methods were called: usersDbSetMock.Verify(x => x.Add(...), Times.Once);
}
If anyone is still looking for answers I've implemented a small library to allow mocking DbContext.
step 1
Install Coderful.EntityFramework.Testing nuget package:
Install-Package Coderful.EntityFramework.Testing
step 2
Then create a class like this:
internal static class MyMoqUtilities
{
public static MockedDbContext<MyDbContext> MockDbContext(
IList<Contract> contracts = null,
IList<User> users = null)
{
var mockContext = new Mock<MyDbContext>();
// Create the DbSet objects.
var dbSets = new object[]
{
MoqUtilities.MockDbSet(contracts, (objects, contract) => contract.ContractId == (int)objects[0] && contract.AmendmentId == (int)objects[1]),
MoqUtilities.MockDbSet(users, (objects, user) => user.Id == (int)objects[0])
};
return new MockedDbContext<SourcingDbContext>(mockContext, dbSets);
}
}
step 3
Now you can create mocks super easily:
// Create test data.
var contracts = new List<Contract>
{
new Contract("#1"),
new Contract("#2")
};
var users = new List<User>
{
new User("John"),
new User("Jane")
};
// Create DbContext with the predefined test data.
var dbContext = MyMoqUtilities.MockDbContext(
contracts: contracts,
users: users).DbContext.Object;
And then use your mock:
// Create.
var newUser = dbContext.Users.Create();
// Add.
dbContext.Users.Add(newUser);
// Remove.
dbContext.Users.Remove(someUser);
// Query.
var john = dbContext.Users.Where(u => u.Name == "John");
// Save changes won't actually do anything, since all the data is kept in memory.
// This should be ideal for unit-testing purposes.
dbContext.SaveChanges();
Full article: http://www.22bugs.co/post/Mocking-DbContext/
I'm late, but found this article helpful: Testing with InMemory (MSDN Docs).
It explains how to use an in memory DB context (which is not a database) with the benefit of very little coding and the opportunity to actually test your DBContext implementation.
I am pretty new to Generic class in C#. I was trying to create one and ran into compiler error that I am not sure how to get around it.
Basically, I have a class G that implements ICollection
public class G<T> : ICollection<T> where T : IEqualityComparer
{
private ArrayList _members = new ArrayList();
public void Add(T item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
throw new NotImplementedException();
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { throw new NotImplementedException(); }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in _members)
{
yield return (T)item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
I wanted to be able to do comparison in G and Find item in G, so I have put a constraint that T has to be implementing IEqualityComparer. Then, I have an actual class called IntegerClass that implement IEqualityComparer as below. So far, so good, no compiler error.
public class IntegerClass : IEqualityComparer<int>
{
public bool Equals(int x, int y)
{
throw new NotImplementedException();
}
public int GetHashCode(int obj)
{
throw new NotImplementedException();
}
}
However, when I tried to create an instance of G above. I got a compiler error.
class Program
{
static void Main(string[] args)
{
G<IntegerClass> i = new G<IntegerClass>();
}
}
The error is:
The type 'TestGeneric.IntegerClass' cannot be used as type parameter 'T' in the generic type or method 'TestGeneric.G<T>'.
There is no implicit reference conversion from 'TestGeneric.IntegerClass' to 'System.Collections.IEqualityComparer'
Could someone pinpoint what I have overlooked? Why would I need conversion? All I did was replacing class T with IntegerClass that implements IEqualityComparer interface. What should I do otherwise? I am new to this generic stuff, but have found it quite useful. I am thinking it could be very useful if I understand it correctly. Please help.
Update:
Based on some suggestion, I saw what was wrong and I updated the code as follow:
public class IntegerClass : IEqualityComparer
{
public bool Equals(object x, object y)
{
throw new NotImplementedException();
}
public int GetHashCode(object obj)
{
throw new NotImplementedException();
}
}
public class G<T> : ICollection<T> where T : IEqualityComparer
{
private ArrayList _members = new ArrayList();
public void Add(T item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
throw new NotImplementedException();
}
public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public int Count
{
get { throw new NotImplementedException(); }
}
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in _members)
{
yield return (T)item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
I think it might work but I got the warning below:
'TestGeneric.IntegerClass.Equals(object, object)' hides inherited member 'object.Equals(object, object)'. Use the new keyword if hiding was intended.
I know object has an Equals methods but the warning does not make sense. Should it say use the new keyword if hiding was NOT intended?
Your constraint refers to the non-generic System.Collections.IEqualityComparer interface, which is not the same as IEqualityComparer<T>.
You could fix that error by specify the generic type in the constraint.
However, that's not what you want; an IEqualityComparer is a class that compares other things.
You want where T : IEquatable<T>.
change your IntegerClass like below.
public class IntegerClass : IEqualityComparer<IntegerClass>
{
public bool Equals(IntegerClass IC1, IntegerClass IC2)
{
throw new NotImplementedException();
}
public int GetHashCode(IntegerClass obj)
{
throw new NotImplementedException();
}
}
Or you could resort it to the fact that Objects has an equality function.
Hence, this would work
public interface IMyComparer
{
object Comparer { get; }
}
public class IntegerClass : IMyComparer, IEqualityComparer<int>
{
public object Comparer { get { return this; } }
public bool Equals(int x, int y)
{
throw new NotImplementedException();
}
public int GetHashCode(int obj)
{
throw new NotImplementedException();
}
}
public class G<T> : ICollection<T> where T : IMyComparer
{
Your implementations
}
Hope it helps.
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?
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.
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.
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
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/