EF Core扩展开发:创建自定义功能和插件的完整教程

EF Core扩展开发:创建自定义功能和插件的完整教程

【免费下载链接】efcore efcore: 是 .NET 平台上一个开源的对象关系映射(ORM)框架,用于操作关系型数据库。适合开发者使用 .NET 进行数据库操作,简化数据访问和持久化过程。 【免费下载链接】efcore 项目地址: https://gitcode.com/GitHub_Trending/ef/efcore

引言:为什么需要EF Core扩展?

你是否曾经在使用Entity Framework Core时遇到过这样的痛点:

  • 需要为所有实体自动添加审计字段(创建时间、修改时间等)
  • 想要实现全局的软删除功能
  • 需要在执行SQL命令前进行自定义处理
  • 希望为特定数据库提供额外的优化功能

EF Core的强大之处在于其高度可扩展的架构设计。通过扩展机制,你可以深度定制ORM行为,创建属于自己的数据库提供程序,或者为现有功能添加增强特性。本文将带你全面掌握EF Core扩展开发的核心技术。

扩展开发的核心架构

EF Core扩展体系概览

mermaid

核心接口解析

1. 拦截器接口 (IInterceptor)
public interface IInterceptor
{
    // 基础拦截器接口,所有具体拦截器都继承自此接口
}

// 具体拦截器示例
public interface IDbCommandInterceptor : IInterceptor
{
    ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<DbDataReader> result, 
        CancellationToken cancellationToken = default);
    
    ValueTask<DbDataReader> ReaderExecutedAsync(
        DbCommand command, 
        CommandExecutedEventData eventData, 
        DbDataReader result, 
        CancellationToken cancellationToken = default);
}
2. 选项扩展接口 (IDbContextOptionsExtension)
public interface IDbContextOptionsExtension
{
    DbContextOptionsExtensionInfo Info { get; }
    
    void ApplyServices(IServiceCollection services);
    
    void Validate(IDbContextOptions options);
    
    IDbContextOptionsExtension ApplyDefaults(IDbContextOptions options);
}

实战:创建自定义拦截器

示例1:SQL命令日志拦截器

public class SqlCommandLoggerInterceptor : IDbCommandInterceptor
{
    private readonly ILogger<SqlCommandLoggerInterceptor> _logger;

    public SqlCommandLoggerInterceptor(ILogger<SqlCommandLoggerInterceptor> logger)
    {
        _logger = logger;
    }

    public ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<DbDataReader> result, 
        CancellationToken cancellationToken = default)
    {
        LogCommand(command, eventData);
        return new ValueTask<InterceptionResult<DbDataReader>>(result);
    }

    private void LogCommand(DbCommand command, CommandEventData eventData)
    {
        var parameters = command.Parameters.Cast<DbParameter>()
            .Select(p => $"{p.ParameterName}={p.Value}")
            .ToArray();

        _logger.LogInformation("执行SQL: {CommandText} 参数: {Parameters}", 
            command.CommandText, string.Join(", ", parameters));
    }
}

示例2:自动审计字段拦截器

public class AuditInterceptor : ISaveChangesInterceptor
{
    public ValueTask<InterceptionResult<int>> SavingChangesAsync(
        DbContextEventData eventData, 
        InterceptionResult<int> result,
        CancellationToken cancellationToken = default)
    {
        var context = eventData.Context;
        if (context == null) return new ValueTask<InterceptionResult<int>>(result);

        var entries = context.ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);

        var now = DateTime.UtcNow;
        var currentUser = GetCurrentUser(); // 实现获取当前用户的方法

        foreach (var entry in entries)
        {
            if (entry.Entity is IAuditableEntity auditable)
            {
                if (entry.State == EntityState.Added)
                {
                    auditable.CreatedAt = now;
                    auditable.CreatedBy = currentUser;
                }
                auditable.UpdatedAt = now;
                auditable.UpdatedBy = currentUser;
            }
        }

        return new ValueTask<InterceptionResult<int>>(result);
    }
}

public interface IAuditableEntity
{
    DateTime CreatedAt { get; set; }
    string CreatedBy { get; set; }
    DateTime? UpdatedAt { get; set; }
    string? UpdatedBy { get; set; }
}

创建自定义选项扩展

实现完整的选项扩展

public class CustomOptionsExtension : IDbContextOptionsExtension
{
    private DbContextOptionsExtensionInfo? _info;
    private readonly bool _enableFeatureX;
    private readonly string _customSetting;

    public CustomOptionsExtension()
    {
        _enableFeatureX = false;
        _customSetting = string.Empty;
    }

    protected CustomOptionsExtension(CustomOptionsExtension copyFrom)
    {
        _enableFeatureX = copyFrom._enableFeatureX;
        _customSetting = copyFrom._customSetting;
    }

    public virtual DbContextOptionsExtensionInfo Info
        => _info ??= new ExtensionInfo(this);

