Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在LINQ to实体查询中选择具有记录的聚合(分组)统计信息_Linq_C# 4.0_Linq To Entities_Group By_Outer Join - Fatal编程技术网

在LINQ to实体查询中选择具有记录的聚合(分组)统计信息

在LINQ to实体查询中选择具有记录的聚合(分组)统计信息,linq,c#-4.0,linq-to-entities,group-by,outer-join,Linq,C# 4.0,Linq To Entities,Group By,Outer Join,我有一个查询,我正试图从SQL(T-SQL)移植到LINQ,再移植到Entities 4.0(C#)。结果集包含标准“详细信息行”和聚合“统计”信息的组合 原始SQL使用标准的select left连接到聚合信息,类似于: SELECT UserId, Name, Email, ISNULL(Stats.TotalPosts, 0) as TotalPosts, Stats.LastPost FROM Users LEFT OUTER JOIN (

我有一个查询,我正试图从SQL(T-SQL)移植到LINQ,再移植到Entities 4.0(C#)。结果集包含标准“详细信息行”和聚合“统计”信息的组合

原始SQL使用标准的select left连接到聚合信息,类似于:

SELECT 
    UserId, 
    Name, 
    Email, 
    ISNULL(Stats.TotalPosts, 0) as TotalPosts,
    Stats.LastPost
FROM Users
LEFT OUTER JOIN
(
    SELECT UserId, COUNT(*) as TotalPosts, MAX(DatePosted) as LastPost
    FROM Articles
    GROUP BY UserId
) as Stats ON Stats.UserId = Users.UserID
出于性能原因,SELECT语句中使用左连接而不是子查询-返回多个聚合统计信息(总文章数和最后一篇文章的日期)

我在C#4.0中将其转换为LINQ to Entities查询方面取得了一些成功,但我不完全确定join应该如何与group语句相结合。我想我是从SQL的角度来考虑这个问题,而不是正确地使用LINQ

我成功地将统计数据分解为一个单独的查询:

var stats =
(
    from a in entities.Articles
    group a by a.UserId into g
    select new
    {
        UserId = g.Key,
        TotalPosts = g.Count(),
        LastUpdated = g.Max(i => i.DatePosted)
    }
);

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = s.TotalPosts,
        LastUpdated = s.LastUpdated
    }
);
不幸的是,LINQ查询中使用的联接过滤掉了所有未提交任何文章的用户

通过包含DefaultIfEmpty切换到与外部联接等效的连接会导致其他问题-我只能为TotalPosts返回“null”,而不是0。即使在select中使用“TotalPosts=(s.TotalPosts==null)?0:s.TotalPosts”,也会引发异常,除非TotalPosts属性可为null

以这种方式组合详细信息行和聚合信息的最佳实践是什么

谢谢

试试这个:

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId into g
    from a in g.DefaultIfEmpty()
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = a.TotalPosts,
        LastUpdated = a.LastUpdated
    }
);

要获得外部联接,需要使用
DefaultIfEmpty
。 要解决空值问题,您可以尝试

TotalPosts = s.TotalPosts.GetValueOrDefault(),
或者,如果s.TotalPosts不知何故没有显示为
int?
,您可以尝试类似的黑客攻击

TotalPosts = ((int?)s.TotalPosts).GetValueOrDefault(0),

一个选项是确保
stats
查询中的相应属性可为空。如果可能的话,LINQ to实体将进行必要的调整以实现这一目标。然后像往常一样执行左外连接

var stats =
(
    from a in entities.Articles
    group a by a.UserId into g
    select new
    {
        UserId = g.Key,
        TotalPosts = (int?)g.Count(),
        LastUpdated = g.Max(i => i.DatePosted)
    }
);

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId into joinedStats
    from s in joinedStats.DefaultIfEmpty() // do left outer join
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = s.TotalPosts,  // null if doesn't contain stats
        LastUpdated = s.LastUpdated // default DateTime if doesn't contain stats
    }
);

我无法编译这个。我的LINQ知识不是很好,但是GetValueOrDefault不是一个“int”或“int”的扩展方法,这很有帮助,但是我仍然存在TotalPosts为null而不是零的问题(并且要求它被键入为“int?”。有没有办法在查询中解决这个问题,并滚动“stats”在同一条语句中?
TotalPosts
的类型为
int?
,如果相应的用户没有任何数据,则其值为
null
。这正是您想要的,对吗?当然可以将
stats
查询与主查询合并,但它现在的方式更干净。我不会这样做如果我是你。@ShadowChaser:linq的工作方式是将查询合并在一起没有任何好处。与SQL不同,它不能提高性能。将它们分开实际上更清晰。很高兴听到我将它们分开的消息!查询工作得很好。我将TotalPosts保留为int?和null,而不是零,并在调用中处理它代码。虽然不完美,但仍然可以很好地工作。谢谢!