Add Include to repository - c#

I have a working repository.
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public TEntity Get(int id)
{
return Context.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate);
}
public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().SingleOrDefault(predicate);
}
public void Add(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
}
public void Remove(TEntity entity)
{
Context.Set<TEntity>().Remove(entity);
}
}
As I read in coding repositories, that you don't add any class until you really need it. Now, I need to add Include. I found this one in this community Use Include() method in repository:
public static class IncludeExtension
{
public static IQueryable<TEntity> Include<TEntity>(this IDbSet<TEntity> dbSet,
params Expression<Func<TEntity, object>>[] includes)
where TEntity : class
{
IQueryable<TEntity> query = null;
foreach (var include in includes)
{
query = dbSet.Include(include);
}
return query ?? dbSet;
}
}
Then, I changed it to fit in my code (As I think) to be:
public IEnumerable<TEntity> Include(IDbSet<TEntity> dbSet,
params Expression<Func<TEntity, object>>[] includes)
{
IEnumerable<TEntity> query = null;
foreach (var include in includes)
{
query = dbSet.Include(include);
}
return query ?? dbSet;
}
With direct access to context, I am able to write:
Provinces = _cmsDbContext.Provinces.Include(c => c.District).Include(c => c.District.Country).ToList();
But, with repository, I can't write:
Provinces = Currentunitofwork.ProvinceRepository.Include(c => c.District).Include(c => c.District.Country).ToList();
I got error:
cannot convert lambda expression to type IDbSet<Province> because it is not a delegate type
What is the problem here, please.

I suspect that your code is passing in the lambda expression to the IDbSet parameter and can not convert it to that type.
I have not been able to test but it compiles, if the method is a member of the Repository class then try this.
public IEnumerable<TEntity> Include(params Expression<Func<TEntity, object>>[] includes)
{
IDbSet<TEntity> dbSet = Context.Set<TEntity>();
IEnumerable<TEntity> query = null;
foreach (var include in includes)
{
query = dbSet.Include(include);
}
return query ?? dbSet;
}

Again thanks to #Adam Carr.
This is the method code now:
public IQueryable<TEntity> Include(params Expression<Func<TEntity, object>>[] includeExpressions)
{
IDbSet<TEntity> dbSet = Context.Set<TEntity>();
IQueryable<TEntity> query = null;
foreach (var includeExpression in includeExpressions)
{
query = dbSet.Include(includeExpression);
}
return query ?? dbSet;
}
What I change is use Set as a method not a property. So, instead of:
IDbSet<TEntity> dbSet = Context.Set<TEntity>;
I used:
IDbSet<TEntity> dbSet = Context.Set<TEntity>();
Also, I used IQueryable instead of IEnumerable.

Related

how to inherit from generic repository without needing to implement all the members

Im trying to create a generic repository ,here is my IGenericRepository:
public interface IGenericRepository<TEntity> where TEntity : class
{
Task<IEnumerable<TEntity>> FindByFilterAsync(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] including);
Task<TEntity> GetByIdAsync(int id);
Task<bool> InsertAsync(TEntity obj);
}
here is the implementation of the respository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly IServiceScopeFactory scopeFactory;
public GenericRepository(IServiceScopeFactory scopeFactory) => this.scopeFactory = scopeFactory;
public async Task<IEnumerable<TEntity>> FindByFilterAsync(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] including)
{
using (var scope = this.scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<CleanArchitectureContext>();
var x = db.Set<TEntity>().AsQueryable();
if (including != null)
{
including.ToList().ForEach(s =>
{
if (s != null)
{
x = x.Include(s);
}
});
}
return await x.Where(predicate).ToListAsync().ConfigureAwait(true);
}
}
public Task<TEntity> GetByIdAsync(int id)
{
throw new NotImplementedException();
}
public Task<bool> InsertAsync(TEntity obj)
{
throw new NotImplementedException();
}
}
now lets say i need to have an interface which is going to inherit from the IGeneric repository and store data in the db:
public interface IStorePayoutRepository:IGenericRepository<PayoutModel>
{
}
so far so good, the problem is if i want to have a class and inherit from IStorePayoutRepository,then i need to implement all the members inside that which is not what i want,because i need only the store one(save or insert into db),
public class PayoutRepository : IGenericRepository<PayoutEntity>
{
//Im forced to implement all the members inside generic interface
}
as you see its not very optimal to implement all the members everytime as they could be irrelevant to the usecase,whats the right way here?i appreciate your help

