I can't get the QueryObjectGraph to add INCLUDE child tables if my life depended on it...what am I missing? Stuck for third day on something that should be simple :-/
DAL:
public abstract class RepositoryBase<T> where T : class
{
private MyLPL2Context dataContext;
private readonly IDbSet<T> dbset;
protected RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
DataContext.Configuration.LazyLoadingEnabled = true;
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected MyLPL2Context DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public IQueryable<T> QueryObjectGraph(Expression<Func<T, bool>> filter,
params string[] children)
{
foreach (var child in children)
{
dbset.Include(child);
}
return dbset.Where(filter);
}
...
DAL repositories
public interface IBreed_TranslatedSqlRepository : ISqlRepository<Breed_Translated>
{
}
public class Breed_TranslatedSqlRepository : RepositoryBase<Breed_Translated>,
IBreed_TranslatedSqlRepository
{
public Breed_TranslatedSqlRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{}
}
BLL Repo:
public IQueryable<Breed_Translated>
QueryObjectGraph(Expression<Func<Breed_Translated, bool>> filter,
params string[] children)
{
return _r.QueryObjectGraph(filter, children);
}
Controller:
var breeds1 = _breedTranslatedRepository
.QueryObjectGraph(b => b.Culture == culture, new string[] { "AnimalType_Breed" })
.ToList();
I can't get to Breed.AnimalType_Breed.AnimalTypeId ..I can drill as far as Breed.AnimalType_Breed then the intelisense expects an expression?
Cues if any, DB Tables: bold is many-to-many
Breed, Breed_Translated, AnimalType_Breed, AnimalType, ...
AnimalBreed_Type represents many to many relation so the AnimalBreed_Type property in Breed_Translated entity is collection! The collection type doesn't have your table properties. You must use First or Single to get single related entity from this collection and check it's AnimalTypeId.
If you look at Include it has a return type. Linq is mostly functional so it will NOT alter any objects but rather return new ones. You need to store this new object instead.
Try:
var query = dbset.Where(filter);
foreach (var child in children)
{
query = query.Include(child);
}
return query;
OTHER NOTE: You can simplify this:
var breeds1 = _breedTranslatedRepository
.QueryObjectGraph(b => b.Culture == culture, new string[] { "AnimalType_Breed" })
.ToList();
To
var breeds1 = _breedTranslatedRepository
.QueryObjectGraph(b => b.Culture == culture, "AnimalType_Breed")
.ToList();
When you methods takes a params string[] argument
Related
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'm not sure where to put my specific queries for business objects.
When we start using multiple table specific queries for the repository pattern, where
should these be placed? Service Layer or Repository?
For Example Please see below:
Ex:
class HR_Repository<T> : IRepository<T> where T : class
{
private readonly LoginDataContext dataContext;
public HR_Repository(LoginDataContext dataContext)
{
this.dataContext = dataContext;
}
public void Commit()
{
dataContext.SubmitChanges();
}
public IList<T> FindAll()
{
var table = this.LookupTableFor(typeof(T));
return table.Cast<T>().ToList();
}
public IQueryable<T> Find()
{
var table = this.LookupTableFor(typeof(T));
return table.Cast<T>();
}
public void Add(T item)
{
var table = this.LookupTableFor(typeof(T));
table.InsertOnSubmit(item);
}
public void Delete(T item)
{
var table = this.LookupTableFor(typeof(T));
table.DeleteOnSubmit(item);
}
private ITable LookupTableFor(Type entityType)
{
return dataContext.GetTable(entityType);
}
}
I have this class below in the repository currently. But I plan to place many more of these for other tables. This to me doesn't "feel" right.
Would multiple classes such as these be considered best practice or frowned upon?:
public static class UserQueries
{
public static Employee ByUserName(this IQueryable<Employee> employees, string username)
{
return employees.Where(u => u.User_Name == username).FirstOrDefault();
}
}
Additionally I'm planning to use another method (GetEmployeeProductivity) that essentially applies logic using data in the Employee object and data found in a separate DataRepository. So now I'm using an EmployeeRepository and a DataRepository.
Where would this go? Employee Class, Service or Repository?
Generally, your logic that makes decisions based on business rules goes in the service layer. The code that creates, updates, or deletes rows from tables (the standard CRUD functions) goes into the repository.
So if you need to retrieve data by joining multiple tables together, that's in the repository. The code that says "if this condition is met, then do this to the database" is in the service layer. If you need to add or update a row in several tables, that's still in the repository, and can either be done in one method (if the 2 tables conceptually are one, but are split into two for database efficiency reasons, like a one to many or many to many relationship), or you can use separate methods, one for each table, and just call them from one method in the service layer.
What you can also do is to encapsulate them in each class where they refer. Your second static class has the Method ByUserName who returns an Employee object. You can put this method in Employee class (The Repository: EmployeeRepository). But You have many choices. when working in a team it should be better if everything is well organized.
As there are many thoughts on design pattern compare to repository/UnitOfWork patten, here is the standard implementation of repository & business layer.
***DATA LAYER***
namespace app.data
{
public interface IGenericDataRepository<T> where T : class
{
IList<T> GetAll(params Expression<Func<T, object>>[] navigationProperties);
IList<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
T GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
void Add(params T[] items);
void Update(params T[] items);
void Remove(params T[] items);
}
}
namespace app.data
{
public class GenericDataRepository<T> : IGenericDataRepository<T> where T : class
{
public virtual IList<T> GetAll(params Expression<Func<T, object>>[] navigationProperties)
{
List<T> list;
using (var context = new GatePassEntities())
{
IQueryable<T> dbQuery = context.Set<T>();
//Apply eager loading
foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Include<T, object>(navigationProperty);
list = dbQuery
.AsNoTracking()
.ToList<T>();
}
return list;
}
public virtual IList<T> GetList(Func<T, bool> where,
params Expression<Func<T, object>>[] navigationProperties)
{
List<T> list;
using (var context = new GatePassEntities())
{
IQueryable<T> dbQuery = context.Set<T>();
//Apply eager loading
foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Include<T, object>(navigationProperty);
list = dbQuery
.AsNoTracking()
.Where(where)
.ToList<T>();
}
return list;
}
public virtual T GetSingle(Func<T, bool> where,
params Expression<Func<T, object>>[] navigationProperties)
{
T item = null;
using (var context = new GatePassEntities())
{
IQueryable<T> dbQuery = context.Set<T>();
//Apply eager loading
foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Include<T, object>(navigationProperty);
item = dbQuery
.AsNoTracking() //Don't track any changes for the selected item
.FirstOrDefault(where); //Apply where clause
}
return item;
}
public virtual void Add(params T[] items)
{
using (var context = new GatePassEntities())
{
foreach (T item in items)
{
context.Entry(item).State = EntityState.Added;
}
context.SaveChanges();
}
}
public virtual void Update(params T[] items)
{
using (var context = new GatePassEntities())
{
foreach (T item in items)
{
context.Entry(item).State = EntityState.Modified;
}
context.SaveChanges();
}
}
public virtual void Remove(params T[] items)
{
using (var context = new GatePassEntities())
{
foreach (T item in items)
{
context.Entry(item).State = EntityState.Deleted;
}
context.SaveChanges();
}
}
}
}
//Domain Models like Employee, Department can be your objectcontext, dbcontext (can be generated by EF or other ORM Tools like T4)
namespace app.data
{
public interface IEmployeeRepository : IGenericDataRepository<Employee>
{
}
public interface IDepartmentRepository : IGenericDataRepository<Department>
{
}
}
***BUSINESS LAYER***
namespace app.business
{
public interface IBusinessLayer
{
IList<Employee> GetAllEmployees();
IList<Employee> GetEmployeesByCountryName(string countryName);
Employee GetEmployeeByName(string EmployeeName);
Employee GetEmployeeByIdentityId(int identityId, string EmployeeUniqueIdentityNumber);
void AddEmployee(params Employee[] Employees);
void UpdateEmployee(params Employee[] Employees);
void RemoveEmployee(params Employee[] Employees);
}
}
public class BuinessLayer : IBusinessLayer
{
private readonly IEmployeeRepository _EmployeeRepository;
public BuinessLayer()
{
_EmployeeRepository = new EmployeeRepository();
}
public BuinessLayer(IEmployeeRepository EmployeeRepository)
{
_EmployeeRepository = EmployeeRepository;
}
public IList<Employee> GetAllEmployees()
{
return _EmployeeRepository.GetAll();
}
public IList<Employee> GetEmployeesByCountryName(string countryName)
{
return _EmployeeRepository.GetList(e => e.Country.Employees.Equals(countryName));
}
public Employee GetEmployeeByName(string EmployeeName)
{
return _EmployeeRepository.GetSingle(
d => d.Name.Equals(EmployeeName),
d => d.Country); //include related employees
}
public Employee GetEmployeeByIdentityId(int identityId, string EmployeeUniqueIdentityNumber)
{
var EmployeeIdentity = _EmployeeIdentityRepository
.GetSingle
(
q => q.IdentityId == identityId && q.UniqueIdentityNumber == EmployeeUniqueIdentityNumber);
Employee Employee = new Employee();
if (EmployeeIdentity != null)
{
Employee = _EmployeeRepository.GetSingle(o => o.EmployeeId == EmployeeIdentity.EmployeeId);
}
else
Employee = null;
return Employee;
}
public void AddEmployee(params Employee[] Employees)
{
try
{
_EmployeeRepository.Add(Employees);
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
}
public void UpdateEmployee(params Employee[] Employees)
{
/* Validation and error handling omitted */
_EmployeeRepository.Update(Employees);
}
public void RemoveEmployee(params Employee[] Employees)
{
/* Validation and error handling omitted */
_EmployeeRepository.Remove(Employees);
}
}
Now you can call business layer from the front-end
I have a following problem - limited ORM that imposes coupling of infrastructure to domain objects.
Please note this is a Windows Store Application and Reflection API differs from standard .Net
For example I have to manually implement navigational properties (relations in code) like this (assuming a I have a book aggregate with one-to-many child collection of chapters):
List<Chapters> Chapters
{get {return db.Query<Chapters>.Where(b => b.BookId == this.Id);}}
I want to reduce this coupling be the means of generic extension method, that will retrieve child entities for parent, like
IEnumerable<T> GetChildren<TParent,TChild>(this TParent parent)
where TParent, TChild : Entity
I can assume that all Entities have an Id primary key property and foreign key is composed of Parent entity name and Id ("BookId")
How do I implement a predicate for db.Query.Where Linq method in this extension method, assuming this parameter is parent entity?
Something like so (simplified version):
public static TChild GetHierarchyChild<TParent, TChild>(this TParent parent)
{
var pType = typeof(TParent);
var chType = typeof(TChild);
var chPropInfo = pType
.GetProperties()
.FirstOrDefault(p => p.PropertyType == chType);
if (chPropInfo == null)
{
return default(TChild);
}
return (TChild)chPropInfo.GetValue(parent);
}
public class A
{
public IEnumerable<B> Bs
{
get
{
return new[] { new B(1) };
}
}
}
public class B
{
public B(int id)
{
Id = id;
}
public int Id { get; protected set; }
}
an example:
var a = new A();
var bs = GetHierarchyChild<A, IEnumerable<B>>(a);
bs.ToString();
This works for me but unfortunately Sqlite-Net does not support expressions of this type in its Linq2Db implementation. It crashes on the last statement. I will have to rewrite this part to SQL.
private const string keyName = "Id";
public static async Task<IEnumerable<TChild>> GetChildrenAsync<TParent, TChild>(this TParent parent)
where TParent : Entity
where TChild : Entity, new()
{
var parentType = typeof (TParent);
var parentName = parentType.GetTypeInfo().Name;
var parentKeyValue = (int)parentType.GetRuntimeProperty(keyName).GetValue(parent);
var foreignKeyName = String.Format("{0}{1}", parentName, keyName);
var childProperty = typeof(TChild).GetRuntimeProperty(foreignKeyName);
var connection = DbConnection.Current;
var query = connection.Table<TChild>().Where(c => (int)childProperty.GetValue(c) == parentKeyValue);
return await query.ToListAsync();
}
I am looking a way to create Generic GetById that get params object[] as parameter, knows to find the key/s field/s and know to find the relevant entity.
In the way to find a solution I thought on a generic method that returns the PK fields definition and a generic method that can return the entity based on fields.
I am looking for something I can use in table with one or more fields as primary key.
EDIT
one or more fields as primary key example =
table Customers have (CompanyId, CustomerName, Address, CreateDate).
The primary key of Customers are CompanyId are CustomerName.
I am looking for generic GetById that will know to handle also those such of tables.
You can't get "generic" approach if you don't know how many members is in the key and what types do they have. I modified my solution for single key to multiple keys but as you can see it is not generic - it uses order in which keys are defined:
// Base repository class for entity with any complex key
public abstract class RepositoryBase<TEntity> where TEntity : class
{
private readonly string _entitySetName;
private readonly string[] _keyNames;
protected ObjectContext Context { get; private set; }
protected ObjectSet<TEntity> ObjectSet { get; private set; }
protected RepositoryBase(ObjectContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
Context = context;
ObjectSet = context.CreateObjectSet<TEntity>();
// Get entity set for current entity type
var entitySet = ObjectSet.EntitySet;
// Build full name of entity set for current entity type
_entitySetName = context.DefaultContainerName + "." + entitySet.Name;
// Get name of the entity's key properties
_keyNames = entitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray();
}
public virtual TEntity GetByKey(params object[] keys)
{
if (keys.Length != _keyNames.Length)
{
throw new ArgumentException("Invalid number of key members");
}
// Merge key names and values by its order in array
var keyPairs = _keyNames.Zip(keys, (keyName, keyValue) =>
new KeyValuePair<string, object>(keyName, keyValue));
// Build entity key
var entityKey = new EntityKey(_entitySetName, keyPairs);
// Query first current state manager and if entity is not found query database!!!
return (TEntity)Context.GetObjectByKey(entityKey);
}
// Rest of repository implementation
}
I don't know how useful this would be because it is generic but you could do this:
public TEntity GetById<TEntity>(params Expression<Func<TEntity, bool>>[] keys) where TEntity : class
{
if (keys == null)
return default(TEntity);
var table = context.CreateObjectSet<TEntity>();
IQueryable<TEntity> query = null;
foreach (var item in keys)
{
if (query == null)
query = table.Where(item);
else
query = query.Where(item);
}
return query.FirstOrDefault();
}
and then you could call it like this:
var result = this.GetById<MyEntity>(a => a.EntityProperty1 == 2, a => a.EntityProperty2 == DateTime.Now);
Disclaimer: this really isn't a GetByid, it's really a "let me give you a couple parameters and give me the first entity that matches". But that being said, it uses generics and it will return an entity if there is a match and you search based on primary keys.
I think you can't implement such thing because you won't be able to join between each passed value with the appropriate key field.
I'd suggest using custom method for each entity:
Assuming Code and Name are keys in Person table:
public IEnumerable<Person> ReadById(int code, string name)
{
using (var entities = new Entities())
return entities.Persons.Where(p => p.Code = code && p.Name = name);
}
Ok this is my second stab at it. I think this would work for you.
public static class QueryExtensions
{
public static Customer GetByKey(this IQueryable<Customer> query, int customerId,string customerName)
{
return query.FirstOrDefault(a => a.CustomerId == customerId && a.CustomerName == customerName);
}
}
So the beauty behind this extension method is you can now do this:
Customer customer = Db.Customers.GetByKey(1,"myname");
You obviously have to do this for every type, but probably worth it if you need it :)
I think the set up is a bit but I do think creating a reusable pattern will pay off in the long run. I just wrote this up and haven't tested, but I based off a searching pattern I use a lot.
Required Interfaces:
public interface IKeyContainer<T>
{
Expression<Func<T, bool>> GetKey();
}
public interface IGetService<T>
{
T GetByKey(IKeyContainer<T> key);
}
Example Entities:
public class Foo
{
public int Id { get; set; }
}
public class ComplexFoo
{
public int Key1 { get; set; }
public int Key2 { get; set; }
}
Implementation Example:
public class FooKeyContainer : IKeyContainer<Foo>
{
private readonly int _id;
public FooKeyContainer(int id)
{
_id = id;
}
public Expression<Func<Foo, bool>> GetKey()
{
Expression<Func<Foo, bool>> key = x => x.Id == _id;
return key;
}
}
public class ComplexFooKeyContainer : IKeyContainer<ComplexFoo>
{
private readonly int _id;
private readonly int _id2;
public ComplexFooKeyContainer(int id, int id2)
{
_id = id;
_id2 = id2;
}
public Expression<Func<ComplexFoo, bool>> GetKey()
{
Expression<Func<ComplexFoo, bool>> key = x => x.Key1 == _id && x.Key2 == _id2;
return key;
}
}
public class ComplexFooService : IGetService<ComplexFoo>
{
public ComplexFoo GetByKey(IKeyContainer<ComplexFoo> key)
{
var entities = new List<ComplexFoo>();
return entities.Where(key.GetKey()).FirstOrDefault();
}
}
Usage:
var complexFoo = ComplexFooService.GetByKey(new ComplexFooKeyContainer(1, 2));
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/