虽然我很欣赏 @berkansasmaz 让我走上了正确的道路,但这并不是一个完整的解决方案。我想我会在这里分享一些我的解决方案,希望能帮助别人。
ABP 文档中给出的所有示例都用于初始化不依赖于其他实体的实体。我想创建示例数据,以便在创建新租户时,该租户已经可以使用。
我的解决方案是修改提供的 SaasDataSeedContributor 以包含我自己的 ITenantDataSeeder 实现。
public interface ITenantDataSeeder
{
Task CreateTenantAsync();
}
public class TenantDataSeeder : ITenantDataSeeder, ITransientDependency
{
private readonly ICurrentTenant _currentTenant;
private readonly ILogger<TenantDataSeeder> _logger;
private readonly ICountryDataSeeder _countryDataSeeder;
// other private fields here
public TenantDataSeeder(
ICountryDataSeeder countryDataSeeder,
IStateDataSeeder stateDataSeeder,
IAddressDataSeeder addressDataSeeder,
IEmailAddressDataSeeder emailAddressDataSeeder,
IPhoneNumberDataSeeder phoneNumberDataSeeder,
IDepartmentDataSeeder departmentDataSeeder,
IContactDataSeeder contactDataSeeder,
ICampaignDataSeeder campaignDataSeeder,
ILeadSourceDataSeeder leadSourceDataSeeder,
IProductGroupDataSeeder productGroupDataSeeder,
IProductDataSeeder productDataSeeder,
IClientNeedDataSeeder clientNeedDataSeeder,
IRevenueRepeatFreqDataSeeder repeatFreqDataSeeder,
IOpportunityStatusDataSeeder opportunityStatusDataSeeder,
IUserDataSeeder userDataSeeder,
IClientDataSeeder clientDataSeeder,
IOpportunityDataSeeder opportunityDataSeeder,
IOpportunityLineItemDataSeeder opportunityLineItemDataSeeder,
ISalesNoteDataSeeder salesNoteDataSeeder,
ICurrentTenant currentTenant,
ILogger<TenantDataSeeder> logger)
{
// private fields initialized here
}
public async Task CreateTenantAsync()
{
// Host Seeding
await _countryDataSeeder.CreateCountriesAsync();
if (!_currentTenant.IsAvailable)
return;
// Tenant Seeding
try
{
await _stateDataSeeder.CreateStatesAsync();
await _addressDataSeeder.CreateAddressesAsync();
await _emailAddressDataSeeder.CreateEmailAddressesAsync();
await _phoneNumberDataSeeder.CreatePhoneNumbersAsync();
await _departmentDataSeeder.CreateDepartmentsAsync();
await _contactDataSeeder.CreateContactsAsync();
await _campaignDataSeeder.CreateCampaignsAsync();
await _leadSourceDataSeeder.CreateLeadSourcesAsync();
await _productGroupDataSeeder.CreateProductGroupsAsync();
await _productDataSeeder.CreateProductsAsync();
await _clientNeedDataSeeder.CreateClientNeedsAsync();
await _repeatFreqDataSeeder.CreateRevenueRepeatFreqsAsync();
await _opportunityStatusDataSeeder.CreateOpportunityStatusesAsync();
//await _userDataSeeder.CreateUsersAsync();
await _clientDataSeeder.CreateClientsAsync();
await _opportunityDataSeeder.CreateOpportunitiesAsync();
await _opportunityLineItemDataSeeder.CreateOpportunityLineItemsAsync();
await _salesNoteDataSeeder.CreateSalesNotesAsync();
}
catch (ApplicationException e)
{
_logger.LogException(e, LogLevel.Warning);
}
}
}
然后我写了几个<Entity>DataSeeder接口和实现。
请注意,这些类不要实现IDataSeeder 接口,也不实现IDataSeedContributor 接口。我可能应该将它们重新命名为 <Entity>DataSeederFragment 或类似名称以避免混淆。
以下是其中一个接口及其实现的示例:
public interface IStateDataSeeder
{
Task<IEnumerable<State>> CreateStatesAsync();
}
public class StateDataSeeder : IStateDataSeeder, ITransientDependency
{
protected static IList<State> States = new List<State>();
private readonly ICurrentTenant _currentTenant;
private readonly IGuidGenerator _guidGenerator;
private readonly ICountryDataSeeder _countryDataSeeder;
private readonly IStateRepository _stateRepository;
public StateDataSeeder(
IStateRepository stateRepository,
ICountryDataSeeder countryDataSeeder,
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant)
{
_stateRepository = stateRepository;
_countryDataSeeder = countryDataSeeder;
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
}
public async Task<IEnumerable<State>> CreateStatesAsync()
{
if (!_currentTenant.IsAvailable)
return Enumerable.Empty<State>();
try
{
var states = _stateRepository.Where(a => a.TenantId == _currentTenant.Id);
if (states.Any()) return states;
}
catch (NullReferenceException e)
{
throw new ApplicationException($"Separated tenant database not yet initialized. Run the DbMigrator.");
}
if (States.Any()) return States;
var usId = Guid.Parse(CountryConsts.IdUnitedStates);
var countries = await _countryDataSeeder.CreateCountriesAsync();
var usCountry = countries.FirstOrDefault(c => c.Id == usId);
if (usCountry == null)
throw new ApplicationException("Seeded Country United States was null.");
States.Add(await _stateRepository.InsertAsync(new State
(
id: _guidGenerator.Create(),
name: "Alabama",
code: "AL",
primary: false,
active: true,
countryId: usId
), autoSave: true));
// other states here
States.Add(await _stateRepository.InsertAsync(new State
(
_guidGenerator.Create(),
name: "Wyoming",
code: "WY",
primary: false,
active: true,
countryId: usId
), autoSave: true));
foreach (var state in States) state.TenantId = _currentTenant.Id;
return States;
}
}
注意:所有这些接口和实现都放在我的ProjectName.Domain 项目下。
我很确定其中一些可以做得更好。我争论过是否将静态类字段用作缓存,但我的下游“数据播种机”需要能够访问状态,并且存储库在[UnitOfWork] 提交它们之前无法返回它们。
我邀请您提出建设性的批评。但是,我发布此内容的目的是帮助像我这样的其他人。我花了太长时间试图弄清楚这一点。