C# 无法在LINQ to Entities查询中构造实体
实体框架生成了一个名为C# 无法在LINQ to Entities查询中构造实体,c#,entity-framework,C#,Entity Framework,实体框架生成了一个名为Product的实体类型。 我写了这个查询 public IQueryable<Product> GetProducts(int categoryID) { return from p in db.Products where p.CategoryID== categoryID select new Product { Name = p.Name}; } 但是当我使用选择p而不是选择新产品{Name=p.Na
Product
的实体类型。
我写了这个查询
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
但是当我使用选择p
而不是选择新产品{Name=p.Name}代码>它工作正常
如何执行自定义选择节?您不能(也不应该能够)投影到映射的实体上。但是,您可以投影到匿名类型或:
您的方法将返回DTO的列表
public List<ProductDTO> GetProducts(int categoryID)
{
return (from p in db.Products
where p.CategoryID == categoryID
select new ProductDTO { Name = p.Name }).ToList();
}
public List GetProducts(int categoryID)
{
返回(来自数据库产品中的p)
其中p.CategoryID==CategoryID
选择newproductdto{Name=p.Name}).ToList();
}
另一种简单的方法:)
public IQueryable GetProducts(int categoryID)
{
var productList=db.Products
.其中(p=>p.CategoryID==CategoryID)
.选择(项目=>
新产品
{
Name=项目名称
})
托利斯先生()
.AsQueryable();//实际上,在“ToList()”之后它没有用处:D
返回产品列表;
}
我发现还有另一种方法可行,您必须构建一个从产品类派生的类并使用它。例如:
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};
}
公共类伪产品:产品{}
公共IQueryable GetProducts(内部类别ID)
{
从p返回,单位为db.Products
其中p.CategoryID==CategoryID
选择new PseudoProduct(){Name=p.Name};
}
不确定这是否是“允许的”,但它是有效的。这里有一种不声明传统类的方法:
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 GetProducts(int categoryID)
{
var query=来自数据库产品中的p
其中p.CategoryID==CategoryID
选择新的{Name=p.Name};
var products=query.ToList()。选择(r=>newproduct
{
名称=r.名称;
}).ToList();
退货产品;
}
但是,仅当您希望将多个实体合并到一个实体中时才使用此选项。上述功能(简单的产品到产品映射)是这样完成的:
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;
}
int categoryID = 1;
var prods = repository.GetProducts(categoryID);
public List GetProducts(int categoryID)
{
var query=来自数据库产品中的p
其中p.CategoryID==CategoryID
选择p;
var products=query.ToList();
退货产品;
}
您可以投影到匿名类型,然后从它投影到模型类型
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 });
}
public IEnumerable GetProducts(int categoryID)
{
返回(来自Context.Set()中的p)
其中p.CategoryID==CategoryID
选择新建{Name=p.Name}).ToList()
.Select(x=>新产品{Name=x.Name});
}
编辑:由于这个问题受到了广泛关注,我将更具体一点
您不能直接投影到模型类型中(EF限制),因此无法解决此问题。唯一的方法是投射到匿名类型(第一次迭代),然后投射到模型类型(第二次迭代)
还请注意,当您以这种方式部分加载实体时,它们无法更新,因此它们应该保持分离状态
我从来没有完全理解为什么这是不可能的,并且这个线程上的答案没有给出强烈的反对理由(主要是关于部分加载的数据)。在部分加载状态下,实体无法更新是正确的,但这样,该实体将被分离,因此不可能意外尝试保存它们
考虑我在上面使用的方法:结果我们仍然有一个部分加载的模型实体。此实体已分离
考虑以下(希望存在)可能的代码:
return (from p in Context.Set<Product>()
where p.CategoryID == categoryID
select new Product { Name = p.Name }).AsNoTracking().ToList();
return(来自Context.Set()中的p)
其中p.CategoryID==CategoryID
选择新产品{Name=p.Name}).AsNoTracking().ToList();
这还可能导致分离实体的列表,因此我们不需要进行两次迭代。编译器会很聪明地看到使用了AsNoTracking(),这将导致分离实体,因此它可以允许我们这样做。但是,如果省略了AsNoTracking(),它可能会抛出与现在相同的异常,以警告我们需要对我们想要的结果足够具体。针对另一个标记为重复()的问题,我根据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();
注:
此解决方案仅在任务类(此处称为“事件”)上具有导航属性(外键)时有效。
如果您没有,您可以使用其他发布的解决方案中的一个与“AsQueryable()”配合使用。如果您使用的是实体框架,那么请尝试从使用复杂模型作为实体的DbContext中删除属性
我在将多个模型映射到名为Entity的viewmodel时遇到了相同的问题
public DbSet<Entity> Entities { get; set; }
公共数据库集实体{get;set;}
从DbContext中删除条目修复了我的错误。您可以使用它,它应该可以工作-->在使用select创建新列表之前,必须使用toList
:
db.Products
.where(x=>x.CategoryID == categoryID).ToList()
.select(x=>new Product { Name = p.Name}).ToList();
如果您正在执行Linq to Entity
,则在select
closure of query中,不能将ClassType
与new
一起使用
看看我的项目的这个片段
//...
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
在我对查询执行了ToList
之后,它就变成了内存集合
,因此我们可以在select中使用新类类型
您可以通过使用数据传输对象(DTO)来解决这个问题
这些有点像viewmodels,您可以在其中输入所需的属性,并可以在控制器中手动映射它们,或者使用第三方解决方案(如AutoMapper)来映射它们
使用DTO,您可以:
- 使数据可序列化(Json)
- 去掉循环引用
- 保留不需要的属性以减少网络流量(viewmodelwise)
- 使用对象展平
今年我在学校里学的
db.Products
.where(x=>x.CategoryID == categoryID).ToList()
.select(x=>new Product { Name = p.Name}).ToList();
//...
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
//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
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
public class ProductViewModel
{
[Key]
public string ID { get; set; }
public string Name { get; set; }
}
int categoryID = 1;
var prods = repository.GetProducts(categoryID);
public IEnumerable<ProductViewModel> GetProducts(int categoryID)
{
List<ProductViewModel> lstPVM = new List<ProductViewModel>();
var anonymousObjResult = from p in db.Products
where p.CategoryID == categoryID
select new
{
CatID = p.CategoryID,
Name = p.Name
};
// NOTE: If you have any dates that are nullable and null, you'll need to
// take care of that: ClosedDate = (DateTime?)p.ClosedDate ?? DateTime.Now
// If you want a particular date, you have to define a DateTime variable,
// assign your value to it, then replace DateTime.Now with that variable. You
// cannot call a DateTime.Parse there, unfortunately.
// Using
// new Date("1","1","1800");
// works, though. (I add a particular date so I can edit it out later.)
// I do this foreach below so I can return a List<ProductViewModel>.
// You could do: return anonymousObjResult.ToList(); here
// but it's not as clean and is an anonymous type instead of defined
// by a ViewModel where you can control the individual field types
foreach (var a in anonymousObjResult)
{
ProductViewModel pvm = new ProductViewModel();
pvm.ID = a.CatID;
pvm.Name = a.Name;
lstPVM.Add(rvm);
}
// Obviously you will just have ONE item there, but I built it
// like this so you could bring back the whole table, if you wanted
// to remove your Where clause, above.
return lstPVM;
}
List<ProductViewModel> lstProd = new List<ProductViewModel>();
if (prods != null)
{
// For setting the dates back to nulls, I'm looking for this value:
// DateTime stdDate = DateTime.Parse("01/01/1800");
foreach (var a in prods)
{
ProductViewModel o_prod = new ReportViewModel();
o_prod.ID = a.ID;
o_prod.Name = a.Name;
// o_prod.ClosedDate = a.ClosedDate == stdDate ? null : a.ClosedDate;
lstProd.Add(o_prod);
}
}
return View(lstProd); // use this in your View as: @model IEnumerable<ProductViewModel>