我有两个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,则Row
与IsMatch()
匹配。
我有一个行列表,采用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等时
答案 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)
});