Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Пользовательский запрос счетчика EF Core

Автор вопроса: Daniel DreierОтветов: 2

Я работаю над небольшим проектом ASP.NET Core для пометки изображений с использованием Entity Framework Core в базе данных Sqlite, в основном только для обучения. Есть две таблицы (и POCO), Теги и Изображения, где несколько тегов связаны с каждым изображением. Я пытаюсь подсчитать все изображения, с которыми связаны теги.

В простом SQL я бы написал SELECT COUNT(DISTINCT ImageId) FROM Tags, чтобы получить количество, а в LINQ я придумал _context.Tags.Select(t => t.Image).Distinct().Count(). Но этот запрос LINQ заставляет EF-Core объединять две таблицы, возвращать все строки, а затем выполнять Distinct и Count в коде.

Я пытался сделать _context.Tags.FromSql("SELECT COUNT(DISTINCT ImageId) FROM Tags"), но поскольку этот запрос возвращает только счетчик, вызов завершается ошибкой, потому что EF не может сопоставить результат с тегом. Я также пытался использовать _context.Database.FromSql<int>, но не смог найти никакой реальной документации по нему и, похоже, для него не существует IntelliSense.

На данный момент я сделал то, что подробно описано в разделе "ADO.NET" этого запись в блоге от Эрика Андерсона:

int count;
using (var connection = _context.Database.GetDbConnection())
{
    connection.Open();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT COUNT(DISTINCT ImageId) FROM Tags";
        string result = command.ExecuteScalar().ToString();

        int.TryParse(result, out count);
    }
}

Но лучший ли это способ добиться эффективного подсчета?


Изменить: вот запрос, который EF помещает в вывод отладки:

SELECT "t"."TagId", "t"."Content", "t"."ImageId", "t.Image"."ImageId", "t.Image"."FileName", "t.Image"."Path", "t.Image"."Url"
FROM "Tags" AS "t"
LEFT JOIN "Images" AS "t.Image" ON "t"."ImageId" = "t.Image"."ImageId"
ORDER BY "t"."ImageId"

entity-framework-corelinq

Ответы

Ответ 1 · рейтинг 4 · Tim Taber

На данный момент вы не можете определить специальный результат. Хорошая новость заключается в том, что в настоящее время он находится в очереди: https://github.com/aspnet/EntityFramework/issues/1862

А пока вот метод расширения, который будет работать:

public static int IntFromSQL(this ApplicationDbContext context, string sql )
{
    int count;
    using (var connection = context.Database.GetDbConnection())
    {
        connection.Open();

        using (var command = connection.CreateCommand())
        {
            command.CommandText = sql;
            string result = command.ExecuteScalar().ToString();

            int.TryParse(result, out count);
        }
    }
    return count;
}

Применение:

int result = _context.IntFromSQL("SELECT COUNT(DISTINCT ImageId) FROM Tags");

Ответ 2 · LittleDebugger

Ваша исходная строка кода должна была сделать именно то, что вы хотели. Это также рекомендуется вместо встроенного SQL.

_context.Tags.Select(t => t.Image).Distinct().Count()

Вы уверены, что это вызывало базу данных для двух таблиц, а затем запрашивало их в памяти? Если вы наблюдали такое поведение во время отладки, то, возможно, ваша проверка вызвала перечисление IQueryable, которое вызовет базу данных, используя запрос, отличный от того, который был бы в противном случае.

Способ проверки фактического запроса без нарушения работающего кода - использование MyLoggerProvider из документации Entity Framework Core.

https://docs.efproject.net/en/latest/miscellaneous/logging.html?highlight=logging

Как только регистратор будет зарегистрирован в коде, любой SQL-запрос, запущенный к серверу, будет отображаться в окне консоли и/или в файле c:\temp\log.txt.

Следующее сообщение журнала было создано при использовании Distinct() и Count() в таблицах базы данных примера веб-сайта.

SELECT COUNT(*)
FROM (
SELECT DISTINCT [a.Blog].[BlogId], [a.Blog].[Url]
FROM [Posts] AS [a]
INNER JOIN [Blogs] AS [a.Blog] ON [a].[BlogId] = [a.Blog].[BlogId]
) AS [t]Closing connection to database '***' on server 'tcp:**************'.

Наконец, поскольку вам не нужны никакие свойства t.Image, кажется, что вы должны использовать Where(), а не Select().

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы