Linq to entities:寻找匹配

时间:2015-11-14 01:15:11

标签: c# entity-framework linq

我有两个EF课程:

 class Row
 {
    public long CategoryId { get; set; }
    public virtual Category Category { get; set; }
    public long VesselId { get; set; }
    public virtual Vessel Vessel { get; set; }
    public int TruckType { get; set; }
 }

 class RowFilter
 {
    public long? CategoryId { get; set; }
    public virtual Category Category { get; set; }
    public long? VesselId { get; set; }
    public virtual Vessel Vessel { get; set; }
    public int? TruckType { get; set; }
    public long? PortId { get; set; }
    public virtual Port Port { get; set; }

    public bool IsMatch(Row row) 
    {
       if (CategoryId == null || CategoryId == row.CategoryId) {
         if (VesselId == null || VesselId == row.VesselId) {
           if (TruckType == null || TruckType == row.TruckType) {
             if (PortId == null || PortId == row.Vessel.PortId) {
                return true;
             }
           }
         }
       }

       return false;
    }
 }


那就是:
如果Filter为该行返回true,则RowIsMatch()匹配。


我有一个行列表,采用IQueryable方式:

   var rows = dbContext.Rows.AsQueryable().Where(...);

...对于每一行,我想选择(项目)行本身以及与此行匹配的过滤器列表。我可以用Linq-to-Objects的方式(“在内存中”)轻松地完成这个任务:

   // Linq-to-objects

   rows.ToList().Select(r => new 
   {
       row = r,
       filters = dbContext.RowsFilters.Where(f => f.IsMatch(r))
   };

问题是...... 是否可以使用Linq-to-Entities进行?(sql,而非“在内存中”)

在静态世界中,我会有这些导航属性:

 class Row
 {
    ...
    public virtual List<RowFilter> RowFilters { get; set; }
 }

 class RowFilter
 {
    ...
    public virtual List<Rows> Rows { get; set; }
 }

但......这意味着需要进行大量更新:创建新的RowFilter时,创建新的Row等时

2 个答案:

答案 0 :(得分:1)

您可以执行以下步骤:

修改IsMatch方法以返回Expression<Func<Row, bool>>类型并按如下方式实现:

public Expression<Func<Row, bool>> IsMatch()
{
    Expression<Func<Row, bool>> filter = r => (CategoryId == null || CategoryId == r.CategoryId)
                    && (VesselId == null || VesselId == r.VesselId)
                    && (TruckType == null || TruckType == r.TruckType)
                    && (PortId == null || PortId == r.PortId);

    return filter;
}

然后就这样使用它:

var rowFilter = new RowFilter { PortId = 1, CategoryId = 2, TruckType = 3, VesselId = 4 };
var query = context.Rows.Where(rowFilter.IsMatch());

所有linq都被翻译成SQL,然后在服务器端执行。 EF生成的SQL如下所示:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[CategoryId] AS [CategoryId], 
    [Extent1].[VesselId] AS [VesselId], 
    [Extent1].[TruckType] AS [TruckType], 
    [Extent1].[PortId] AS [PortId]
FROM [dbo].[Rows] AS [Extent1]
WHERE (@p__linq__0 IS NULL OR @p__linq__1 = [Extent1].[CategoryId]) AND (@p__linq__2 IS NULL OR @p__linq__3 = [Extent1].[VesselId]) AND (@p__linq__4 IS NULL OR @p__linq__5 = [Extent1].[TruckType]) AND (@p__linq__6 IS NULL OR @p__linq__7 =  CAST( [Extent1].[PortId] AS bigint))

答案 1 :(得分:0)

您可以使用以下查询:

var query = from r in context.Rows
    from f in context.RowFilters.Where(f =>
        (f.CategoryId == null || f.CategoryId == r.CategoryId) &&
        (f.VesselId == null || f.VesselId == r.VesselId) &&
        (f.TruckType == null || f.TruckType == r.TruckType) &&
        (f.PortId == null || f.PortId == r.Vessel.PortId))
        .DefaultIfEmpty()
    let x = new {r, f}
    group x by x.r
    into gr
    select new
    {
        row = gr.Key,
        filters = gr.Select(y => y.f).Where(yf => yf != null)
    };

var result = query.ToList();

以下是另一种语法:

var query = context.Rows
    .SelectMany(r =>
        context.RowFilters.Where(f =>
            (f.CategoryId == null || f.CategoryId == r.CategoryId) &&
            (f.VesselId == null || f.VesselId == r.VesselId) &&
            (f.TruckType == null || f.TruckType == r.TruckType) &&
            (f.PortId == null || f.PortId == r.Vessel.PortId))
            .DefaultIfEmpty()
            .Select(f => new {r, f}))
    .GroupBy(x => x.r)
    .Select(x => new
    {
        row = x.Key,
        filters = x.Select(y => y.f).Where(yf => yf != null)
    });