I am following this article on MSDN. I ported it to EF Code First.
public interface IUnitOfWork
{
IRepository<Employee> Employees { get; }
IRepository<TimeCard> TimeCards { get; }
void Commit();
}
public class HrContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<TimeCard> TimeCards { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasMany(e => e.TimeCards)
.WithOptional(tc => tc.Employee);
}
}
public class SqlRepository<T> : IRepository<T>
where T : class
{
private readonly DbSet<T> entitySet;
public SqlRepository(DbContext context)
{
this.entitySet = context.Set<T>();
}
public void Add(T newEntity)
{
this.entitySet.Add(newEntity);
}
public IQueryable<T> FindAll()
{
return this.entitySet;
}
public T FindById(params object[] keys)
{
return this.entitySet.Find(keys);
}
public IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate)
{
return this.entitySet.Where(predicate);
}
public void Remove(T entity)
{
this.entitySet.Remove(entity);
}
}
public class SqlUnitOfWork : IUnitOfWork, IDisposable
{
private readonly HrContext context;
private IRepository<Employee> employees;
private IRepository<TimeCard> timeCards;
public SqlUnitOfWork()
{
this.context = new HrContext();
}
public IRepository<Employee> Employees
{
get
{
return new SqlRepository<Employee>(context);
}
}
public IRepository<TimeCard> TimeCards
{
get
{
return new SqlRepository<TimeCard>(context);
}
}
public void Commit()
{
this.context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
var query = from e in unitOfWork.Employees.FindAll()
from tc in unitOfWork.TimeCards.FindAll()
where tc.Employee.Id == e.Id && e.Name.StartsWith("C")
select tc;
var timeCards = query.ToList();
This model is great as it gives me testability. However, running queries like the one above throws this
LINQ to Entities does not recognize the method
'System.Linq.IQueryable`1[DomainModel.Models.TimeCard] FindAll()'
method, and this method cannot be translated into a store expression.
I understand the error but is there any way to avoid it but still keep the repositories for testability?
Your select statement cannot be translated due to the nature of how IQueryable<T>
and the query providers work: see this thread for more info What is the difference between IQueryable<T> and IEnumerable<T>?
You can 'help' the linq provider by dividing your expression into separate statements like this:
var ems = unitOfWork.Employees.FindAll();
var tcs = unitOfWork.TimeCards.FindAll();
var query = from e in ems
from tc in tcs
where tc.Employee.Id == e.Id && e.Name.StartsWith("C")
select tc;
Or you can let FindAll() return IEnumerable<T>
instead of IQueryable<T>
and then your original expression should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With