Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7
Для контроля операций, которые выполняет контекст данных, мы можем использовать логгирование. Entity Framework Core поддерживает логгирование, причем мы мжем воспользоваться как встроенными возможностями, так и создать и встроить свою инфраструктуру логгирования. Рассмотрим оба варианта и начнем со встроенных возможностей.
В EntityFramework Core 5.0 было существенно упрощено логгирование. В частности, был добавлен LogTo(), который позволяет логгировать информацию. Он применяется при конфигурации класса контекста данных.
Например, выведем всю информацию об операциях с базой данных на консоль. Допустим, у нас будут следующая модель:
public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
И следующий класс контекста данных:
using Microsoft.EntityFrameworkCore; namespace HelloApp { public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); optionsBuilder.LogTo(System.Console.WriteLine); } } }
В методе OnConfiguring()
у передаваемого в качестве параметра объекта DbContextOptionsBuilder вызывается метод LogTo(),
в который передается делегат Action<string>
- то есть некоторое действие, которое принимает один параметр типа string и
и ничего не возвращает. Именно такое действие представляет традиционный метод Console.WriteLine()
, который выводит строку на консоль. И поскольку класс Console расположен в
пространстве имен System, то прописываем System.Console.WriteLine.
Для тестирования пусть у нас определена следующая программа:
using Microsoft.EntityFrameworkCore.Infrastructure; using System; using System.Linq; using Microsoft.Extensions.Logging; namespace HelloApp { public class Program { public static void Main(string[] args) { using (ApplicationContext db = new ApplicationContext()) { User user1 = new User { Name = "Tom", Age = 33 }; User user2 = new User { Name = "Alice", Age = 26 }; db.Users.Add(user1); db.Users.Add(user2); db.SaveChanges(); var users = db.Users.ToList(); Console.WriteLine("Данные после добавления:"); foreach (User u in users) { Console.WriteLine($"{u.Id}.{u.Name} - {u.Age}"); } } Console.Read(); } } }
И при запуске приложения на консоль будет выведена детальная информация по всем операциям:
Подобным образом можно логгировать в другие места. Например, логгирование в окно отладки (что в программе производится с помощью метода
Debug.WriteLine()
):
optionsBuilder.LogTo(message => System.Diagnostics.Debug.WriteLine(message));
Другим распространенным способом логгирования является вывод в файл:
using Microsoft.EntityFrameworkCore; using System.IO; using System.Threading.Tasks; namespace HelloApp { public class ApplicationContext : DbContext { private readonly StreamWriter logStream = new StreamWriter("mylog.txt", true); public DbSet<User> Users { get; set; } public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); optionsBuilder.LogTo(logStream.WriteLine); } public override void Dispose() { base.Dispose(); logStream.Dispose(); } public override async ValueTask DisposeAsync() { await base.DisposeAsync(); await logStream.DisposeAsync(); } } }
Собственно для записи в файл используется объект класса StreamWriter
из пространства имен System.IO. Его метод logStream.WriteLine
, который пищет в файл строку, передается в метод
LogTo()
. Для закрытия и утилизации файлового потока StreamWriter в классе контекста переопределены методы Dispose/DisposeAsync, в
которых вызывается метод Dispose/DisposeAsync объекта StreamWriter. В итоге при выполнении программы в папке приложения появится файл лога mylog.txt.
Метод LogTo()
имеет ряд перегруженных версий, которые принимают разное количество параметров. Так, мы можем передать в LogTo уровень логгирования
в виде одного из значений перечисления LogLevel:
Trace
: используется для вывода наиболее детализированных сообщений. Подобные сообщения могут нести важную информацию о
приложении и его строении, поэтому данный уровень лучше использовать при разработке, но никак не при публикации
Debug
: для вывода информации, которая может быть полезной в процессе разработки и отладки приложения
Information
: уровень сообщений, позволяющий просто отследить поток выполнения приложения
Warning
: используется для вывода сообщений о неожиданных событиях, например, ошибках, которые не влияют не останавливают выполнение приложения,
но в то же время должны быть иследованы
Error
: для вывода информации об ошибках и исключениях, которые возникли при текущей операции и которые не могут быть обработаны
Critical
: уровень критических ошибок, которые требуют немедленной реакции - ошибками операционной системы, потерей данных в бд,
переполнение памяти диска и т.д.
None
: вывод информации в лог не применяется
По умолчанию EntityFramework Core использует значение Debug
, но можно указать какое-нибудь другое значение:
optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
Каждое сообщение в логе ассоциировано с определенным идентификатором события. По сути идентификаторы
представляют тип возникающих событий. Например, специфические для провайдера SQL Server сообщения
описываются классом SqlServerEventId
. Сообщения более общего характера, описывются классом CoreEventId
,
а сообщения, связанные с выполнением запросов, - класом RelationalEventId
.
Поскольку каждый класс идентификатора имеет довольно много полей, которые представляют опеделенное сообщение, я не буду подробно расписывать все эти поля.
Посмотрим на простом примере, как мы можем конкретизировать сообщения - например, нам надо вывести только выполняемые команды SQL.
В этом случае мы можем воспользоваться RelationalEventId
и его переменной CommandExecuted
, которая представляет окончание выполнения команды:
optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted});
Другим способом фильтрации сообщений представляет использование категорий, которые представлены классом DbLoggerCategory и который позволяет задать нужные категории логгирования:
Database.Command
: категория для выполняемых команд, позволяет получить выполняемый код SQL
Database.Connection
: категория для операций подключения к БД
Database.Transaction
: категория для транзакций с бд
Migration
: категория для миграций
Model
: категория для действий, совершаемых при привязке модели
Query
: категория для запросов за исключением тех, что генерируют исполняемый код SQL
Scaffolding
: категория для действий, выполняемых в поцессе обратного инжиниринга (то есть когда по базе данных генерируются классы и класс контекста)
Update
: категория для сообщений вызова DbContext.SaveChanges()
Infrastructure
: категория для всех остальных сообщений
Например, выведем в лог информацию только об исполняемых командах:
optionsBuilder.LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Command.Name });