无法从实体框架/SQL 服务器获取正确的行

本文关键字:获取 服务器 SQL 实体 框架 | 更新日期: 2023-09-27 18:34:38

我在SQL Server中有一个表:

select * from TaskSave
TaskID  SaveTypeID  ResultsPath PluginName                      PluginConfiguration
---------------------------------------------------------------------
  92       1         NULL       NULL                            NULL
  92       7         NULL       RGP_MSWord.WordDocumentOutput   www|D:'Users'Peter'Documents'Temp
  92       7         NULL       RGP_WC.WCOutput                 wcwc|D:'Users'Peter'Documents'Temp|.docx|123|456|789

我正在尝试使用 C#/实体框架阅读:

public static List<TaskSave> GetSavesPerTask(Task task)
{
    using (Entities dbContext = new Entities())
    {
        var savesPerTask = from p in dbContext.TaskSaves.Include("SaveType").Where(q => q.TaskID == task.TaskID) select p;
        //return savesPerTask.ToList();
        // DEBUG
        var x = savesPerTask.ToList();
        foreach (var y in x)
        {
            Console.WriteLine("SaveTypeID {0}, Plugin Name {1}", y.SaveTypeID, y.PluginName);
        }
        return x;
    }
}

业务规则指出 TaskID + SaveTypeID + PluginName 是唯一的,即一个任务可以有多个插件。如果保存类型不是"插件",则任务 ID + 保存类型ID 必须是唯一的。

我的问题是GetSavesPerTask方法返回错误的结果。它检索三行,但第 2 行是重复的——我得到 2 行,PluginName RGP_MSWord.WordDocumentOutput,而不是RGP_WC.WCOutput行。调试打印显示:

SaveTypeID 1, Plugin Name 
SaveTypeID 7, Plugin Name RGP_MSWord.WordDocumentOutput
SaveTypeID 7, Plugin Name RGP_MSWord.WordDocumentOutput

调试器和数据的最终用户都同意第三行不存在。

我尝试删除包含子句,但这对结果集没有影响。下面是调试器报告的 SQL(来自更简单的情况(:

savesPerTask    {SELECT 
    [Extent1].[TaskID] AS [TaskID], 
    [Extent1].[SaveTypeID] AS [SaveTypeID], 
    [Extent1].[ResultsPath] AS [ResultsPath], 
    [Extent1].[PluginName] AS [PluginName], 
    [Extent1].[PluginConfiguration] AS [PluginConfiguration]
    FROM (SELECT 
    [TaskSave].[TaskID] AS [TaskID], 
    [TaskSave].[SaveTypeID] AS [SaveTypeID], 
    [TaskSave].[ResultsPath] AS [ResultsPath], 
    [TaskSave].[PluginName] AS [PluginName], 
    [TaskSave].[PluginConfiguration] AS [PluginConfiguration]
    FROM [dbo].[TaskSave] AS [TaskSave]) AS [Extent1]
    WHERE [Extent1].[TaskID] = @p__linq__0}

我已经将 SQL 从调试器复制并粘贴到 SSMS 中,它得到了正确的结果。我尝试在 EF 模型中删除并重新创建这两个表。我已经重新编译了很多次,因为我尝试了不同的事情(添加调试、刷新模型、将返回移动到 using 块之外等(。

TaskSave表没有主键,所以我在编译过程中收到错误 6002(我忽略了它,因为它似乎不会影响其他任何东西,而且我在互联网上找不到任何解决方法(

"Database.dbo.TaskSave"没有定义主键。已推断出键,并将定义创建为只读表/视图。

如何修复返回正确行的GetSavesPerTask方法?我犯了什么愚蠢的错误?我还有许多其他表和 CRUD 操作,它们似乎按预期工作。

这是我的版本

  • .NET 4.6.01055
  • C# 2015
  • SQL Server 2014
  • SQL Server 数据工具 14.0.50730.0

无法从实体框架/SQL 服务器获取正确的行

最有可能的是,问题只是缺少显式主键。无论如何,数据库中的每个表都应该有一个主键......

此处发生的情况是:由于没有主键,EF 将仅使用表(或视图(中的所有不可为空的列作为"替换"PK。不确定您的情况是哪些 - 可能(TaskIDSaveTypeID(??

当 EF 读取数据时,它将:

  • 读取行
  • 检查主键(或在这种情况下的"替身"PK(
  • 如果它已经读取了具有该PK的行 - 它只是复制它已经拥有的那行 - 它将忽略表/视图中的任何非PK列!

因此,在您的情况下,一旦读取了第一行并TaskID = 92, SaveTypeID = 7了"替身"PK,则具有这两个值的任何进一步行都将仅获得已读取的值 - 无论数据库中存储了什么!

解决方案:正如我之前所说:每个都应该有一个适当的主键来唯一标识每一行!

在您的情况下,我建议只添加这样的代理键:

ALTER TABLE dbo.TaskSave
ADD TaskSaveID INT IDENTITY(1,1) NOT NULL
ALTER TABLE dbo.TaskSave
ADD CONSTRAINT PK_YourTableName
    PRIMARY KEY CLUSTERED(TaskSaveID)

现在,每一行都有自己唯一的TaskSaveID,EF可以检测到正确的PK,你所有的麻烦都应该消失了。