    public virtual CustomOptionsExtension WithFeatureX(bool enable)
    {
        var clone = Clone();
        clone._enableFeatureX = enable;
        return clone;
    }

    public virtual CustomOptionsExtension WithCustomSetting(string setting)
    {
        var clone = Clone();
        clone._customSetting = setting;
        return clone;
    }

    public void ApplyServices(IServiceCollection services)
    {
        services.AddSingleton<ICustomService, CustomService>();
        
        if (_enableFeatureX)
        {
            services.AddSingleton<IFeatureXService, FeatureXService>();
        }
    }

    public void Validate(IDbContextOptions options)
    {
        // 验证配置是否有效
        if (string.IsNullOrEmpty(_customSetting))
        {
            throw new InvalidOperationException("Custom setting is required");
        }
    }

    protected virtual CustomOptionsExtension Clone()
        => new CustomOptionsExtension(this);

    private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
    {
        public ExtensionInfo(IDbContextOptionsExtension extension)
            : base(extension)
        {
        }

        public override bool IsDatabaseProvider => false;
        public override string LogFragment => "custom extension";

        public override int GetServiceProviderHashCode()
        {
            var extension = (CustomOptionsExtension)Extension;
            var hashCode = new HashCode();
            hashCode.Add(extension._enableFeatureX);
            hashCode.Add(extension._customSetting);
            return hashCode.ToHashCode();
        }

        public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
            => other is ExtensionInfo otherInfo &&
               ((CustomOptionsExtension)Extension)._enableFeatureX == 
               ((CustomOptionsExtension)otherInfo.Extension)._enableFeatureX &&
               ((CustomOptionsExtension)Extension)._customSetting == 
               ((CustomOptionsExtension)otherInfo.Extension)._customSetting;

        public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
        {
            debugInfo["Custom:EnableFeatureX"] = 
                ((CustomOptionsExtension)Extension)._enableFeatureX.ToString();
            debugInfo["Custom:CustomSetting"] = 
                ((CustomOptionsExtension)Extension)._customSetting;
        }
    }
}

创建扩展方法

public static class CustomDbContextOptionsExtensions
{
    public static DbContextOptionsBuilder UseCustomFeatures(
        this DbContextOptionsBuilder optionsBuilder,
        bool enableFeatureX = false,
        string customSetting = "")
    {
        Check.NotNull(optionsBuilder, nameof(optionsBuilder));

        var extension = optionsBuilder.Options.FindExtension<CustomOptionsExtension>()
            ?? new CustomOptionsExtension();

        extension = extension
            .WithFeatureX(enableFeatureX)
            .WithCustomSetting(customSetting);

        ((IDbContextOptionsBuilderInfrastructure)optionsBuilder)
            .AddOrUpdateExtension(extension);

        return optionsBuilder;
    }
}

实现自定义约定

模型构建约定示例

public class TableNamingConvention : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
        {
            var tableName = entityType.ClrType.Name.ToSnakeCase();
            entityType.SetTableName(tableName);
            
            // 为所有字符串属性设置默认长度
            foreach (var property in entityType.GetProperties()
                .Where(p => p.ClrType == typeof(string)))
            {
                property.SetMaxLength(200);
            }
        }
    }
}

public static class StringExtensions
{
    public static string ToSnakeCase(this string input)
    {
        if (string.IsNullOrEmpty(input)) return input;
        
        return Regex.Replace(input, 
            @"([a-z0-9])([A-Z])", "$1_$2").ToLower();
    }
}

注册约定到模型构建器

public class CustomModelBuilder : ModelBuilder
{
    public CustomModelBuilder(ConventionSet conventions)
        : base(conventions)
    {
    }
    
    protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.Conventions.Add(_ => new TableNamingConvention());
        base.ConfigureConventions(configurationBuilder);
    }
}

依赖注入和服务扩展

服务集合扩展

public static class CustomServiceCollectionExtensions
{
    public static IServiceCollection AddCustomEfCoreServices(
        this IServiceCollection services,
        Action<CustomOptions>? configureOptions = null)
    {
        services.Configure(configureOptions ?? (o => { }));
        
        services.AddScoped<ICustomDbContextService, CustomDbContextService>();
        services.AddSingleton<ICustomValueGenerator, CustomValueGenerator>();
        
        // 注册拦截器
        services.AddScoped<SqlCommandLoggerInterceptor>();
        services.AddScoped<AuditInterceptor>();
        
        return services;
    }
}

public class CustomOptions
{
    public bool EnableAdvancedLogging { get; set; }
    public string DefaultSchema { get; set; } = "custom";
}

自定义DbContext配置

public class CustomDbContext : DbContext
{
    private readonly ICustomDbContextService _customService;

    public CustomDbContext(
        DbContextOptions<CustomDbContext> options,
        ICustomDbContextService customService)
        : base(options)
    {
        _customService = customService;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder
                .UseCustomFeatures(enableFeatureX: true, customSetting: "production")
                .AddInterceptors(
                    new SqlCommandLoggerInterceptor(
                        LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger<SqlCommandLoggerInterceptor>()),
                    new AuditInterceptor());
        }
        
        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
        
