LINQ外连接具有重复项

时间:2015-02-24 14:32:41

标签: c# linq linq-to-sql linqpad

我在LINQPad中运行此查询。它的工作原理除了ProductSeries有重复的记录。

var query = from etaRecord in EtaRecord_0140

  join productSeriesRecord in ProductSeries
  on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID
  into productSeriesGroup
  from productSeries in productSeriesGroup.DefaultIfEmpty()

  where etaRecord.State == "A"
  select new { EtaRecord = etaRecord, ProductSeriesRecord = productSeries };

query.Dump();

我尝试使用FirstOrDefault()代替DefaultIfEmpty(),但我收到此错误:

  

类型为' LINQPad.User.ProductSeries'的表达式是不允许的   具有源类型的查询表达式中的后续from子句   ' System.Linq.IQueryable&#39 ;.类型推断失败了   拨打' SelectMany'。

如何获取ProductSeries的FirstOrDefault(),以便每个EtaRecord只有一行?

.NET小提琴就在这里:https://dotnetfiddle.net/kRrold

5 个答案:

答案 0 :(得分:1)

看起来你需要分组:

var query = from etaRecord in EtaRecord_0140

join productSeriesRecord in ProductSeries
on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID
into productSeriesGroup
from productSeries in productSeriesGroup.DefaultIfEmpty()

where etaRecord.State == "A"
group productSeries by new { etaRecord.ProductSeriesId, etaRecord } into g
select new 
       { 
         EtaRecord = g.Key.etaRecord, 
         ProductSeriesRecord = g.Select(x => x).FirstOrDefault() 
        };

UPDATED FIDDLE

答案 1 :(得分:0)

您是说您加入的收藏品有重复吗?因为如果是这样,您可以提前对ProductSeries集合进行分组。

var query = from etaRecord in EtaRecord_0140

join productSeriesRecord in ProductSeries.GroupBy(series => series.ProductSeriesID).Select(seriesGroup => seriesGroup.First())
on etaRecord.ProductSeriesID equals productSeriesRecord.ProductSeriesID
into productSeriesGroup
from productSeries in productSeriesGroup.DefaultIfEmpty()

where etaRecord.State == "A"
select new { EtaRecord = etaRecord, ProductSeriesRecord = etaRecord };

query.Dump();

现在假设您正在使用静态列表,而不是数据库。如果它是数据库连接,那么您应该对结果执行不同的操作。事后可以以类似的方式完成。

答案 2 :(得分:0)

我会在子查询中执行此操作:

var query = from etaRecord in EtaRecord_0140
            where etaRecord.State == "A"
            select new 
            { 
                EtaRecord = etaRecord,
                ProductSeriesRecord = 
                  (from productSeriesRecord in ProductSeries
                   where productSeriesRecord.ProductSeriesID == etaRecord.ProductSeriesID
                   select productSeriesRecord).FirstOrDefault()
            };

在LINQ to对象中,这可能是一个低效的操作,因为子查询是为每个etaRecord执行的,但由于整个语句被转换为SQL,查询优化器将处理优化的执行计划。

这是LINQ-to-entities的故事。

LINQ-to-SQL 总是似乎为组连接(join - into)和FirstOrDefault()生成n + 1个查询。我尝试了几种方案,但我无法生成一个查询。我能找到生成一个查询的唯一解决方案是:

var query = from etaRecord in EtaRecord_0140
            where etaRecord.State == "A"
            from productSeriesRecord in
                     ProductSeries
                        .Where(ps => ps.ProductSeriesID == etaRecord.ProductSeriesID)
                        .Take(1)
                        .DefaultIfEmpt()
            select new { EtaRecord = etaRecord, ProductSeriesRecord = productSeries };

因此,放弃了连接语法,并以相当人为的方式查询属于ProductSeries的{​​{1}}的第一条记录。

答案 3 :(得分:0)

问题是您的额外from条款:

from productSeries in productSeriesGroup.DefaultIfEmpty() 

你应该放弃,只需使用:

let productSeries = productSeriesGroup.FirstOrDefault()

...或者只在productSeriesGroup.FirstOrDefault()子句中使用select,如下所示:

var query = from etaRecord in etaRecords            
            join productSeriesRecord in productSeriesRecords
            on etaRecord.ProductSeriesId equals productSeriesRecord.ProductSeriesId
            into productSeriesGroup
            select new { EtaRecord = etaRecord,
                         ProductSeriesRecord = productSeriesGroup.FirstOrDefault() };

如果有任何改变,结果现在是:

Snuh 1 - null
Snuh 2 - null
Snuh 3 - null
Snuh 4 - Description A
Snuh 5 - null
Snuh 6 - Description B

我认为这就是你想要的。

答案 4 :(得分:0)

你应该可以添加一个额外的过滤步骤来按EtaRecord分组,只需选择每组的第一条记录

query  = (from r in query 
         group r by r.EtaRecord.EtaId into results 
         select results.FirstOrDefault());