C#Web API LINQ实体查询-将AnonymousType强制转换为类对象

本文关键字:转换 对象 AnonymousType LINQ API 实体 查询 C#Web | 更新日期: 2023-09-27 18:00:51

我得到了以下代码:

public List<Product> ListAll()
{
    List<Product> products = new List<Product>();
    var db_products = db.Products
        .Where(p => p.Enabled == true)
        .OrderBy(p => p.Name)
        .Select(p => new
        {
            ProductId = p.ProductId,
            Name = p.Name,
            ...
        })
        .ToList();
    foreach (var dbP in db_products)
    {
        Product p = new Product();
        p.ProductId = dbP.ProductId;
        p.Name = dbP.Name;
        ...
        products.Add(p);
    }
    return products;
}

它按照我的意愿工作,因为它成功地返回了一个List of Product对象。尽管如此,难道没有一种方法可以不使用foreach循环,这样我就可以立即投射它吗?

我确实尝试过:

public List<Product> ListAll()
{
    List<Product> products = db.Products
        .Where(p => p.Visible == true)
        .OrderBy(p => p.Name)
        .Select(p => new
        {
            ProductId = p.ProductId,
            Name = p.Name,
            ...
        })
        .AsEnumerable()
        .Cast<Product>()
        .ToList();
    return products;
}

public List<Product> ListAll()
{
    List<Product> products = db.Products
        .Where(p => p.Visible == true)
        .OrderBy(p => p.Name)
        .Select(p => new Product
        {
            ProductId = p.ProductId,
            Name = p.Name,
            ...
        })
        .ToList();
    return products;
}

但两者都不起作用。对于第二个,我得到以下错误:

at System.Data.Objects.ELinq.ExpressionConverter.CheckInitializerType(Type type)
   at System.Data.Objects.ELinq.ExpressionConverter.MemberInitTranslator.TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SelectTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Data.Entity.Internal.Linq.InternalQuery`1.GetEnumerator()
   at System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at SeashellBrawlCorvee...ListAll() in c:'Users'...'Documents'Visual Studio 2012'Projects'seashell-brawl-corvee'seashell-brawl-corvee'...'ProductController.cs:line 149
   at SeashellBrawlCorvee...ProductsRepository..ctor() in c:'Users'...'Documents'Visual Studio 2012'Projects'seashell-brawl-corvee'seashell-brawl-corvee'...'ProductsRepository.cs:line 21
   at SeashellBrawlCorvee...ProductsController..cctor() in c:'Users'...'Documents'Visual Studio 2012'Projects'seashell-brawl-corvee'seashell-brawl-corvee'...'ProductsController.cs:line 16

如果有人知道一个解决方案,我会很感激,否则我只会坚持foreach循环。

提前感谢您的回复。

C#Web API LINQ实体查询-将AnonymousType强制转换为类对象

当你从ORM调用一个方法时,它可以返回一个代理,所以当web api需要对它进行反序列化时,你会遇到很多问题。

一种方法是创建一个ViewModel。对于示例,创建一个包含所需信息的类:

public class ProductViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    // properties
}

当您查询数据时,尝试返回一个List<ProductViewModel>,例如:

return db.Products
    .Where(p => p.Visible == true)
    .OrderBy(p => p.Name)
    .Select(p => new ProductViewModel() { Id = p.ProductId, Name = p.Name })
    .ToList();

使用ViewModel暴露于ViewRequest是一种很好的实践。

我假设ProductDbContext的一个类。

如果是这样,LINQ将尝试将您的投影(Select(映射到DB,因此它将尝试构建一个查询,但不知道如何进行。

解决方案是:

  • 以便具体化查询,因此创建查询并在此时执行,使用.ToList()(或ToArray()ToDictionary()…(
  • 或者将其强制转换为enumerable,这样它就不再可查询,也不能使用AsEnumerable()将其映射到模型/DB

然后Select投影可以在不映射到EF模型/DB:的情况下完成

public List<Product> ListAll()
{
  List<Product> products = db.Products
      .Where(p => p.Visible == true)
      .OrderBy(p => p.Name)
      . AsEnumerable() // or .ToList()
      .Select(p => new Product
      {
          ProductId = p.ProductId,
          Name = p.Name,
          ...
      })
      .ToList();

一般来说,使用AsEnumerable更有效。有关更多信息,请参阅:

  • 这个问题的广泛答案:两者之间有什么区别。ToList((。AsEnumerable((,AsQueryable((
  • 并且这个问答;A: AsEnumerable((对LINQ实体的影响是什么