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

时间:2019-02-05 11:41:58

标签: c# sql sql-server entity-framework-core

我收到SQL异常:

  

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

SELECT中的

。我读了几个标题相同的问题,但它们都是关于插入的。我正在选择。

代码如下:

cm = confusion_matrix(train_labels_Encode, m)
plt.imshow(cm)
plt.show()

如果生成 string.Join 进行控制台,然后将SQL命令放入Management Studio的查询窗口中,则不会出现任何错误。我得到适当的结果。 EF CORE中的问题显然是我传递了太多类别ID。命令是根据产品类别ID获取嵌套类别。

编辑:

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

编辑2-解决方案

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

1 个答案:

答案 0 :(得分:3)

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