I would say build your DAL using IQueryable, and pass it around, make sure your object contexts lifetime is the request. This way you will get benefit of delayed execution, but are exposed to the risk of inefficient querying of database.
Then make sure you performance test your application( or at least the parts that are most likely to get traffic) and see the data access patterns. Create specialized methods in your DAL to retrieve fully materialized objects and make these queries as pre compiled queries.
a sample of the repository interface would be like
public interface IDataContext
{
void Add<T>(T entity) where T : BaseEntity;
void Delete<T>(T entity) where T : BaseEntity;
IQueryable<T> Find<T>(Expression<Func<T, bool>> where) where T : BaseEntity;
}
where BaseEntity is the base class to all our classes, it looks like, this class is not mapped to any table in DB
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime CreateDateTime { get; set; }
public string CreateUser { get; set; }
public DateTime ModDateTime { get; set; }
public string ModUser { get; set; }
public byte[] RowVersion { get; set; }
}
Expression<Func<T, bool>>
would pass the whole expression to your repository instead of just Func, since EF works on expression to generate SQL query, a typical use would be
ICollection<WFGroup> wgGroups = this.dataContext.Find<WFGroup>((w) => true).ToList();
where WFGroup is a class derived from BaseEntity, I typically use lazy loading and proxy and don't detach/attach objects to a context.