实体框架中的用户定义表生成不正确的查询

Posted

技术标签:

【中文标题】实体框架中的用户定义表生成不正确的查询【英文标题】:User defined table in Entity Framework generating incorrect query 【发布时间】:2020-03-27 21:02:23 【问题描述】:

我认为我目前遇到了 Entity Framework 6 和 ADO.NET 中的错误。由于有最后期限,我不确定我是否可以等待修复此错误,希望有人可以帮助我解决问题。

问题是查询在应该是 0.01 和 0.05 的地方使用了值 1 和 5。然而奇怪的是,0.1 似乎正在工作

当前生成的查询是:(从 SQL Server Profiler 获取)

declare @p3  dbo.someUDT
insert into @p3 values(NULL,5)
insert into @p3 values(5,0.10)
insert into @p3 values(NULL,1)
insert into @p3 values(1,2)

exec sp_executesql N'Select * from @AName',N'@AName  [dbo].[someUDT] READONLY',@AName=@p3

虽然正确的代码是:

declare @p3  dbo.someUDT
insert into @p3 values(NULL,0.05)
insert into @p3 values(0.05,0.10)
insert into @p3 values(NULL,0.01)
insert into @p3 values(0.01,0.02)

exec sp_executesql N'Select * from @AName',N'@AName  [dbo].[someUDT] READONLY',@AName=@p3

我已经在 github 上创建了一个问题:User defined table inserting wrong value

我想在我的参数化查询中使用用户定义的表,这个问题解释了这是如何完成的: Entity Framework Stored Procedure Table Value Parameter

这是用于获取上述 SQL 代码的 C# 代码

DataTable dataTable = new DataTable();
dataTable.Columns.Add("value1", typeof(decimal));
dataTable.Columns.Add("value2", typeof(decimal));

dataTable.Rows.Add(null,0.05m); 
dataTable.Rows.Add(0.05m,0.1m); 
dataTable.Rows.Add(null,0.01m); 
dataTable.Rows.Add(0.01m,0.02m); 
List<SqlParameter> Parameters = new List<SqlParameter>();

Parameters.Add(new SqlParameter("@AName", SqlDbType.Structured)  Value = dataTable , TypeName= "dbo.someUDT" );

dbContext.Database.ExecuteSqlCommand("Select * from @AName", Parameters.ToArray());

以及获取用户定义表的SQL代码

CREATE TYPE [dbo].[someUDT] AS TABLE
(
   [value1] [decimal](16, 5) NULL,
   [value2] [decimal](16, 5) NULL
)

编辑:Gert Arnold 想通了。根据他的回答,我在这里找到了一份现有报告SQL Server Profiler TextData Column handles Decimal Inputs Incorrectly

【问题讨论】:

你可以试试这个dataTable.Rows.Add(null,0.05m); 并检查它生成的查询 @rjs123431 我之前试过,结果一样 您要创建一个新表并返回该表的所有值吗?对不起,但我不明白你真正想要什么。你能分享一下你的主要目标是什么吗? @LuttiCoelho 很抱歉造成混乱,Select * from @AName 是占位符。我实际上是在一个更大的查询中加入表格,我认为这与问题无关,因为这已经以更简单的格式复制了问题。 奇怪的是,我确实看到了错误的 SQL,但是当我使用Database.SqlQuery(而不是Database.ExecuteSqlCommand)时,我在客户端收到了正确的值! 【参考方案1】:

这是一个奇怪的 Sql Profiler 工件。值被正确传输。我可以通过使用您的用户定义类型和一个小表创建一个数据库来证明这一点:

CREATE TABLE [dbo].[Values](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Value] [decimal](16, 5) NOT NULL,
 CONSTRAINT [PK_Values] PRIMARY KEY CLUSTERED ([Id] ASC) ON [PRIMARY]
GO

并插入几个值:

Id          Value
----------- ---------------------------------------
1           10.00000
2           1.00000
3           0.10000
4           0.01000

然后我运行你的代码,稍作修改:

DataTable dataTable = new DataTable();
dataTable.Columns.Add("value1", typeof(decimal));
dataTable.Columns.Add("value2", typeof(decimal));

