Строка или двоичные данные будут обрезаны ГДЕ В - PullRequest
0 голосов
/ 05 февраля 2019

Я получаю исключение 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, что я передаю слишком много идентификаторов категории.Команда для получения категорий вложенности на основе идентификатора категории продукта.

РЕДАКТИРОВАТЬ:

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

1 Ответ

0 голосов
/ 05 февраля 2019

Вы должны быть очень осторожны при использовании метода FromSql со встроенной интерполированной строкой SQL.

Обычно интерполированные строки разрешаются в тип string, но метод FromSql имеет перегрузку с параметром FormattableString, что позволяет ему находить заполнители внутри интерполированной строки и связывает параметр команды для каждого из них.

Обычно это функциональность.Но в вашем случае вы хотите, чтобы в строку SQL встраивалась только объединенная строка с идентификаторами, в то время как EF создает для нее параметр, поэтому даже если не было ошибки усечения, запрос не будет возвращать правильные результаты, поскольку он будет содержатьчто-то вроде WHERE IN (@param) и @param будет содержать разделенный запятыми текст, который никогда не будет совпадать.

Самое простое решение - принудительно перегрузить другой метод FromSql, поместив SQL в переменную:

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

или используйте оператор приведения:

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

Лучшим подходом будет создание заполнителей ({0}, {1}, ...) внутри строки SQL и передача значений через 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);
...