簡體   English   中英

字符串或二進制數據將被截斷WHERE IN

[英]String or binary data would be truncated WHERE IN

我得到SQL異常:

字符串或二進制數據將被截斷

在SELECT中。 我讀了幾個帶有相同標題的問題,但它們都是關於插入的。 我正在選擇。

代碼如下:

List<CategoryName> navigation = await db.Query<CategoryName>().FromSql(
    $"WITH NestedCategories AS (
        SELECT *
        FROM Categories
        WHERE Id IN (
            {string.Join(",", products.Select(x =>
                x.Categories.First().CategoryId).Distinct().Select(x => $"'{x}'"))}
        )
        UNION ALL 
            SELECT t.*
            FROM Categories t
            INNER JOIN NestedCategories c On c.ParentId = t.Id
    )
    SELECT DISTINCT c.Id, c.Name, c.ParentId
    FROM NestedCategories c")
.AsNoTracking()
.ToListAsync();

如果我生成string.Join到控制台,然后將SQL命令放入Management Studio的查詢窗口,我不會收到任何錯誤。 我得到了適當的結果。 問題顯然在EF CORE中,我傳遞了太多的類別ID。 命令是根據Product-Category Id獲取嵌套類別。

編輯:

public class CategoryName
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
}

編輯2 - 解決方案

string inClause = string.Join(",", products.Select(x => x.Categories.First().CategoryId).Distinct().Select(x => $"'{x}'"));

List<CategoryName> navigation = new List<CategoryName>();

using (DbCommand command = db.Database.GetDbConnection().CreateCommand())
{
     command.CommandText = $"WITH NestedCategories AS (SELECT * FROM Categories WHERE Id IN ({inClause}) UNION ALL SELECT t.* FROM Categories t INNER JOIN NestedCategories c On c.ParentId = t.Id) SELECT DISTINCT c.Id, c.Name, c.ParentId FROM NestedCategories c";

      await db.Database.GetDbConnection().OpenAsync();

      DbDataReader reader = await command.ExecuteReaderAsync();

      while (await reader.ReadAsync())
          navigation.Add(new CategoryName() { Id = reader.GetInt32(0), Name = reader.GetString(1), ParentId = await reader.IsDBNullAsync(2) ? null : await reader.GetFieldValueAsync<int?>(2) });
}

FromSql方法與內聯插值SQL字符串一起使用時應該非常小心。

通常,插值字符串被解析為string類型,但FromSql方法具有FormattableString參數的重載,這允許它在插值字符串中找到占位符並為每個字符串綁定命令參數

這通常是一種功能。 但是在你的情況下你只想把帶有id的連接字符串嵌入到SQL字符串中,而EF為它創建一個參數,所以即使沒有截斷錯誤,查詢也不會返回正確的結果,因為它會包含像WHERE IN (@param)@param將包含永遠不會匹配的逗號分隔文本。

最簡單的解決方法是通過將SQL放在變量中來強制另一個FromSql方法重載:

var sql = $"...";
List<CategoryName> navigation = await db.Query<CategoryName>().FromSql(sql)
    // ...

或使用演員:

List<CategoryName> navigation = await db.Query<CategoryName>().FromSql((string)
    $"...")
    // ...

更好的方法是在SQL字符串中創建占位符({0},{1},...),並通過params object[] parameters參數傳遞值。 這樣,EF Core將綁定每個值的參數(例如WHERE IN (@p0, @p1, ...) )而不是嵌入常量:

var parameters = products.Select(x => x.Categories.First().CategoryId).Distinct()
    .Cast<object>().ToArray(); // need object[]

var placeholders = string.Join(",", Enumerable.Range(0, parameters.Length)
    .Select(i = "{" + i + "}"));

var sql =
$"WITH NestedCategories AS (
    SELECT *
    FROM Categories
    WHERE Id IN ({placeholders})
    UNION ALL 
        SELECT t.*
        FROM Categories t
        INNER JOIN NestedCategories c On c.ParentId = t.Id
)
SELECT DISTINCT c.Id, c.Name, c.ParentId
FROM NestedCategories c";

var query = db.Query<CategoryName>().FromSql(sql, parameters);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM