【问题标题】:C# LINQ to SQL Specified type member inside lambda expressionC# LINQ to SQL 在 lambda 表达式中指定类型成员
【发布时间】:2013-11-14 22:13:08
【问题描述】:

我知道 LINQ 不能使用未映射到数据库列的属性,但我不明白为什么一个 LINQ 语句在非静态方法中工作,但在其中尝试时出现此错误。

这是我的工作方法:

private TemplatesAPIContext db = new TemplatesAPIContext();

// GET api/Template
public IQueryable<TemplateDto> GetTemplates()
{
    return db.TemplateModels.Include(t => t.Categories).Select(
        x => new TemplateDto
        {
            TemplateID = x.TemplateID,
            Name = x.Name,
            HTMLShowcase = x.HTMLShowcase,
            ShortDescription = x.ShortDescription,
            CreationDate = x.CreationDate,
            Downloads = x.Downloads,
            Tags = x.Tags,
            Categories = db.CategoryModels
                            .Where(c => x.Categories.Where(a => a.TemplateID == x.TemplateID)
                            .Select(a => a.CategoryID).Contains(c.CategoryID))
        }
    );
}

我不想重复这个复杂的 DTO 构建(我实际上仍然需要添加一些其他关系,它会变得更加复杂)并在控制器中的每个方法上键入它,所以我想制作一个 lambda 表达式并将其传递给方法。

所以我这样做了:

private static readonly Expression<Func<TemplateModel, TemplateDto>> AsTemplateDto =
    x => new TemplateDto
    {
        TemplateID = x.TemplateID,
        Name = x.Name,
        HTMLShowcase = x.HTMLShowcase,
        ShortDescription = x.ShortDescription,
        CreationDate = x.CreationDate,
        Downloads = x.Downloads,
        Tags = x.Tags,
        Categories = new TemplatesAPIContext().CategoryModels
                            .Where(c => x.Categories.Where(a => a.TemplateID == x.TemplateID)
                            .Select(a => a.CategoryID).Contains(c.CategoryID))
    };

希望能打电话:

// GET api/Template
public IQueryable<TemplateDto> GetTemplates()
{
    return db.TemplateModels.Include(t => t.Categories).Select(AsTemplateDto);
}

但这会返回这个错误,这对我来说没有意义,因为它是完全相同的查询,唯一的区别是我需要在 lambda 中实例化 dbContext,因为我不能使用在控制器中实例化的那个lambda 表达式是静态的。

错误

LINQ to Entities 不支持指定的类型成员“CategoryModels”。仅支持初始化程序、实体成员和实体导航属性。

【问题讨论】:

  • 您正试图从一个上下文中查询另一个上下文?无论如何,EF 无法将该方法转换为 SQL 查询(您认为会如何?)
  • 让我吃惊的是它在第一个示例中有效。我想我要问的是,如何编写 lambda 表达式以产生与第一个示例相同的查询?

标签: c# linq entity-framework lambda asp.net-web-api


【解决方案1】:

在查询中使用与进行查询的上下文相同的上下文非常重要,这样查询提供程序才能了解您要执行的操作。因此,您所需要的只是一种复制特定于给定上下文的表达式的方法,这并不难,您已经完成了几乎所有的工作。

//TODO rename method as appropriate
private static Expression<Func<TemplateModel, TemplateDto>> 
    CreateTemplateDTO(TemplatesAPIContext context)
{
    return x => new TemplateDto
    {
        TemplateID = x.TemplateID,
        Name = x.Name,
        HTMLShowcase = x.HTMLShowcase,
        ShortDescription = x.ShortDescription,
        CreationDate = x.CreationDate,
        Downloads = x.Downloads,
        Tags = x.Tags,
        Categories = context.CategoryModels
            .Where(c => x.Categories.Where(a => a.TemplateID == x.TemplateID)
            .Select(a => a.CategoryID).Contains(c.CategoryID))
    };
}

现在你可以写了:

public IQueryable<TemplateDto> GetTemplates()
{
    return db.TemplateModels.Include(t => t.Categories)
        .Select(CreateTemplateDTO(db));
}

【讨论】:

  • 嗯,我以为我尝试将上下文作为参数传递给表达式,但它给了我一些错误,让我再试一次。
  • @francisco.preller 作为一个领域,你几乎肯定无法做到;它需要是一个方法,或者充其量是一个属性,但最好是一个方法。
  • 啊,明白了,必须把它变成一个方法,而不仅仅是一个属性。谢谢,这就像一个魅力。男人,我是如此接近>
  • @francisco.preller 是的,当你完成了 95% 的工作时,我几乎感觉很难获得解决方案的荣誉。
  • 我觉得不得不问,但我从昨晚开始就一直在敲我的头:P
【解决方案2】:

您的第一个方法是一个简单的表达式树,它只包含树节点中的简单操作(例如将 A 分配给 B),因此它可以很容易地编译成 SQL 查询。
另一个方法包含 TemplatesAPIContext 的实例化。无法进行数据库查询。

【讨论】:

    猜你喜欢
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-27
    • 2012-07-17
    • 2012-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多