EF获取具有子列表的父实体列表

时间:2015-04-21 11:55:32

标签: c# entity-framework linq-to-entities

我必须在我的项目中接下来的两个实体

public class Product
{
    public Product()
    {
        this.ProductImages = new HashSet<ProductImage>();
        this.ProductParams = new HashSet<ProductParam>();
    }
    public int ID { get; set; }
    public int BrandID { get; set; }
    public int CodeProductTypeID { get; set; }
    public string SeriaNumber { get; set; }
    public string ModelNumber { get; set; }
    public decimal Price { get; set; }
    public bool AvailableInStock { get; set; }

    public virtual Brand Brand { get; set; }
    public virtual CodeProductType CodeProductType { get; set; }
    public virtual ICollection<ProductImage> ProductImages { get; set; }
    public virtual ICollection<ProductParam> ProductParams { get; set; }

}

public class ProductParam
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int CodeProductParamId { get; set; }
    public string Value { get; set; }

    public virtual Product Product { get; set; }
    public virtual CodeProductParam CodeProductParam { get; set; }
}

我希望获得具有指定参数列表的产品列表

var prodParamCritria = new List<ProductParam>() 
{
new ProductParam(){CodeProductParamId =1, Value="Black" }, 
new ProductParam(){CodeProductParamId =2, Value="Steal"}
};
在SQL中

我可以使用EXISTS子句twise

来实现
SELECT *
FROM   Products p
WHERE  EXISTS (
           SELECT *
           FROM   ProductParams pp
           WHERE  pp.ProductId = p.ID
                  AND (pp.CodeProductParamId = 1 AND pp.[Value] = N'Black')
       )
       AND EXISTS (
               SELECT *
               FROM   ProductParams pp
               WHERE  pp.ProductId = p.ID
                      AND pp.CodeProductParamId = 2
                      AND pp.[Value] = N'Steal'
           )

如何通过EF方法或linq获得相同的结果

3 个答案:

答案 0 :(得分:1)

我想这样的事情应该有用

db.Product.Where(x => x.ProductParams.FirstOrDefault(y => y.CodeProductParamId == 1) != null && x.ProductParams.FirstOrDefault(y => y.CodeProductParamId == 2) != null).ToList();

或更好

db.Product.Where(x => x.ProductParams.Any(y => y.CodeProductParamId == 1) && x.ProductParams.Any(y => y.CodeProductParamId == 2)).ToList();

好的,如果您需要查询列表prodParamCriteria中的参数,它将如下所示:

db.Product.Where(x => prodParamCritria.All(c=> x.ProductParams.Any(p=>p.CodeProductParamId == c.CodeProductParamId && p.Value== c.Value))).ToList();

我忘了复杂的类型不能在查询数据库中使用,所以我建议你将prodParamCriteria转换为字典并在查询中使用它

Dictionary<int, string> dctParams = prodParamCritria.ToDictionary(x => x.CodeProductParamId , y=>y.Value);
db.Product.Where(x => dctParams.All(c => x.ProductParams.Any(p=> p.CodeProductParamId == c.Key && p.Value== c.Value))).ToList();

答案 1 :(得分:1)

试试这个:

  var products= db.Products.Where(p=>p.ProductParams.Any(pp=>pp.CodeProductParamId == 1 && pp.Value == "Black") && 
                                     p.ProductParams.Any(pp=>pp.CodeProductParamId == 2 && pp.Value == "Steal"));

更新

使用ProductParam列表将其用作过滤器的问题是,EF不知道如何将PodructParam对象转换为SQL,如果您执行类似的查询这样:

 var products2 = db.Products.Where(p => prodParamCritria.All(pp => p.ProductParams.Any(e => pp.CodeProductParamId == e.CodeProductParamId && pp.Value == e.Value)));

在@BostjanKodre的回答中你会得到NotSupportedException

我有一个解决方案,但你可能不喜欢它。要解决该问题,您可以在致电ToList之前调用Where方法。这样你就可以将所有产品都带到内存中,你可以使用Linq to Object而不是Linq to Entities,但这是非常低效的,因为你在内存中而不是在DB中进行过滤。

var products3 = db.Products.ToList().Where(p => prodParamCritria.All(pp => p.ProductParams.Any(e => pp.CodeProductParamId == e.CodeProductParamId && pp.Value == e.Value)));

如果您希望按一个条件筛选,那么这可能更简单,您将能够使用特定基本类型的列表进行筛选。例如,如果您希望仅按CodeProductParamId过滤产品,则可以执行以下操作:

  var ids = new List<int> {1, 2};
  var products = db.Products.Where(p => ids.All(i=>p.ProductParams.Any(pp=>pp.CodeProductParamId==i))).ToList();

这是因为您使用的是基本类型而不是自定义对象。

答案 2 :(得分:0)

另一种变化:

IEnumerable<Int32> lis = prodParamCritria.Select(x => x.CodeProductParamId).ToList();
var q = Products.Select(
        x => new { 
            p = x,
            cs = x.ProductParams.Where(y => lis.Contains(y.Id))
        }
    ).Where(y => y.cs.Count() == lis.Count()).
    ToList();

有一个命名类(或者可能没有,但不是在linqpad中)

public class daoClass {
    public Product p {get; set;}
    public Int32 cs {get; set;}
}

IEnumerable<Int32> lis = prodParamCritria.Select(x => x.CodeProductParamId).ToList();
var q = Products.Select(
        x => new daoClass { 
            p = x,
            cs = x.ProductParams.Where(y => lis.Contains(y.Id))
        }
    ).Where(y => y.cs.Count() == lis.Count()).
    SelectMany(y => y.p).
    ToList();