You can have a relatively clean IRepository<T>
by sticking to the pattern.
Data Access LAyer
- reference to EF and Core project
- Has
Respository<T> : IRepository<T>
- Option has IEFJunk declaration (Only if using multiple repositories)
- Reference to Core
- Respository is injected with Context (typically during instantiation)
Core
- Interfaces that can be "Injected"
- IRepository declaration. With no EF data type use.
- NO reference to EF
So now code in Core you can refer to an IRepository<t>
.
The implementing class can have EF specifics. But this can not be accessed from core!
so you can have IQueryable.
public interface IRepositoryBase<TPoco>{
IQueryable<TPoco> GetListQ(Expression<Func<TPoco, bool>> predicate);
//...
BUt if you decided you wanted to add
//...
// logically exposing IQueryable<T> Include<T>(this IQueryable<T> source, string path) from EF
IQueryable<TPoco> IncludeNAVProp(string navToInclude);
}
Then the Repository implementation
return Context.Set<TPoco>().Include(navToInclude);
requires the underlying provider to be EF. So now mocking is against an actual EF provider.
And unless you are careful, EF specific code. leaks out.
Indeed the interface IRepository that has the CONCEPT "include" can already be considered LEAKY.
Keeping EF specifics out of your Interfaces, is the key to avoiding the leaks.
And you can have 1 IRepository<t>
and 1 Respository<t>
and support 100s of tables