web-dev-qa-db-ja.com

Entity Frameworkのユーザー定義テーブルが不正なクエリを生成する

私は現在、Entity Framework 6とおそらくADO.NETのバグを経験していると思います。期限があるので、このバグが修正されるのを待つことができるかどうかはわかりません。うまくいけば、誰かが私にクリーンな作業を手伝ってくれると思います。

問題は、クエリが値1と5を0.01と0.05にする必要がある場所で使用することです。しかし、奇妙なことに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で問題を作成しました: 間違った値を挿入するユーザー定義テーブル

パラメーター化されたクエリでユーザー定義テーブルを使用したいのですが、この質問では、これがどのように行われるかを説明します: Entity Frameworkストアドプロシージャテーブルの値パラメーター

これは、上記の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列が10進数入力を正しく処理しません

10
Joost K

これは奇妙なSQLプロファイラーアーティファクトです。値は正しく転送されます。ユーザー定義型と1つの小さなテーブルを使用してデータベースを作成することで、そのことを示すことができます。

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());
}

MyContexDbContextを継承したクラスであり、それ以外のものはありません)

0.001m0.03mの間の値は1つだけですそして、クエリが返すのはまさにこれです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で。

それは地域設定と小数点区切り文字がロギングのどこかで小数点区切り文字と混同されていることに関係していると思います。

11
Gert Arnold

正直なところ、私はあなたと同じ問題を抱えていません:

これは私のプロファイラログです。

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ビット)を使用しています

1
XAMT