        // 应用自定义约定
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            // 自定义模型配置逻辑
            _customService.ConfigureEntity(entityType, modelBuilder);
        }
        
        base.OnModelCreating(modelBuilder);
    }
}

高级主题:性能优化和最佳实践

性能优化技巧

优化技术适用场景实现方式性能提升
编译查询高频查询EF.CompileAsyncQuery()减少表达式树编译开销
批量操作大量数据插入/更新自定义批量处理器减少数据库往返次数
缓存机制重复查询结果查询结果缓存拦截器避免重复数据库查询
连接池高并发场景自定义连接管理减少连接建立开销

编译查询示例

public static class CompiledQueries
{
    public static readonly Func<MyDbContext, int, Task<Customer?>> GetCustomerById =
        EF.CompileAsyncQuery((MyDbContext context, int id) =>
            context.Customers.FirstOrDefault(c => c.Id == id));
    
    public static readonly Func<MyDbContext, string, IAsyncEnumerable<Customer>> GetCustomersByCity =
        EF.CompileAsyncQuery((MyDbContext context, string city) =>
            context.Customers.Where(c => c.City == city));
}

// 使用编译查询
var customer = await CompiledQueries.GetCustomerById(dbContext, 123);
await foreach (var customer in CompiledQueries.GetCustomersByCity(dbContext, "Beijing"))
{
    // 处理客户数据
}

测试和调试扩展

单元测试策略

[TestFixture]
public class CustomInterceptorTests
{
    [Test]
    public async Task AuditInterceptor_ShouldSetAuditFields()
    {
        // 安排
        var interceptor = new AuditInterceptor();
        var dbContext = CreateTestDbContext();
        
        var customer = new Customer { Name = "Test" };
        dbContext.Customers.Add(customer);
        
        var eventData = new DbContextEventData(
            dbContext, 
            dbContext.GetService<IDiagnosticsLogger<DbLoggerCategory.Database>>());
        
        // 执行
        await interceptor.SavingChangesAsync(eventData, InterceptionResult<int>.SuppressWithResult(1));
        
        // 断言
        customer.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
        customer.UpdatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }
    
    private TestDbContext CreateTestDbContext()
    {
        var options = new DbContextOptionsBuilder<TestDbContext>()
            .UseInMemoryDatabase("Test")
            .Options;
            
        return new TestDbContext(options);
    }
}

调试技巧

// 添加调试日志
public class DebugInterceptor : IDbCommandInterceptor
{
    public ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<DbDataReader> result, 
        CancellationToken cancellationToken = default)
    {
        Debug.WriteLine($"Executing: {command.CommandText}");
        foreach (DbParameter parameter in command.Parameters)
        {
            Debug.WriteLine($"  {parameter.ParameterName} = {parameter.Value}");
        }
        
        return new ValueTask<InterceptionResult<DbDataReader>>(result);
    }
}

部署和发布扩展

NuGet包配置

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <PackageId>MyCompany.EntityFrameworkCore.Extensions</PackageId>
    <Version>1.0.0</Version>
    <Description>Custom extensions for Entity Framework Core</Description>
    <PackageTags>entity-framework-core;efcore;extensions</PackageTags>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
  </ItemGroup>

</Project>

版本兼容性矩阵

EF Core版本扩展版本主要特性注意事项
8.0+2.0+编译查询、拦截器需要.NET 8
7.01.5+批量操作、JSON支持部分特性受限
6.01.0+基础扩展功能维护模式

总结

通过本文的深入学习,你已经掌握了EF Core扩展开发的核心技术:

  1. 拦截器机制 - 实现对数据库操作的全面监控和定制
  2. 选项扩展 - 创建可配置的扩展功能
  3. 约定系统 - 统一模型构建规则
  4. 服务扩展 - 通过依赖注入集成自定义服务

EF Core的扩展架构提供了极大的灵活性,让你能够根据具体业务需求定制ORM行为。无论是简单的审计字段自动填充,还是复杂的自定义数据库提供程序,都可以通过扩展机制优雅实现。

记住扩展开发的最佳实践:

  • 保持扩展的单一职责原则
  • 提供清晰的配置接口
  • 考虑性能影响并进行优化
  • 编写充分的单元测试
  • 确保版本兼容性

现在,你已经具备了创建专业级EF Core扩展的能力,开始构建属于你自己的ORM增强功能吧!

【免费下载链接】efcore efcore: 是 .NET 平台上一个开源的对象关系映射(ORM)框架,用于操作关系型数据库。适合开发者使用 .NET 进行数据库操作,简化数据访问和持久化过程。 【免费下载链接】efcore 项目地址: https://gitcode.com/GitHub_Trending/ef/efcore

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值