Entity Framework repostory pattern GetAll() too slow

I'm using repository layer. My problem here is GetAll() method is too slow when join a table with large records. It is taking 40 seconds to run a simple query.
IGenericRepository:
public interface IGenericRepository<TEntity>
{
TEntity FindBy(Expression<Func<TEntity, bool>> predicate);
IEnumerable<TEntity> GetAll();
TEntity GetById(int id);
TEntity Insert(TEntity entity);
TEntity Update(TEntity entity);
void Delete(object id);
void Save();
}
GenericRepository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private MyStoreContext _dbContext;
protected DbSet<TEntity> DbSet;
public GenericRepository()
{
_dbContext = new MyStoreContext ();
DbSet = _dbContext.Set<TEntity>();
}
public TEntity FindBy(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate).SingleOrDefault();
}
public IEnumerable<TEntity> GetAll()
{
return DbSet.AsNoTracking();
}
public TEntity GetById(int id)
{
return DbSet.Find(id);
}
public TEntity Insert(TEntity entity)
{
DbSet.Add(entity);
Save();
return entity;
}
public TEntity Update(TEntity obj)
{
DbSet.Attach(obj);
_dbContext.Entry(obj).State = EntityState.Modified;
Save();
return obj;
}
public void Delete(object id)
{
TEntity entityToDelete = DbSet.Find(id);
Delete(entityToDelete);
}
public void Delete(TEntity entityToDelete)
{
if (_dbContext.Entry(entityToDelete).State == EntityState.Detached)
{
DbSet.Attach(entityToDelete);
}
DbSet.Remove(entityToDelete);
Save();
}
public void Save()
{
try
{
_dbContext.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
System.Console.WriteLine("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage); // you just put the log to know the errors
}
}
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_dbContext != null)
{
_dbContext.Dispose();
_dbContext = null;
}
}
}
}
Linq:
var conceptosDetalle = from pf in _parFactfRepository.GetAll()
join inve in _inveRepository.GetAll() on pf.CVE_ART equals inve.CVE_ART
where inve.STATUS == "A" && pf.CVE_DOC == cveDoc
orderby inve.CTRL_ALM, inve.CVE_ART
select new MyViewModel()
{
CTRL = inve.CTRL_ALM,
CVE_ART = inve.CVE_ART,
UNID = "PIEZA",
CANT = pf.CANT,
DESCR = inve.DESCR,
PREC = pf.PREC,
DESC1 = pf.DESC1,
TOTIMP4 = pf.TOTIMP4
};
The query returns 10 records. parFactfRepository contains 992590 rows, inveRepository contains 41908 rows.
What i'm doing wrong?
That's because you're mixing and matching repository-based queries and LINQ queries. Rather than doing a true join, you're fetching all the rows for each table and then joining them in-memory.
The easiest way to fix this is probably to just return IQueryable<TEntity> rather than IEnumerable<TEntity> from your GetAll method. Using IEnumerable<TEntity> forces the query to evaluate. If you're going to return IEnumerable<TEntity> your data should be fully-baked, i.e. no further alterations to the query are necessary (including joins).
That said, this is yet one more failing of trying to use the repository pattern with EF. If you aren't very careful, you end up introducing logical errors like this that aren't obvious as to why they are happening. Entity Framework already implements the repository pattern; that is what a DbSet is. If you want an abstraction over that, introduce a service layer. With that, you'd simply have a method like:
public IEnumerable<MyViewModel> GetConceptosDetalle()
{
...
}
And that method would contain this entire query (using EF directly, rather than your completely unnecessary repository layer). That way, your application simply calls a method that returns the data it needs, and that service layer contains all the logic. That's true abstraction. With a repository, you're bleeding logic all over your codebase.
Note: I made it return MyViewModel simply for ease of explanation, but in reality, you should return some sort of DTO, which you could then map to your view model. It would be bad idea to leak view business logic into a service layer.

Mocking using MOQ and generic types

