Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.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
C# 根据最近相关实体的值选择实体集合_C#_Sql Server_Entity Framework_Tsql - Fatal编程技术网

C# 根据最近相关实体的值选择实体集合

C# 根据最近相关实体的值选择实体集合,c#,sql-server,entity-framework,tsql,C#,Sql Server,Entity Framework,Tsql,在我目前的项目中,我经常在几个地方遇到相同类型的困难 最简单的例子如下: 我有两个相关的实体,Request和RequestAction。 每个RequestAction都有一个状态和时间戳,并指向一个请求 我需要根据最近相关RequestAction的状态查询请求集合 示例:获取最近RequestAction状态为打开的所有请求 将数据库非规范化以使最新状态成为请求实体的属性不是一个选项 我需要在我的应用程序的许多其他地方进行相同类型的过滤;我为历史记录和审计记录了许多项目的版本,但我通常只想

在我目前的项目中,我经常在几个地方遇到相同类型的困难

最简单的例子如下:

我有两个相关的实体,Request和RequestAction。 每个RequestAction都有一个状态和时间戳,并指向一个请求

我需要根据最近相关RequestAction的状态查询请求集合

示例:获取最近RequestAction状态为打开的所有请求

将数据库非规范化以使最新状态成为请求实体的属性不是一个选项

我需要在我的应用程序的许多其他地方进行相同类型的过滤;我为历史记录和审计记录了许多项目的版本,但我通常只想查看每个项目的最新版本

我可以想出两种解决办法,但都不是特别有吸引力

1我可以手动编写SQL。这个问题的原始SQL答案涉及将表连接到自身,表的一个实例的时间戳大于另一个实例的时间戳上有一个外部连接,然后查找第二个表为null的记录,表明找不到更大的时间戳。这就是我过去处理这种情况的方式。它在数据库中是高效的,因为它使用索引,不像在其中有max的子查询。然而,我的开发团队被强烈要求使用entity与数据库进行对话,因此强烈反对使用此解决方案

2我可以使用实体将所有值加载到内存中,并在代码中手动循环查找我要查找的值。代码如下所示:

var openRequests = new List<Request>();
var allRequests = context.Requests;
foreach (var request in allRequests)
{
    var recentAction = request.RequestActions
        .OrderByDescending(c => c.ActionTimestamp)
        .First();
    if (recentAction.Status == "Open")
    {
        openRequests.Add(request);
    }
}
虽然这将为我提供我想要的结果,但它效率极低,而且浪费了大量资源和执行时间。我查询的数据库非常大,遍历每个记录确实是不可行的


使用实体是否有一种有效的方法来实现这一点?我发现很难想象我是唯一一个需要这种功能的人

SQL端识别任何打开的请求操作的最新时间戳不就是:

;WITH MostRecentActions AS 
(
  SELECT RequestID, ActionTimestamp,
    /* other relevant RequestAction columns, */
    rn = ROW_NUMBER() OVER (PARTITION BY RequestID ORDER BY ActionTimestamp DESC)
  FROM dbo.RequestActions
  WHERE Status = 'Open'
)
SELECT RequestID, ActionTimestamp /* , other columns */ 
  FROM MostRecentActions
  WHERE rn = 1;      

您可以在一个DB查询中使用LINQ to实体查询:

var openRequests = context.Requests
    .Where(r => r.RequestActions
        .OrderByDescending(ra => ra.ActionTimestamp)
        .Select(ra => ra.Status)
        .FirstOrDefault() == "Open")
    .ToList();
生成的SQL如下所示:

var openRequests = new List<Request>();
var allRequests = context.Requests;
foreach (var request in allRequests)
{
    var recentAction = request.RequestActions
        .OrderByDescending(c => c.ActionTimestamp)
        .First();
    if (recentAction.Status == "Open")
    {
        openRequests.Add(request);
    }
}
选择 [Extent1].[Id]作为[Id], [Extent1][SomeProp1]作为[SomeProp1], [extend1][SomeProp2]作为[SomeProp2],-…等。 来自[dbo]。[Requests]作为[Extent1] 交叉应用选择前1个[项目1]。[状态]作为[状态] 从选择 [Extent2].[Status]作为[Status], [Extent2].[ActionTimestamp]作为[ActionTimestamp] 来自[dbo]。[RequestActions]作为[Extent2] 其中[Extent1].[Id]=[Extent2].[RequestId] AS[Project1] 按[Project1].[ActionTimestamp]DESC AS[Limit1]订购 其中N'Open'=[Limit1].[Status]
我不知道这是否是一个好的、性能良好的SQL。

这接近我要寻找的选项。我想要的是最近的操作是打开的,而不是最近的打开,但是要得到它,我只需要将Status=open条件移动到第二个select。不过,更大的问题是,强烈建议我使用实体与数据库通信,而不是直接查询它。目的是什么?如果很难让EF生成您需要的查询,并且您知道如何使用SQL进行查询…我完全支持您,但我无法说服项目经理。非常感谢!这正是我要找的!据我所知,交叉应用是相当有效的。它需要为外部查询中的每个请求运行内部select,但两个查询都应该相当快。我将由我们的DBA运行它以确认,但看起来这将起作用。