简体   繁体   English

字符串或二进制数据将被截断WHERE IN

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

I am getting SQL Exception: 我得到SQL异常:

String or binary data would be truncated 字符串或二进制数据将被截断

in SELECT. 在SELECT中。 I read few questions with same title but they were all about inserting. 我读了几个带有相同标题的问题,但它们都是关于插入的。 I am SELECTING. 我正在选择。

Code is following: 代码如下:

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

If I generate string.Join to console and then put SQL command into query window in Management Studio I dont get any error. 如果我生成string.Join到控制台,然后将SQL命令放入Management Studio的查询窗口,我不会收到任何错误。 I get proper results. 我得到了适当的结果。 Issue is obviously in EF CORE that I am passing too many category Ids. 问题显然在EF CORE中,我传递了太多的类别ID。 Command is to get nesting categories based on Product-Category Id. 命令是根据Product-Category Id获取嵌套类别。

EDIT: 编辑:

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

EDIT 2 - Solution 编辑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) });
}

You should be very careful when using FromSql method with an inline interpolated SQL string. FromSql方法与内联插值SQL字符串一起使用时应该非常小心。

Normally interpolated strings are resolved to string type, but FromSql method has overload with FormattableString parameter, which allows it to find the placeholders inside the interpolated string and bind a command parameter for each of them. 通常,插值字符串被解析为string类型,但FromSql方法具有FormattableString参数的重载,这允许它在插值字符串中找到占位符并为每个字符串绑定命令参数

This normally is a functionality. 这通常是一种功能。 But in your case you want just the joined string with ids to be embedded in the SQL string, while EF creates a parameter for it, so even if there was not a truncation error, the query won't return correct results because it would contain something like WHERE IN (@param) and @param will contain the comma separated text which will never match. 但是在你的情况下你只想把带有id的连接字符串嵌入到SQL字符串中,而EF为它创建一个参数,所以即使没有截断错误,查询也不会返回正确的结果,因为它会包含像WHERE IN (@param)@param将包含永远不会匹配的逗号分隔文本。

The simplest fix is to force the other FromSql method overload by either putting the SQL in a variable: 最简单的解决方法是通过将SQL放在变量中来强制另一个FromSql方法重载:

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

or use cast operator: 或使用演员:

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

A better approach would to create placeholders ({0}, {1}, ...) inside the SQL string and pass values through params object[] parameters argument. 更好的方法是在SQL字符串中创建占位符({0},{1},...),并通过params object[] parameters参数传递值。 This way EF Core will bind parameter for each value (eg WHERE IN (@p0, @p1, ...) ) rather than embedded constants: 这样,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