Method to unit test: GetUserInfo
Following is the class containing the method:
public class AccountService : IAccountService
{
IUnitOfWork _UnitOfWork;
public AccountService(IUnitOfWork unitOfWork)
{
_UnitOfWork = unitOfWork;
}
public UserInfo GetUserInfo(string userName, string password)
{
var userInfo = new UserInfo();
userInfo.UserType = UserType.Invalid;
// Statement of interest
var portalUser = _UnitOfWork.Repository<DvaPortalUser>().Query().Filter(t => t.Email == userName && t.Password == password).Get().FirstOrDefault();
//....Rest of the code is not included for clarity
}
}
The interface to mock IUnitOfWork:
public interface IUnitOfWork
{
void Dispose();
void Save();
void Dispose(bool disposing);
IRepository<T> Repository<T>() where T : class;
}
Repository Implementation:
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
public virtual RepositoryQuery<TEntity> Query()
{
var repositoryGetFluentHelper = new RepositoryQuery<TEntity>(this);
return repositoryGetFluentHelper;
}
internal IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>,
IOrderedQueryable<TEntity>> orderBy = null,
List<Expression<Func<TEntity, object>>>
includeProperties = null,
int? page = null,
int? pageSize = null)
{
IQueryable<TEntity> query = DbSet;
if (includeProperties != null)
includeProperties.ForEach(i => query.Include(i));
if (filter != null)
query = query.Where(filter);
if (orderBy != null)
query = orderBy(query);
if (page != null && pageSize != null)
query = query
.Skip((page.Value - 1)*pageSize.Value)
.Take(pageSize.Value);
return query.ToList();
}
}
RepositoryQuery Implementation:
public sealed class RepositoryQuery<TEntity> where TEntity : class
{
private readonly List<Expression<Func<TEntity, object>>> _includeProperties;
private readonly Repository<TEntity> _repository;
private Expression<Func<TEntity, bool>> _filter;
private Func<IQueryable<TEntity>,
IOrderedQueryable<TEntity>> _orderByQuerable;
private int? _page;
private int? _pageSize;
public RepositoryQuery(Repository<TEntity> repository)
{
_repository = repository;
_includeProperties = new List<Expression<Func<TEntity, object>>>();
}
public RepositoryQuery<TEntity> Filter(Expression<Func<TEntity, bool>> filter)
{
_filter = filter;
return this;
}
public IEnumerable<TEntity> Get()
{
return _repository.Get(
_filter,
_orderByQuerable, _includeProperties, _page, _pageSize);
}
}
Unit Test Method:
[TestMethod]
public void AccountService_GetUserInfo_SuccessfulLogin()
{
var _UnitOfWork = new Mock<IUnitOfWork>();
_AccountService = new AccountService(_UnitOfWork.Object);
_UnitOfWork.Setup(a => a.Repository<T>())).Returns(??); //How do I setup this statement?
_UnitOfWork.VerifyAll();
}
Question: How do I setup mock call for the statement _UnitOfWork.Repository()?
I dont know how your IRepository<T> is implemented, but i gues your Query method returns a IEnumerable<T> or a list.
internal interface IRepository<T>
{
IEnumerable<T> Query();
}
// your mock
_UnitOfWork.Setup(a => a.Repository<DvaPortalUser>())).Returns(() => new MyTestRepository());
1. Solution
Define a explicit implemtation of you repository.
// your implementation
public class MyTestRepository : IRepository<DvaPortalUser>
{
public IEnumerable<DvaPortalUser> Query()
{
// return some test users (mocks)
return new List<DvaPortalUser> {new DvaPortalUser(), new DvaPortalUser()};
}
}
2. Solution (thanks to Yuliam Chandra)
Define a mock instead of the implemtation
var repository = new Mock<IRepository<DvaPortalUser>>();
// return some test users (mocks)
repository.Setup(a => a.Query()).Returns(new[] { new DvaPortalUser() });
_UnitOfWork.Setup(a => a.Repository<DvaPortalUser>()).Returns(repository.Object);
What you choose depends on your solution.

Implementing IDbSet in Generic Repository

