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);
}
}
}
}
}
👍🎉🎊