关于c#:无法在LINQ to Entities查询中构造实体

The entity cannot be constructed in a LINQ to Entities query

有一个名为产品的实体类型由实体框架生成。我写了这个问题

1
2
3
4
5
6
public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}

下面的代码引发以下错误:

0

1
var products = productRepository.GetProducts(1).Tolist();

但当我使用select p而不是select new Product { Name = p.Name};时,它工作正常。

如何执行自定义选择节?


不能(也不能)投影到映射的实体上。但是,您可以投影到匿名类型或DTO上:

1
2
3
4
5
public class ProductDTO
{
    public string Name { get; set; }
    // Other field you may need from the Product entity
}

您的方法将返回DTO的列表。

1
2
3
4
5
6
public List<ProductDTO> GetProducts(int categoryID)
{
    return (from p in db.Products
            where p.CategoryID == categoryID
            select new ProductDTO { Name = p.Name }).ToList();
}


可以投影到匿名类型,然后从匿名类型投影到模型类型

1
2
3
4
5
6
7
public IEnumerable<Product> GetProducts(int categoryID)
{
    return (from p in Context.Set<Product>()
            where p.CategoryID == categoryID
            select new { Name = p.Name }).ToList()
           .Select(x => new Product { Name = x.Name });
}

编辑:我会更具体一点,因为这个问题引起了很多关注。

您不能直接投影到模型类型(ef限制),因此没有办法解决这个问题。唯一的方法是投射到匿名类型(第一次迭代),然后投射到模型类型(第二次迭代)。

请注意,当您以这种方式部分加载实体时,它们不能被更新,因此它们应该保持分离状态。

我从来没有完全理解为什么这是不可能的,并且这个线程上的答案没有给出有力的理由来反对它(主要是关于部分加载的数据)。在部分加载状态下,实体无法更新是正确的,但随后,该实体将被分离,因此不可能意外尝试保存它们。

考虑一下我上面使用的方法:结果仍然有一个部分加载的模型实体。此实体已分离。

考虑此(希望存在)可能的代码:

1
2
3
return (from p in Context.Set<Product>()
        where p.CategoryID == categoryID
        select new Product { Name = p.Name }).AsNoTracking().ToList();

这也可能导致分离实体的列表,因此我们不需要进行两次迭代。编译器会很聪明地看到已经使用了asnotracking(),这将导致分离的实体,因此它可以允许我们这样做。但是,如果省略了asnotracking(),它可能会抛出与现在抛出的异常相同的异常,以警告我们需要对我们想要的结果足够具体。


我发现有另一种方法是有效的,您必须构建一个从产品类派生的类并使用它。例如:

1
2
3
4
5
6
7
8
public class PseudoProduct : Product { }

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new PseudoProduct() { Name = p.Name};
}

不确定这是否是"允许的",但它是有效的。


以下是一种不声明附加类的方法:

4

但是,仅当您希望在单个实体中组合多个实体时才使用此选项。上述功能(简单的产品到产品映射)如下所示:

1
2
3
4
5
6
7
8
9
public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select p;
    var products = query.ToList();

    return products;
}

另一个简单的方法:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public IQueryable<Product> GetProducts(int categoryID)
{
    var productList = db.Products
        .Where(p => p.CategoryID == categoryID)
        .Select(item =>
            new Product
            {
                Name = item.Name
            })
        .ToList()
        .AsQueryable(); // actually it's not useful after"ToList()" :D

    return productList;
}


您可以使用它,它应该可以工作——>在使用select创建新列表之前,必须使用toList

1
2
3
db.Products
    .where(x=>x.CategoryID == categoryID).ToList()
    .select(x=>new Product { Name = p.Name}).ToList();


您可以通过使用数据传输对象(DTO)来解决这个问题。

这些有点像视图模型,您可以在其中放入所需的属性,并可以在控制器中手动映射它们,或者使用第三方解决方案(如automapper)。

通过DTO,您可以:

  • 使数据可序列化(JSON)
  • 去掉循环引用
  • 通过保留不需要的属性来减少网络流量(ViewModelWise)
  • 使用对象展平

我今年在学校学的,这是一个非常有用的工具。


在回答另一个标记为"重复"的问题(见此处)时,我根据Soren的回答,找到了一个简单快捷的解决方案:

1
2
3
4
5
6
7
8
9
10
data.Tasks.AddRange(
    data.Task.AsEnumerable().Select(t => new Task{
        creator_id   = t.ID,
        start_date   = t.Incident.DateOpened,
        end_date     = t.Incident.DateCLosed,
        product_code = t.Incident.ProductCode
        // so on...
    })
);
data.SaveChanges();

音符:只有在任务类(这里称为"事件")上具有导航属性(外键)时,此解决方案才有效。如果你没有,你可以只使用其他发布的解决方案中的一个与"asqueryable()"一起使用。


在许多情况下,不需要转换。考虑一下您想要强类型列表的原因,并评估您是否只想要数据,例如,在Web服务中或者为了显示它。它与类型无关。您只需要知道如何读取它并检查它是否与您定义的匿名类型中定义的属性相同。这就是优化方案,因为您不需要实体的所有字段,这就是匿名类型存在的原因。

一个简单的方法是:

1
IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();

仅添加AsEnumerable():

1
2
3
4
5
6
public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}


如果执行Linq to Entity时,在select结束查询only anonymous types are allowed (new without type)时,不能将ClassTypenew一起使用。

看看我项目中的这段代码

1
2
3
4
5
//...
var dbQuery = context.Set<Letter>()
                .Include(letter => letter.LetterStatus)
                .Select(l => new {Title =l.Title,ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated,LetterStatus = new {ID = l.LetterStatusID.Value,NameInArabic = l.LetterStatus.NameInArabic,NameInEnglish = l.LetterStatus.NameInEnglish} })
                               ^^ without type__________________________________________________________________________________________________________^^ without type

在select闭包中添加了new keyword,即使在complex properties上也会出现此错误。

0

因为它将转换为SQL语句并在SQLServer上执行

那么我什么时候可以在select闭包上使用new with types

如果你和LINQ to Object (in memory collection)打交道,你可以用它。

1
2
3
4
5
6
//opecations in tempList , LINQ to Entities; so we can not use class types in select only anonymous types are allowed
var tempList = dbQuery.Skip(10).Take(10).ToList();// this is list of  so we have to convert it so list of <letter>

//opecations in list , LINQ to Object; so we can use class types in select
list = tempList.Select(l => new Letter{ Title = l.Title, ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated, LetterStatus = new LetterStatus{ ID = l.LetterStatus.ID, NameInArabic = l.LetterStatus.NameInArabic, NameInEnglish = l.LetterStatus.NameInEnglish } }).ToList();
                                ^^^^^^ with type

在我执行了toList查询后,它变成了in memory collection,所以我们可以在select中使用new ClassTypes


如果使用的是实体框架,请尝试从将复杂模型用作实体的dbContext中删除属性。我在将多个模型映射到名为Entity的ViewModel时遇到了同样的问题

1
public DbSet<Entity> Entities { get; set; }

从dbContext中删除条目修复了我的错误。


您可以向集合中添加AsEnumerated,如下所示:

1
2
3
4
5
6
public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products.AsEnumerable()
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}