I have followed this tutorial and already implemented in my project.
I made some changes to make more testable, so I implement interface
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
internal MyEntities context;
internal IDbSet<T> dbSet;
public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
return dbSet.SqlQuery(query, parameters).ToList();
}
...
}
public interface IEmployeeRepository : IGenericRepository<Employee> { }
public class EmployeeRepository : GenericRepository<Employee>, IEmployeeRepository { }
in UnitOfWork:
public class UnitOfWork : IDisposable
{
private MyEntities context = new MyEntities();
private IEmployeeRepository employeeRepository;
public IEmployeeRepository EmployeeRepository
{
get { return employeeRepository ?? (employeeRepository = new EmployeeRepository(context)); }
}
}
Then I cannot access dbSet in GetWithRawSql() because I change to IDbSet.
(1) How to solve this?
(2) Is there any better approach (without dbSet) to this code using the above approach as this is also failed because IDbSet
unitOfWork.EmployeeRepository.dbSet.Select(c => new {
EmpID = c.EmployeeID,
FullName = (c.LastName + ", " + c.FirstName)
}).OrderBy(o => o.FullName);
Thanks.
i believe that this is a poor implementatin, here is the one im currently using:
this is going to be your IRepository
public interface IRepository<T> where T : class, IEntity
{
void Add(T item);
void Remove(T item);
void Update(T item);
void Attach(T item);
IQueryable<T> All<TProperty>(params Expression<Func<T, TProperty>>[] path);
IQueryable<T> All(params string[] path);
T Find(object id);
T First(Expression<Func<T, bool>> predicate, params string[] path);
Task<T> FirstAsync(Expression<Func<T, bool>> predicate, params string[] path);
T First<TProperty>(Expression<Func<T, bool>> predicate, params Expression<Func<T, TProperty>>[] path);
IQueryable<T> Where<TProperty>(Expression<Func<T, bool>> predicate, params Expression<Func<T, TProperty>>[] path);
IQueryable<T> Where(Expression<Func<T, bool>> predicate, params string[] includes);
}
often you need to attach entities to context so i added Attach method but if you want to keep entity framework attach Seperate (witch is not really needed) from regular IRepository you can define a new interface and called ISqlRepository and inherit from IRepository, creating two IRepository will just make it more complex since it dosnt make any problem in UnitTest. any way if you do want to seperate this is going to be your ISqlRespository:
public interface ISqlRepository<T>: IRepository<T> where T:class, IEntity
{
void Update(T item);
void Attach(T item);
}
this is unitOfWork interface:
public interface IUnitOfWork<T> where T : class, IEntity
{
IRepository<T> Repistory { get; }
IRepository<TEntity> GetTypeRepository<TEntity>() where TEntity : class, IEntity;
object GetContext();
int Commit();
Task<int> CommitAsync();
}
and finally concrete implementation (without ISqlRepository since i dont use that):
this is implementation of IRespository:
public class SqlRepository<T>: IRepository<T> where T: class, IEntity
{
protected readonly DbSet<T> _objectSet;
protected ApplicationDbContext _context;
public SqlRepository(ApplicationDbContext context)
{
_objectSet = context.Set<T>();
this._context = context;
}
public void Add(T item)
{
_objectSet.Add(item);
}
public void Update(T item)
{
_context.Entry(item).State = EntityState.Modified;
}
public void Remove(T item)
{
_objectSet.Remove(item);
}
public T First(Expression<Func<T, bool>> predicate, params string[] path)
{
IQueryable<T> query = _objectSet;
if (path != null)
{
path.ForeEach(i => query = query.Include(i));
}
return query.FirstOrDefault(predicate);
}
public T First<TProperty>(Expression<Func<T, bool>> predicate, params Expression<Func<T, TProperty>>[] path)
{
IQueryable<T> query = _objectSet;
path.ForeEach(p => query = query.Include(p));
return query.First(predicate);
}
public IQueryable<T> Where<TProperty>(Expression<Func<T, bool>> predicate, params Expression<Func<T, TProperty>>[] path)
{
IQueryable<T> query = _objectSet;
path.ForeEach(p => query = query.Include(p));
return query.Where(predicate);
}
public IQueryable<T> Where(Expression<Func<T, bool>> predicate, params string[] includes)
{
IQueryable<T> query = _objectSet;
includes.ForeEach(i => query = query.Include(i));
return query.Where(predicate);
}
public IQueryable<T> All<TProperty>(params Expression<Func<T, TProperty>>[] path)
{
IQueryable<T> query = _objectSet;
if (path != null)
{
path.ForeEach(p => query.Include(p));
}
return query;
}
public IQueryable<T> All(params string[] path)
{
IQueryable<T> query = _objectSet;
if (path != null)
{
path.ForeEach(p => query = query.Include(p));
}
return query;
}
public T Find(object id)
{
return _objectSet.Find(id);
}
public void Attach(T item)
{
T old = _objectSet.Local.FirstOrDefault(i => i.Id == item.Id);
if (old != null)
{
_context.Entry<T>(old).State = EntityState.Detached;
}
_objectSet.Attach(item);
}
public System.Threading.Tasks.Task<T> FirstAsync(Expression<Func<T, bool>> predicate, params string[] path)
{
IQueryable<T> query = _objectSet;
if(path != null)
{
path.ForeEach(i => query = query.Include(i));
}
return query.FirstOrDefaultAsync(predicate);
}
}
this the unitOfWork:
public class SqlUnitOfWork<T> : IUnitOfWork<T> where T : class, IEntity
{
private ApplicationDbContext _db;
public SqlUnitOfWork(ApplicationDbContext context)
{
_db = context;
}
public IRepository<T> Repistory
{
get
{
return new SqlRepository<T>(_db);
}
}
public int Commit()
{
return _db.SaveChanges();
}
public Task<int> CommitAsync()
{
return _db.SaveChangesAsync();
}
public object GetContext()
{
return this._db;
}
public IRepository<TEntity> GetTypeRepository<TEntity>() where TEntity : class, IEntity
{
return new SqlRepository<TEntity>(_db);
}
}

Include is gone

I am trying to replace the joins and use include but I don't know how to do that:
IRepository<Call> callRepository =
ObjectFactory.GetInstance<IRepository<Call>>();
IRepository<Reason> reasonRepository =
ObjectFactory.GetInstance<IRepository<Reason>>();
IRepository<SimTask.Domain.Business.Entities.System.Company>
companyRepository = ObjectFactory.GetInstance<IRepository<SimTask.Domain.
Business.Entities.System.Company>>();
IQueryable<CallsListItem> x =
from call in callRepository.GetQuery()
join reason in reasonRepository.GetQuery() on call.ReasonId equals reason.Id
join company in companyRepository.GetQuery() on call.CompanyId equals company.CompanyId
where call.CompanyId == companyId &&
(!isClosed.HasValue || call.IsClosed.Equals(isClosed.Value))
select new CallsListItem()
{
CallId = call.Id,
Description = call.Description,
CloseDateTime = call.CloseDateTime,
IsClosed = call.IsClosed,
OpenDateTime = call.OpenDateTime,
PhoneNumber = call.PhoneNumber,
ReasonName = reason.Name,
CompanyName = company.CompanyName
};
IRepository is implemented by:
public class EFRepository<T> : IRepository<T> where T : class
{
ObjectContext _context;
IObjectSet<T> _objectSet;
private ObjectContext Context
{
get
{
if (_context == null)
{
_context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
}
return _context;
}
}
private IObjectSet<T> ObjectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this.Context.CreateObjectSet<T>();
}
return _objectSet;
}
}
public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
{
return (TUnitOfWork)UnitOfWork.Current;
}
public IQueryable<T> GetQuery()
{
return ObjectSet;
}
public IEnumerable<T> GetAll()
{
return GetQuery().ToList();
}
public IEnumerable<T> Find(Func<T,bool> where)
{
return this.ObjectSet.Where<T>(where);
}
public T Single(Func<T,bool> where)
{
return this.ObjectSet.Single<T>(where);
}
public T First(Func<T,bool> where)
{
return this.ObjectSet.First<T>(where);
}
public void Delete(T entity)
{
this.ObjectSet.DeleteObject(entity);
}
public void Add(T entity)
{
this.ObjectSet.AddObject(entity);
}
public void Attach(T entity)
{
this.ObjectSet.Attach(entity);
}
public void SaveChanges()
{
this.Context.SaveChanges();
}
}
Why is Include better then joins?
How can I do Include?
Include is eager loading and it fills navigation properties in real entities without need to project to non entity type - this cannot be achieved with joins. Also Include uses left outer joins whereas your query uses inner joins so you will not get entities which don't have related entity.
In EFv1 and EFv4 Include is a method of ObjectQuery. I wrote this answer using EFv4.1 which contains extension method for IQueryable<T> as well as Includes with lambda expression. You can try it - it is just another library you will link to your project and you can still use EFv4.
The reason to wrap Include in custom method is not introducing dependency to EF in upper layer. If you don't download EFv4.1 you can use this:
public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params string[] includes)
where T : class
{
if (includes != null)
{
var objectQuery = query as ObjectQuery;
if (objectQuery == null)
{
throw new InvalidOperationException("...");
}
objectQuery = includes.Aggregate(objectQuery,
(current, include) => current.Include(include));
}
return objectQuery;
}
The big disadvantage in both approaches (EFv4 and EFv4.1) is casting to ObjectQuery (EFv4.1 do it internally) - this can be serious issue in unit tests where you don't work with real queries.

Categories