dataTable.Rows.Add(0.001m, 0.03m);
List<SqlParameter> Parameters = new List<SqlParameter>();

Parameters.Add(new SqlParameter("@AName", SqlDbType.Structured)  Value = dataTable, TypeName = "dbo.someUDT" );

using(var context = new MyContext(connStr))

    var query = "Select v.Id from dbo.[Values] v, @AName a "
        + " where v.Value BETWEEN a.value1 AND a.value2";
    var result = context.Database.SqlQuery<int>(query, Parameters.ToArray());

MyContex 只是一个继承自 DbContext 的类,仅此而已)

0.001m0.03m 之间只有一个值这正是查询返回的内容4

但是,Sql Server 探查器会记录以下内容:

declare @p3 dbo.someUDT
insert into @p3 values(1,3) -- See here: the log is warped

exec sp_executesql N'Select v.Value from dbo.[Values] v, @AName a  where v.Value BETWEEN a.value1 AND a.value2',N'@AName [dbo].[someUDT] READONLY',@AName=@p3

在返回记录 #2 的 SSMS 中。

我认为这与区域设置和小数分隔符与日志记录中某处的小数组分隔符混淆有关。

【讨论】:

我从没想过日志记录的问题。开箱即用的想法很棒,感谢您解决这个问题! 根据您的回答,我收到了此错误报告 feedback.azure.com/forums/908035-sql-server/suggestions/… 似乎不止我一个。【参考方案2】:

老实说,我和你没有同样的问题:

这是我的 Profiler 日志:

declare @p3 dbo.someUDT
insert into @p3 values(NULL,0.05)
insert into @p3 values(0.05,0.10)
insert into @p3 values(NULL,0.01)
insert into @p3 values(0.01,0.02)

exec sp_executesql N'Select * from @AName',N'@AName [dbo].[someUDT] READONLY',@AName=@p3

我尝试了 EntityFramework 6.2.0 & 6.3.0 & 6.4.0 版本,但这些都没有显示问题:

DataTable dataTable = new DataTable();
dataTable.Columns.Add("value1", typeof(decimal));
dataTable.Columns.Add("value2", typeof(decimal));

dataTable.Rows.Add(null, 0.05);
dataTable.Rows.Add(0.05M, 0.1M);
dataTable.Rows.Add(null, 0.01);
dataTable.Rows.Add(0.01, 0.02);
List<SqlParameter> Parameters = new List<SqlParameter>();

Parameters.Add(new SqlParameter("@AName", SqlDbType.Structured)  Value = dataTable, TypeName = "dbo.someUDT" );

var dbContext = new test01Entities();
dbContext.Database.ExecuteSqlCommand("Select * from @AName", Parameters.ToArray());

另外,我测试了 ADO.NET 并得到相同的结果:

SqlConnection cn = new SqlConnection("Data Source=(local);Initial Catalog=Test01;Integrated Security=true;");
using (var cmd = new SqlCommand("[foo]", cn))

    cmd.CommandType = CommandType.StoredProcedure;
    cn.Open();
    cmd.Parameters.AddWithValue("@param1", 0.02);
    cmd.Parameters.AddWithValue("@param2", 0.020);
    cmd.ExecuteNonQuery();

我正在使用 Visual Studio 2017、.NET Framework 4.6.1 和 Microsoft SQL Server Enterprise(64 位)

【讨论】:

我很确定这与语言设置(机器与数据库)有关,所以你很幸运。 我必须补充一点,我和@GertArnold 都住在同一个国家,这很可能解释了为什么我们都能重现这个问题 @GertArnold 请制作一个示例(VS 解决方案 + SQL 数据库)并分享。我会找到线索的。 @XAMT 这是一个 Sql Server Profiler 错误,所以我们无法控制。如果您愿意,您可以使用您的机器和数据库服务器的语言设置来查看在您运行代码时何时出现错误,但 IMO 是在消遣部门。

以上是关于实体框架中的用户定义表生成不正确的查询的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis框架-ResultMap节点

本地化表和实体框架

带有 Sql 视图的实体框架在生成 .sql 文件中显示为表

通过实体框架中的多个表获取值?

在模型中使用多对多时访问实体框架自动生成的第三个表

如何创建此实体命名查询