在 Entity Framework Core (EF Core) 中,Database属性的类型是DatabaseFacade,
DatabaseFacade
是一个关键的门面类,提供对数据库相关操作的统一访问接口。它封装了与底层数据库交互的核心功能,包括连接管理、事务处理、执行原始 SQL 以及数据库迁移等操作。下面从功能、用法、常见场景和最佳实践几个方面详细解析:
1. 核心功能
DatabaseFacade
提供以下主要功能:
// 获取底层 DbConnection
var connection = context.Database.GetDbConnection();
// 检查连接状态
if (context.Database.GetDbConnection().State != ConnectionState.Open)
{
await context.Database.OpenConnectionAsync();
}
// 执行连接相关操作
var databaseName = context.Database.GetDbConnection().Database;
1.2 事务处理
// 开始事务
await using var transaction = await context.Database.BeginTransactionAsync();
try
{
// 执行数据操作
context.Users.Add(new User { Name = "John" });
await context.SaveChangesAsync();
// 提交事务
await transaction.CommitAsync();
}
catch (Exception)
{
// 回滚事务
await transaction.RollbackAsync();
}
1.3 执行原始 SQL
// 执行查询
var users = await context.Users
.FromSqlRaw("SELECT * FROM Users WHERE Age > {0}", 18)
.ToListAsync();
// 执行非查询命令
var rowsAffected = await context.Database.ExecuteSqlRawAsync(
"UPDATE Users SET LastLogin = GETDATE() WHERE Id = {0}", 1);
1.4 数据库迁移
// 应用所有待迁移的更改
await context.Database.MigrateAsync();
// 检查是否需要迁移
bool pendingMigrations = (await context.Database.GetPendingMigrationsAsync()).Any();
// 创建数据库(如果不存在)
await context.Database.EnsureCreatedAsync();
1.5 数据库状态检查
// 检查数据库是否存在
bool exists = await context.Database.EnsureExistsAsync();
// 检查数据库架构是否与模型匹配
bool isCompatible = await context.Database.CanConnectAsync();
2. 获取 DatabaseFacade 实例
在 DbContext
中,可以通过 Database
属性访问 DatabaseFacade
:
public class ApplicationDbContext : DbContext
{
public DbSet<User> Users { get; set; }
// 通过基类的 Database 属性访问 DatabaseFacade
public void SomeMethod()
{
// 执行数据库操作
this.Database.ExecuteSqlRaw("UPDATE Users SET ...");
}
}
3. 常见应用场景
3.1 复杂查询优化
对于 EF Core 查询性能不佳的场景,使用原始 SQL:
var results = await context.Set<ComplexResult>()
.FromSqlRaw("EXEC sp_GetComplexData @Param1, @Param2", param1, param2)
.ToListAsync();
3.2 批量操作
执行批量更新或删除:
await context.Database.ExecuteSqlRawAsync(
"DELETE FROM Orders WHERE OrderDate < {0}", DateTime.Now.AddYears(-1));
3.3 数据库初始化与迁移
在应用启动时确保数据库和架构存在:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
using (var scope = app.ApplicationServices.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate(); // 应用所有迁移
}
// 其他配置...
}
3.4 分布式事务
结合 ADO.NET 使用分布式事务:
await using var transaction = await context.Database.BeginTransactionAsync();
try
{
// EF Core 操作
context.Users.Add(new User { Name = "Alice" });
await context.SaveChangesAsync();
// ADO.NET 操作(使用相同连接)
var connection = context.Database.GetDbConnection();
using (var command = connection.CreateCommand())
{
command.Transaction = transaction.GetDbTransaction();
command.CommandText = "UPDATE Log SET Status = 'Processed' WHERE Id = 1";
await command.ExecuteNonQueryAsync();
}
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
4. 高级用法
4.1 控制连接超时
context.Database.SetCommandTimeout(TimeSpan.FromMinutes(2)); // 设置 2 分钟超时
4.2 拦截数据库操作
通过自定义拦截器记录 SQL 执行时间:
public class SqlLoggingInterceptor : DbCommandInterceptor
{
private readonly ILogger _logger;
public SqlLoggingInterceptor(ILogger<SqlLoggingInterceptor> logger)
{
_logger = logger;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
_logger.LogInformation("Executing SQL: {Sql}", command.CommandText);
return base.ReaderExecuting(command, eventData, result);
}
}
// 在 DbContext 中注册拦截器
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.AddInterceptors(new SqlLoggingInterceptor(_logger));
}
4.3 多数据库操作
在多个 DbContext
之间共享事务:
await using var transaction = await context1.Database.BeginTransactionAsync();
try
{
// 操作 context1
context1.Users.Add(new User { Name = "Bob" });
await context1.SaveChangesAsync();
// 操作 context2(使用相同事务)
context2.Orders.Add(new Order { UserId = 1 });
await context2.SaveChangesAsync();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
5. 注意事项
5.1 原始 SQL 的安全性
始终使用参数化查询,避免 SQL 注入:
// 安全写法
await context.Database.ExecuteSqlRawAsync(
"UPDATE Users SET Name = {0} WHERE Id = {1}", newName, id);
// 不安全写法(避免)
await context.Database.ExecuteSqlRawAsync(
$"UPDATE Users SET Name = '{newName}' WHERE Id = {id}");
5.2 跨数据库兼容性
直接编写的 SQL 可能不支持多种数据库提供者(如 SQL Server 和 PostgreSQL 的语法差异)。
5.3 事务边界
确保事务的生命周期正确管理,避免长时间持有事务导致锁争用。
5.4 性能考虑
频繁调用 DatabaseFacade
方法可能影响性能,优先使用 EF Core 的 LINQ 查询。
总结
DatabaseFacade
是 EF Core 中连接应用程序和底层数据库的桥梁,提供了丰富的数据库操作功能。它在需要执行原始 SQL、管理事务或进行数据库迁移时特别有用。但在使用时应权衡其灵活性与潜在风险,遵循最佳实践以确保代码的安全性和可维护性。在大多数情况下,优先使用 EF Core 的高级 API,仅在必要时才通过 DatabaseFacade
进行底层操作。