2015-11-14 2 views
1

У меня есть эти два класса: EFLinq для юридических лиц: открытие матчей

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 соответствует Row если таковому IsMatch() возвращает истинную для этой строки.


У меня есть список строк, в IQueryable образом:

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

... и для каждой строки, я хочу, чтобы выбрать (проект) самой строки и список фильтров, которые соответствуют эта строка. Я могу сделать это легко в пути Linq-на-объекты ("память"):

// 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, при создании новой строки и т.д.

+0

Является 'Row.CategoryId' поле или собственность? В вашем коде это похоже на поле. –

+0

собственность, извините ... отредактирует – sports

+0

Некоторые свойства 'RowFilter' не имеют имени. Я думаю, вы пропустили их. –

ответ

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) 
    }); 
+0

wow! выглядит многообещающим ... как вы напишете первую часть: 'от r в контексте. Режимы из f в контексте. RowFilters' с лямбда-выражениями? – sports

+1

@sports, см. Мое обновление –

+0

Вау, используя 'context' внутри' SelectMany', кажется, это трюк: p awesome – sports

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 затем выполняется на стороне сервера. Сгенерированный SQL с помощью EF выглядит следующим образом:

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)) 
+0

С вашим запросом:' var query = context.Rows.Where (rowFilter.IsMatch()); 'вы возвращаете все строки, которые соответствуют одному RowFilter. Я хочу немного по-другому, я цитирую себя: «Я хочу выбрать (проецировать) самую строку и список фильтров, соответствующих этой строке». То есть: массив с этой формой: '[{someRow, filters}, {otherRow, filters}, ...]' – sports

+0

Нет. Я не возвращаю все строки. rowFilter' является экземпляром 'RowFilter'.Возвращенные строки соответствуют значениям, заданным для свойств 'rowFilter'instance. – CodeNotFound

+0

Проверьте сгенерированный SQL в моем ответе, вы увидите, что есть предложение where. – CodeNotFound

Смежные вопросы