首页 文章

无法在LINQ to Entities查询中构造实体

提问于
浏览
339

存在由实体框架生成的称为产品的实体类型 . 我写了这个查询

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

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

“无法在LINQ to Entities查询中构造实体或复杂类型Shop.Product”

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

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

如何预先形成自定义选择部分?

13 回答

  • 1

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

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

    并且您的方法将返回DTO列表 .

    public List<ProductDTO> GetProducts(int categoryID)
    {
        return (from p in db.Products
                where p.CategoryID == categoryID
                select new ProductDTO { Name = p.Name }).ToList();
    }
    
  • 69

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

    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 });
    }
    

    Edit :由于这个问题得到了很多关注,我将会更具体一些 .

    你不能直接投射到模型类型(EF限制),所以没有办法解决这个问题 . 唯一的方法是投射到匿名类型(第一次迭代),然后投射到模型类型(第二次迭代) .

    另请注意,当您以这种方式部分加载实体时,它们无法更新,因此它们应保持分离状态 .

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

    考虑我上面使用的方法:作为结果,我们仍然有一个部分加载的模型实体 . 该实体是分离的 .

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

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

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

  • 0

    我找到了另一种方法,你必须构建一个派生自Product类的类并使用它 . 例如:

    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};
    }
    

    不确定这是否“允许”,但它确实有效 .

  • 34

    这是一种在不声明aditional类的情况下执行此操作的方法:

    public List<Product> GetProducts(int categoryID)
    {
        var query = from p in db.Products
                where p.CategoryID == categoryID
                select new { Name = p.Name };
        var products = query.ToList().Select(r => new Product
        {
            Name = r.Name;
        }).ToList();
    
        return products;
    }
    

    但是,仅当您要在单个实体中组合多个实体时才使用此选项 . 上述功能(简单的产品到产品映射)是这样完成的:

    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;
    }
    
  • 344

    另一种简单方法:)

    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;
    }
    
  • 1

    您可以使用它并且它应该正常工作 - >在使用select创建新列表之前必须使用 toList

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

    在回答另一个被标记为重复的问题时(see here),我根据Soren的答案找到了一个快速简便的解决方案:

    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();
    

    注意:此解决方案仅在Task类(此处称为“Incident”)上具有导航属性(外键)时才有效 . 如果您没有,可以使用其他已发布的解决方案“AsQueryable()” .

  • 21

    您可以使用数据传输对象(DTO)来解决此问题 .

    这些有点像viewmodel,您可以在其中放入所需的属性,您可以在控制器中手动映射它们,也可以使用AutoMapper等第三方解决方案 .

    使用DTO,您可以:

    • 使数据可串行化(Json)

    • 摆脱循环引用

    • 通过留下不需要的属性来减少网络流量(viewmodelwise)

    • 使用objectflattening

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

  • 0

    只添加AsEnumerable():

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

    如果您正在使用Entity框架,那么尝试从使用您的复杂模型的DbContext中删除属性,因为当将多个模型映射到名为Entity的视图模型时,实体我有同样的问题

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

    从DbContext中删除条目修复了我的错误 .

  • -2

    如果您正在执行 Linq to Entity ,则无法在查询的 select 闭包中使用 ClassTypenew only anonymous types are allowed (new without type)

    看看我项目的这个片段

    //...
    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
    

    您在 complex properties 中的Select闭包中添加了 new keyword ,您将收到此错误

    所以从Linq上的new关键字中删除ClassTypes到Entity查询,

    因为它将转换为sql语句并在SqlServer上执行

    所以 when can I use new with types on select closure?

    你可以使用它正在处理 LINQ to Object (in memory collection)

    //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 <anonymous type> 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

  • 3

    在许多情况下,不需要转换 . 想想你想要强类型List的原因,并评估你是否只想要数据,例如,在Web服务中或显示它 . 这种类型无关紧要 . 您只需要知道如何阅读它并检查它是否与您定义的匿名类型中定义的属性相同 . 这是最佳情况,导致您不需要实体的所有字段,这就是存在匿名类型的原因 .

    一个简单的方法是这样做:

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

    您可以将AsEnumerable添加到您的收藏中,如下所示:

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

相关问题