问题
I'm using Entity Framework 6, and have implemented some semblance of the repository pattern. Initially, my repositories had functions like GetAll that returned an IEnumerable to avoid leaking the data layer abstractions out too much. But, my service layer, which is a wrapper around my repositories and contains business logic, needed more control over the queries. Control such as eager loading of related entities, selecting only certain columns, etc. So, in trying to avoid just exposing the DbContext to my service layer, I adjusted the repositories so that they now return IQueryable so that the service layer can do things like chain Includes onto the query, etc.
Problem is that my repositories have a method for returning a single entity, and the function just returns the POCO for the entity. These are the most likely functions for needing eager loading, calling Include. Can't do that off the POCO, obviously.
Is there something like IQueryable for a single entity for additional chaining of Include logic etc? Or a way to adjust this logic to allow for that? An example of a repository of mine is this:
namespace Portal.Repositories
{
public class UploadRepository : IUploadRepository
{
private readonly IPortalContext m_context;
public UploadRepository(IPortalContext context)
{
m_context = context;
}
#region Methods
public int Count(Expression<Func<Upload, bool>> predicate)
{
return m_context.Uploads.Count(predicate);
}
public Upload Get(Expression<Func<Upload, bool>> predicate)
{
return m_context.Uploads.FirstOrDefault(predicate);
}
public Upload Insert(Upload entity)
{
return m_context.Uploads.Add(entity);
}
public Upload Delete(Upload entity)
{
return m_context.Uploads.Remove(entity);
}
public IQueryable<Upload> All()
{
return m_context.Uploads;
}
public IQueryable<Upload> Where(Expression<Func<Upload, bool>> predicate)
{
return m_context.Uploads.Where(predicate);
}
#endregion
}
}
You can see my Get method, and how it's not possible for my services to pick and choose when to Include or not to, because it just returns a POCO.
回答1:
You can add some functionality to your repositories to pass some include expressions.
using System.Data.Entity;
namespace Portal.Repositories
{
public class UploadRepository : IUploadRepository
{
public Upload Get(
Expression<Func<Upload, bool>> predicate,
params Expression<Func<Upload, object>>[] includeExpressions)
{
var uploads = m_context.Uploads;
foreach (var i in includeExpressions)
uploads = uploads.Include(i);
return uploads.FirstOrDefault(predicate);
}
}
}
回答2:
How about this:
public List<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedEnumerable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = DbSet;
if (filter != null)
{
query = query.Where(filter);
}
query = includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
[Edit]: This is what I currently am using in my own code. You need to change and adjust the code to fit your purpose but the idea is to pass the Include properties as a comma separated value down to your Get function.
来源:https://stackoverflow.com/questions/19665887/iqueryable-functionality-like-include-but-for-a-single-entity