有一种名为Product的实体类型是由实体框架生成的。
我写了这个问题
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
下面的代码抛出以下错误:
实体或复杂类型的Shop。产品不能构造在
LINQ到实体查询"
var products = productRepository.GetProducts(1).Tolist();
但是当我使用select p而不是select new Product {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<Product> GetProducts(int categoryID)
{
var query = from p in db.Products
where p.CategoryID == categoryID
select p;
var products = query.ToList();
return products;
}
它不会让您映射回Product,因为这是您正在查询的表。你需要一个匿名函数,然后你可以将它添加到一个ViewModel,并将每个ViewModel添加到List<MyViewModel>并返回这些。这是一个轻微的题外话,但我包含了关于处理可为空日期的警告,因为这是一个难以处理的问题,以防您有任何问题。我是这样处理的。
希望你有一个ProductViewModel:
public class ProductViewModel
{
[Key]
public string ID { get; set; }
public string Name { get; set; }
}
我有依赖注入/存储库框架,我调用一个函数来获取我的数据。以你的帖子为例,在你的Controller函数调用中,它看起来是这样的:
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>
您可以投射到匿名类型,然后从它投射到模型类型
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限制),所以没有办法绕过这个。唯一的方法是投射到匿名类型(第一次迭代),然后投射到建模类型(第二次迭代)。
还请注意,当您以这种方式部分加载实体时,它们不能被更新,因此它们应该保持分离状态。
我从来没有完全理解为什么这是不可能的,这个帖子上的答案也没有给出强烈的反对理由(主要是关于部分加载的数据)。在部分加载状态下实体不能被更新,这是正确的,但是之后,该实体将被分离,因此不可能意外地尝试保存它们。
考虑一下我上面使用的方法:结果我们仍然有一个部分加载的模型实体。该实体已分离。
考虑以下(希望存在)可能的代码:
return (from p in Context.Set<Product>()
where p.CategoryID == categoryID
select new Product { Name = p.Name }).AsNoTracking().ToList();
这也可能导致分离实体的列表,因此我们不需要进行两次迭代。编译器会聪明地看到AsNoTracking()已经被使用,这将导致分离的实体,所以它可以允许我们这样做。但是,如果AsNoTracking()被省略,它可能会抛出与现在抛出的相同的异常,以警告我们需要对我们想要的结果足够具体。