ABP AbpDataModule
ABP AbpDataModule
2023/6/1
➡️

AbpDataModule

AbpDataModule 这个模块的目标是建立一个脱离 ef core 或 ef 的数据库处理层


namespace Volo.Abp.Data;

[DependsOn(
    typeof(AbpObjectExtendingModule),
    typeof(AbpUnitOfWorkModule),
    typeof(AbpEventBusAbstractionsModule)
)]
public class AbpDataModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        AutoAddDataSeedContributors(context.Services);
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();

        //配置选项 AbpDbConnectionOptions
        Configure<AbpDbConnectionOptions>(configuration);
        // 数据过滤器
        context.Services.AddSingleton(typeof(IDataFilter<>), typeof(DataFilter<>));
    }

    public override void PostConfigureServices(ServiceConfigurationContext context)
    {

        Configure<AbpDbConnectionOptions>(options =>
        {
            //通过连接字符串获取数据库信息
            options.Databases.RefreshIndexes();
        });
    }

    private static void AutoAddDataSeedContributors(IServiceCollection services)
    {
        var contributors = new List<Type>();

        services.OnRegistered(context =>
        {
            if (typeof(IDataSeedContributor).IsAssignableFrom(context.ImplementationType))
            {
                contributors.Add(context.ImplementationType);
            }
        });

        services.Configure<AbpDataSeedOptions>(options =>
        {
            options.Contributors.AddIfNotContains(contributors);
        });
    }
}
public class ConnectionStrings : Dictionary<string, string?>
{
    public const string DefaultConnectionStringName = "Default";

    public string? Default {
        get => this.GetOrDefault(DefaultConnectionStringName);
        set => this[DefaultConnectionStringName] = value;
    }
}

AbpDbConnectionOptions 存放 ConnectionStrings 及 ConnectionStrings 俩个字典,用于获取连接 字符串

public class AbpDbConnectionOptions
{
    //连接字符串名和连接字符串字典
    public ConnectionStrings ConnectionStrings { get; set; }
    // 数据库名和数据库信息字典
    public ConnectionStrings Databases { get; set; }

    public AbpDbConnectionOptions()
    {
        ConnectionStrings = new ConnectionStrings();
        Databases = new AbpDatabaseInfoDictionary();
    }

    public string? GetConnectionStringOrNull(
        string connectionStringName,
        bool fallbackToDatabaseMappings = true,
        bool fallbackToDefault = true)
    {
        var connectionString = ConnectionStrings.GetOrDefault(connectionStringName);
        if (!connectionString.IsNullOrEmpty())
        {
            return connectionString;
        }

        if (fallbackToDatabaseMappings)
        {
            var database = Databases.GetMappedDatabaseOrNull(connectionStringName);
            if (database != null)
            {
                connectionString = ConnectionStrings.GetOrDefault(database.DatabaseName);
                if (!connectionString.IsNullOrEmpty())
                {
                    return connectionString;
                }
            }
        }

        if (fallbackToDefault)
        {
            connectionString = ConnectionStrings.Default;
            if (!connectionString.IsNullOrWhiteSpace())
            {
                return connectionString;
            }
        }

        return null;
    }
}

数据库名及数据库多个链接字符串

public class AbpDatabaseInfo
{
    public string DatabaseName { get; }

    /// <summary>
    /// List of connection names mapped to this database.
    /// </summary>
    public HashSet<string> MappedConnections { get; }

    /// <summary>
    /// Is this database used by tenants. Set this to false if this database
    /// can not owned by tenants.
    ///
    /// Default: true.
    /// </summary>
    public bool IsUsedByTenants { get; set; } = true;

    internal AbpDatabaseInfo(string databaseName)
    {
        DatabaseName = databaseName;
        MappedConnections = new HashSet<string>();
    }

    /// <summary>
    /// Shortcut method to add one or multiple connections to the
    <see cref="MappedConnections"/> collection.
    /// </summary>
    /// <param name="connectionNames"></param>
    public void MapConnection(params string[] connectionNames)
    {
        foreach (var connectionName in connectionNames)
        {
            MappedConnections.AddIfNotContains(connectionName);
        }
    }
}

数据库名和数据库信息字典

public class AbpDatabaseInfoDictionary : Dictionary<string, AbpDatabaseInfo>
{
    private Dictionary<string, AbpDatabaseInfo> ConnectionIndex { get; set; }

    public AbpDatabaseInfoDictionary()
    {
        ConnectionIndex = new Dictionary<string, AbpDatabaseInfo>();
    }

    //用链接字符串获取指定的数据库信息
    public AbpDatabaseInfo? GetMappedDatabaseOrNull(string connectionStringName)
    {
        return ConnectionIndex.GetOrDefault(connectionStringName);
    }

    //配置指定  databaseName  数据库信息
    public AbpDatabaseInfoDictionary Configure(string databaseName,
    Action<AbpDatabaseInfo> configureAction)
    {
        var databaseInfo = this.GetOrAdd(
            databaseName,
            () => new AbpDatabaseInfo(databaseName)
        );

        configureAction(databaseInfo);

        return this;
    }

    // 数据库连接字符串映射数据库信息

    /// <summary>
    /// This method should be called if this dictionary changes.
    /// It refreshes indexes for quick access to the connection informations.
    /// </summary>
    public void RefreshIndexes()
    {
        ConnectionIndex = new Dictionary<string, AbpDatabaseInfo>();

        foreach (var databaseInfo in Values)
        {
            foreach (var mappedConnection in databaseInfo.MappedConnections)
            {
                if (ConnectionIndex.ContainsKey(mappedConnection))
                {
                    throw new AbpException(
                        $"A connection name can not map to multiple databases:
                         {mappedConnection}."
                    );
                }

                ConnectionIndex[mappedConnection] = databaseInfo;
            }
        }
    }
}

数据过滤器

public interface IDataFilter<TFilter>
    where TFilter : class
{
    IDisposable Enable();

    IDisposable Disable();

    bool IsEnabled { get; }
}

public interface IDataFilter
{
    IDisposable Enable<TFilter>()
        where TFilter : class;

    IDisposable Disable<TFilter>()
        where TFilter : class;

    bool IsEnabled<TFilter>()
        where TFilter : class;
}

// 获取数据过滤器 后可以使能或经用数据过滤器,

public class DataFilter : IDataFilter, ISingletonDependency
{
    private readonly ConcurrentDictionary<Type, object> _filters;

    private readonly IServiceProvider _serviceProvider;

    public DataFilter(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
        _filters = new ConcurrentDictionary<Type, object>();
    }

    public IDisposable Enable<TFilter>()
        where TFilter : class
    {
        return GetFilter<TFilter>().Enable();
    }

    public IDisposable Disable<TFilter>()
        where TFilter : class
    {
        return GetFilter<TFilter>().Disable();
    }

    public bool IsEnabled<TFilter>()
        where TFilter : class
    {
        return GetFilter<TFilter>().IsEnabled;
    }

    private IDataFilter<TFilter> GetFilter<TFilter>()
        where TFilter : class
    {
        return (_filters.GetOrAdd(
            typeof(TFilter),
            factory: () => _serviceProvider.GetRequiredService<IDataFilter<TFilter>>()
        ) as IDataFilter<TFilter>)!;
    }
}

数据种子

数据迁移工具实现接口

//
public interface IDataSeedContributor
{
    Task SeedAsync(DataSeedContext context);
}
public class AbpDataSeedOptions
{
    public DataSeedContributorList Contributors { get; }

    public AbpDataSeedOptions()
    {
        Contributors = new DataSeedContributorList();
    }
}

public interface IDataSeeder
{
    Task SeedAsync(DataSeedContext context);
}


public class DataSeeder : IDataSeeder, ITransientDependency
{
    protected IServiceScopeFactory ServiceScopeFactory { get; }
    protected AbpDataSeedOptions Options { get; }

    public DataSeeder(
        IOptions<AbpDataSeedOptions> options,
        IServiceScopeFactory serviceScopeFactory)
    {
        ServiceScopeFactory = serviceScopeFactory;
        Options = options.Value;
    }

    [UnitOfWork]
    public virtual async Task SeedAsync(DataSeedContext context)
    {
      using (var scope = ServiceScopeFactory.CreateScope())
      {
          if (context.Properties.ContainsKey(DataSeederExtensions.SeedInSeparateUow))
          {
            var manager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
            foreach (var contributorType in Options.Contributors)
            {
                var options = context.Properties.
                TryGetValue(DataSeederExtensions.SeedInSeparateUowOptions,
                out var uowOptions)
                    ? (AbpUnitOfWorkOptions) uowOptions!
                    : new AbpUnitOfWorkOptions();
                var requiresNew = context.Properties.
                TryGetValue(DataSeederExtensions.SeedInSeparateUowRequiresNew,
                  out var obj) && (bool) obj!;

                using (var uow = manager.Begin(options, requiresNew))
                {
                    var contributor = (IDataSeedContributor)scope.ServiceProvider.
                    GetRequiredService(contributorType);
                    await contributor.SeedAsync(context);
                    await uow.CompleteAsync();
                }
            }
          }
          else
          {
              foreach (var contributorType in Options.Contributors)
              {
                  var contributor = (IDataSeedContributor)scope.ServiceProvider.
                  GetRequiredService(contributorType);
                  await contributor.SeedAsync(context);
              }
          }
      }
    }
}
👍🎉🎊