Where to place multiple queries for repository - c#

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

Related

Delete all corresponding items from SQLite

I have the following statement which get all the list item and then do loop to delete each item from the database.
Is there a better way to handle deleting all items?
IRepository.cs
public interface IRepository<T> where T : new()
{
TableQuery<T> AsQueryable();
T GetById(object id);
List<T> GetWithChildren(Expression<Func<T, bool>> predicate = null);
void Delete(T entity);
void CreateTable();
void Clear();
}
AnotherService.cs
protected IRepository<TModel> Repository => _repository ?? (_repository = new Repository<TModel>(_factory));
Service.cs
private IRepository<Class> _classRepo;
public void DeleteClasses(string classId)
{
foreach (var class in _classRepo.Get(x => x.ClassId.Equals(classId)).ToList())
{
_classRepo.Delete(class);
}
}

Mocking EF DbContext with Moq

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.

Make a where or any clause in a IRepository pattern with entity framework

I have taken over a project from a collegue, who uses the IRepository pattern. I have never used it before, so I have some problems understanding how to make a WHERE clause or an ANY clause.
Previously I had the following code, which uses the DataContext repository (the actual implementation, where I can use where clauses:
IQueryable<Office> officeList = repository.Offices;
if (specification.CountryId > 0)
{
officeList = repository.Offices.Where(c => c.CountryId == specification.CountryId);
}
if (specification.LetterSize != null)
{
officeList =
officeList.Where(
c => c.OfficeProducts.Any(d => d.OfficeProductDetail.Size == (int)specification.LetterSize));
}
return officeList.ToList();
I would like to understand how to get a code snippet like the one above to work using the IRepository pattern. I have tried to make an implementation of a WHERE/QUERY, but could not get it to work.
My question:
How do you implement a WHERE/ANY statement in practice, so I can do something like the code snippet above?
My IRepository:
public interface IRepository
{
T GetById<T>(long id) where T : class;
void Create<T>(T entity) where T : class;
void Update<T>(T entity) where T : class;
void SaveOrUpdate<T>(T entity) where T : class;
void Delete<T>(T entity) where T : class;
IList<T> FindAll<T>(params OrderBy[] orders) where T : class;
int Count<T>(Expression<Func<T, bool>> whereExpression) where T : class;
bool Exists<T>(Expression<Func<T, bool>> whereExpression) where T : class;
T FindFirst<T>(Expression<Func<T, bool>> whereExpression, params OrderBy[] orders) where T : class;
PaginatedResult<T> Find<T>(Expression<Func<T, bool>> whereExpression, int pageIndex, int pageSize, params OrderBy[] orders) where T : class;
void ExecuteNativeSQL(string sql);
}
The implementation:
public class EFRepository : IRepository
{
private IDBFactory databaseFactory;
private LetterAmazerContext dataContext;
public EFRepository(IDBFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
protected LetterAmazerContext DataContext
{
get { return dataContext ?? (dataContext = databaseFactory.Get()); }
}
public T GetById<T>(long id) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Find(id);
}
public void Create<T>(T entity) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
dbset.Add(entity);
}
public void Update<T>(T entity) where T : class
{
dataContext.Entry(entity).State = EntityState.Modified;
}
public void SaveOrUpdate<T>(T entity) where T : class
{
throw new NotImplementedException();
}
public void Delete<T>(T entity) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
dbset.Remove(entity);
}
public IList<T> FindAll<T>(params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
var query = dbset.Where(t => true);
query = ApplyOrders<T>(query, orders);
return query.ToList<T>();
}
public int Count<T>(Expression<Func<T, bool>> whereExpression) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Count<T>(whereExpression);
}
public bool Exists<T>(Expression<Func<T, bool>> whereExpression) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
return dbset.Count<T>(whereExpression) != 0;
}
public T FindFirst<T>(Expression<Func<T, bool>> whereExpression, params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
var query = dbset.Where(whereExpression);
query = ApplyOrders<T>(query, orders);
return query.SingleOrDefault<T>();
}
public PaginatedResult<T> Find<T>(Expression<Func<T, bool>> whereExpression, int pageIndex, int pageSize, params OrderBy[] orders) where T : class
{
IDbSet<T> dbset = DataContext.Set<T>();
PaginatedResult<T> results = new PaginatedResult<T>();
var query = dbset.AsExpandable().Where(whereExpression);
query = ApplyOrders<T>(query, orders);
results.Results = query.Skip<T>(pageIndex * pageSize).Take<T>(pageSize).ToList<T>();
results.TotalItems = dbset.AsExpandable().LongCount(whereExpression);
return results;
}
public void ExecuteNativeSQL(string sql)
{
DataContext.Database.ExecuteSqlCommand(sql);
}
private IQueryable<T> ApplyOrders<T>(IQueryable<T> query, params OrderBy[] orders)
{
if (orders == null || orders.Length == 0) return query;
foreach (var order in orders)
{
query = query.OrderBy(order.ToString());
}
return query;
}
}
Your repository is at the moment open for arbitrary expressions, including potentially expressions that cannot be evaluated by the implementation.
On one hand this is a potential risk of not being able to deliver an implementation that matches so open contract.
On the other hand, why don't you just then expose it a bit more:
public interface IRepository
{
...
IQueryable<T> Query<T>();
}
and
public class EFRepository : IRepository
{
...
public IQueryable<T> Query<T>()
{
return DataContrxt.Set<T>();
}
}
Note that if you decide to go that way, most of your specific query methods doesn't make sense anymore as such open generic queryable interface satisfies most needs.
Note also that the same concern applies there, if you have multiple implementations you can't possibly guarantee that the contract is satisfied in the same way. Also, your potential client can easily create a query the provider will reject. If you accept these issues, the proposed solution solves your issue as you can now query the repository for almost anything.
And the last note, if you don't plan to have multiple implementations, remove the repository layer completely. The EF context is a unit of work with repositories inside. Starting from version 6, the context can be mocked so unit testing is possible.

EF Many-to-many dbset.Include in DAL on GenericRepository

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

How to set up an in-memory repository

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/

Categories

Resources