在实体框架存储库中使用选择器

时间:2014-01-20 08:52:59

标签: c# entity-framework repository

当我尝试使用某些返回类型时,我的存储库方法中的Select方法出现问题。

我遇到问题的存储库方法是:

public IEnumerable<T> List(Expression<Func<T, bool>> filter = null,
            string include = "", 
            int Taked = 0, Expression<Func<T, T>> selector = null)
{
    IQueryable<T> query = dbSet;
    if (filter != null)
        query = query.Where(filter);

    #region Stringleri İnclude Eder
    foreach (var includeProperty in 
         include.Split(new char[] {','}, 
                       StringSplitOptions.RemoveEmptyEntries))
    {
         query = query.Include(includeProperty);
    }
    #endregion

    if (selector != null)
        query = query.Select(selector);
    if (Taked != 0)
        return query.Take(Taked).ToList();
    return query.ToList();
}

internal DbContext context;
internal DbSet<T> dbSet;

我想使用上面的方法返回我的实体类,但我只想要填充某些属性。我尝试了以下方法:

AdminWork workunit = new AdminWork();
IEnumerable<AdminMenu> adminMenus = workunit.Menu.List(x => x.Online == true,
    selector: z => new AdminMenu 
    {
        MenuID = z.MenuID, 
        Name = z.Name, 
        Path = z.Path
    });

抛出异常:

  

无法在LINQ to Entities查询中构建AdminMenu

我也尝试过以下方法,但需要返回IEnumerable<int>

IEnumerable<AdminMenu> menus = workunit.Menu.List(x => x.Online == true, 
     selector: z => z.MenuID);

我的问题是如何在linq中为实体创建实体类的新实例,因此不会填充每个属性。

1 个答案:

答案 0 :(得分:2)

您无法传递创建AdminMenu实例的选择器,因为这是在您的Entity Framework上下文中映射的类型。因此,Entity Framework希望成为唯一一个创建该类型实例的人,因此它可以跟踪更改,如果已经加载则返回相同的实例等...有关详细信息,请参阅this question

因此,如果要返回AdminMenu列表,则不需要传递选择器(因为您开始查询的dbSet已经是该类型)。当返回类型与dbSet中的类型不同时,您只需要传递一个选择器。

您可以使用此方法:(我已将selector作为非可选参数,因为它将用于定义TResult类型。我还假设T是通用的在类public class Repository<T>中定义的参数,因此在方法中添加的唯一通用参数是TResult

public IEnumerable<TResult> ListProjected<TResult>(Expression<Func<T, TResult>> selector,
                                        Expression<Func<T, bool>> filter = null,
                                        string include = "",
                                        int Taked = 0)
{
    IQueryable<T> query = dbSet;
    if (filter != null)
        query = query.Where(filter);
    #region Stringleri İnclude Eder

    foreach (var includeProperty in include.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
    {
        query = query.Include(includeProperty);
    }

    #endregion

    if (Taked != 0)
        query = query.Take(Taked);

    return query.Select(selector).ToList();
}

当你不想投射到不同的类型时,不同的一个:

public IEnumerable<T> List(Expression<Func<T, bool>> filter = null,
                        string include = "",
                        int Taked = 0)
{
    return ListProjected(x => x, filter, include, Taked);
}

然后您可以使用以下方法:

IEnumerable<AdminMenu> menus = workunit.Menu.List(x => x.Online == true);

IEnumerable<int> menuIds = workunit.Menu.ListProjected(x => x.MenuID, 
                                                       x => x.Online == true);

最后,如果您只想要一些列,则需要使用匿名对象或创建另一个只包含您感兴趣的列的类(如DTO):

var menuSomeColumnsWithAnonymousObject = workunit.Menu.ListProjected(x => new
{
    MenuID = x.MenuID,
    Path = x.Path,
    Name = x.Name
}, x => x.Id == 1);

var menuSomeColumnsWithDTO = workunit.Menu.ListProjected(x => new AdminMenuDTO
{
    MenuID = x.MenuID,
    Path = x.Path,
    Name = x.Name
}, x => x.Id == 1);

答案比我想象的要长,但我希望它有所帮助!