【译】在 ABP 框架项目中配置多个数据库上下文
介绍
随着组织的发展和变得更加复杂,您可能会发现自己有多个数据库平台来满足不同的需求。例如,您可能希望在报表实体上利用 NoSQL 方法来利用该平台的速度和动态对象灵活性,同时在传统的 SQL 解决方案中维护更结构化的数据元素。本文旨在引导您完成扩展 ABP 框架解决方案和利用多数据库体系结构的基本步骤。
源码
此应用程序的源代码可以在 GitHub 上找到。
要求
运行解决方案需要以下工具:
• .NET 6.0 SDK
• MongoDb服务器
• SQL Server
发展
创建项目
首先,我们将通过命令行创建一个新的 ABP 框架项目。由于我们将使用 SQL 实例作为默认数据库,因此我们将在命令行中指定:
abp new MultipleDbContextDemo -t app -u mvc --mobile none --database-provider ef -csf
更新连接字符串
在主 Web 项目中,打开文件并使用相应的连接字符串添加更新对象:appsettings.jsonConnectionStrings
"ConnectionStrings": {
"Default": "Server=localhost;Database=MultipleDbContextDemo;Trusted_Connection=True",
"Mongo": "mongodb://localhost:27017/MultipleDbContextDemo"
},
对解决方案的 Db 移动器项目中的文件重复此过程。appsettings.json
添加要由 MongoDb 处理的实体类型
好吧,所以我们拆分 DbContext 是有原因的。为了模拟这个过程,我们将创建一个由MongoDb显式处理的实体,以便我们可以在DbContext中定义它。首先在项目下添加一个目录,并在另一个下添加一个目录。在这里,我们将放置构成数据元素的类文件,这些文件进入 MongoDb。MultipleDbContext.DomainDataElementSubDataElement
创建类SubDataElements
在目录中,创建一个名为 的新类,并使用以下内容填充它:SubDataElementsSubDataElement.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MultipleDbContextDemo.SubDataElements
{
public class SubDataElement
{
public SubDataElement()
{
}
public SubDataElement(string name, string value)
{
Name = name;
Value = value;
}
public string Name { get; set; }
public string Value { get; set; }
public override bool Equals(object obj)
{
SubDataElement subDataElement = obj as SubDataElement;
if(subDataElement == null) return false;
if (!string.Equals(subDataElement.Name, this.Name, StringComparison.InvariantCultureIgnoreCase)) return false;
if (!string.Equals(subDataElement.Value, this.Value, StringComparison.InvariantCultureIgnoreCase)) return false;
return true;
}
}
}
注意:此类将嵌套在类中,因此我们不需要定义 ABP 实体声明。DataElement
创建实体DataElements
在目录中,创建一个新类,并使用以下内容进行填充:DataElementsDataElement.cs
using MultipleDbContextDemo.SubDataElements;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;
namespace MultipleDbContextDemo.DataElements
{
public class DataElement : FullAuditedAggregateRoot<Guid>
{
public DataElement()
{
}
public DataElement(string name, string description)
{
Name = name;
Description = description;
}
[NotNull]
public string Name { get; set; }
[NotNull]
public string Description { get; set; }
public ICollection<SubDataElement> SubDataElements { get; private set; } = new Collection<SubDataElement>();
/* helper methods for handling the SubDataElements collection */
private bool IsInSubDataElements(SubDataElement subDataElement)
{
return SubDataElements.Any(x=>x.Equals(subDataElement));
}
public void AddSubDataElement(SubDataElement subDataElement)
{
Check.NotNull(subDataElement, nameof(subDataElement));
if(IsInSubDataElements(subDataElement))
{
return;
}
SubDataElements.Add(subDataElement);
}
public void RemoveSubDataElement(SubDataElement subDataElement)
{
Check.NotNull(subDataElement, nameof(subDataElement));
if (!IsInSubDataElements(subDataElement))
{
return;
}
SubDataElements.RemoveAll(x => x.Equals(subDataElement));
}
public void RemoveAllSubDataElements()
{
SubDataElements.Clear();
}
}
}
注意:这是我们的根实体,它包含未定义为实体的对象集合,因此与MongoDb等无模式数据库解决方案比MSSQL等基于模式的解决方案更兼容。
创建文件IDataElementRepository.cs
在目录中,创建一个新类并添加以下内容:DataElementsIDataElementRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace MultipleDbContextDemo.DataElements
{
public interface IDataElementRepository : IRepository<DataElement, Guid>
{
}
}
创建文件DataElementManager.cs
在目录中,创建一个新类文件,并添加以下内容:DataElementsDataElementManager
using MultipleDbContextDemo.SubDataElements;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
namespace MultipleDbContextDemo.DataElements
{
public class DataElementManager : DomainService
{
private readonly IDataElementRepository _dataElementRepository;
public DataElementManager(IDataElementRepository dataElementRepository)
{
_dataElementRepository = dataElementRepository;
}
public async Task<DataElement> CreateAsync(string name, string description, ICollection<SubDataElement> subDataElements = null)
{
Check.NotNull(name, nameof(name));
Check.NotNull(description, nameof(description));
var dataElement = new DataElement(GuidGenerator.Create(), name, description);
if(subDataElements != null)
{
foreach(var subDataElement in subDataElements)
{
dataElement.AddSubDataElement(subDataElement);
}
}
return await _dataElementRepository.InsertAsync(dataElement);
}
public async Task<DataElement> UpdateAsync(Guid id, string name, string description, ICollection<SubDataElement> subDataElements = null)
{
Check.NotNull(name, nameof(name));
Check.NotNull(description, nameof(description));
var dataElement = (await _dataElementRepository.WithDetailsAsync()).Where(x => x.Id == id).FirstOrDefault();
if (dataElement == null || dataElement == default)
throw new UserFriendlyException($"Data Element not found with Id: {id}");
dataElement.Name = name;
dataElement.Description = description;
if (subDataElements != null)
{
subDataElements = new Collection<SubDataElement>();
foreach (var subDataElement in subDataElements)
{
dataElement.AddSubDataElement(subDataElement);
}
}
return await _dataElementRepository.UpdateAsync(dataElement);
}
}
}
创建文件DataElementDataSeedContributor.cs
我们希望看到运行中的数据,因此请创建一个数据种子参与者,以将一些初始数据添加到存储库中。在目录中,创建一个调用以下内容的新文件:DataElementsDataElementDataSeedContributor.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
namespace MultipleDbContextDemo.DataElements
{
public class DataElementDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IDataElementRepository _dataElementRepository;
private readonly IGuidGenerator _guidGenerator;
public DataElementDataSeedContributor(IDataElementRepository dataElementRepository, IGuidGenerator guidGenerator)
{
_dataElementRepository = dataElementRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
if(await _dataElementRepository.GetCountAsync() == 0)
{
var dataElements = new List<DataElement>();
var de1 = new DataElement(_guidGenerator.Create(), "DataElement1", "The first Data Element");
de1.AddSubDataElement(new SubDataElements.SubDataElement("DE1SE1", "Test Data 1"));
de1.AddSubDataElement(new SubDataElements.SubDataElement("DE1SE2", "Test Data 2"));
dataElements.Add(de1);
var de2 = new DataElement(_guidGenerator.Create(), "DataElement2", "The second Data Element");
de2.AddSubDataElement(new SubDataElements.SubDataElement("DE2SE1", "Test Data 1"));
dataElements.Add(de2);
var de3 = new DataElement(_guidGenerator.Create(), "DataElement3", "The third Data Element");
de3.AddSubDataElement(new SubDataElements.SubDataElement("DE3SE1", "Test Data 1"));
de3.AddSubDataElement(new SubDataElements.SubDataElement("DE3SE2", "Test Data 2"));
de3.AddSubDataElement(new SubDataElements.SubDataElement("DE3SE3", "Test Data 3"));
de3.AddSubDataElement(new SubDataElements.SubDataElement("DE3SE4", "Test Data 4"));
dataElements.Add(de3);
var de4 = new DataElement(_guidGenerator.Create(), "DataElement4", "The fourth Data Element");
dataElements.Add(de4);
await _dataElementRepository.InsertManyAsync(dataElements);
}
}
}
}
添加MongoDb项目
我不认为为MongoDb架构创建一个新项目是绝对必要的,但是为了保持代码结构整齐,我们将这样做。
专业提示:您可以使用以下命令在不同的文件夹中创建具有相同名称的新项目,并从那里复制MongoDb项目:。为了我们的目的,我们将明确地通过它abp new MultipleDbContextDemo -t app -u mvc --mobile none --database-provider mongodb -csf
添加项目
在主解决方案中,右键单击该文件夹并添加新项目。选择“类库”项目类型,并针对 .NET 6.0(长期支持)框架(或阅读本文时最新的任何框架)调用它。src MultipleDbContextDemo.MongoDb
创建项目时。删除默认文件并转到下一步。Class1.cs
使用必要的引用更新MultipleDbContextDemo.MongoDb.csproj
将以下行添加到 标记内的 csproj 文件中:<Project>
using MongoDB.Driver;
using MultipleDbContextDemo.DataElements;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
namespace MultipleDbContextDemo.MongoDb;
[ConnectionStringName("Mongo")]
public class MultipleDbContextDemoMongoDbContext : AbpMongoDbContext
{
public IMongoCollection<DataElement> DataElements => Collection<DataElement>();
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
base.CreateModel(modelBuilder);
modelBuilder.Entity<DataElement>(b => { b.CollectionName = $"{MultipleDbContextDemoConsts.DbTablePrefix}DataElements"; });
}
}
创建文件MultipleDbContextDemoMongoDbModule.cs
在目录中,创建一个新类,并放入以下内容:MongoDbMultipleDbContextDemoMongoDbModule.cs
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
using Volo.Abp.Uow;
namespace MultipleDbContextDemo.MongoDb;
[DependsOn(
typeof(MultipleDbContextDemoDomainModule)
)]
[DependsOn(typeof(AbpMongoDbModule))]
public class MultipleDbContextDemoMongoDbModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddMongoDbContext<MultipleDbContextDemoMongoDbContext>(options =>
{
options.AddDefaultRepositories();
});
Configure<AbpUnitOfWorkDefaultOptions>(options =>
{
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled;
});
}
}
创建文件MongoDataElementRepository.cs
这将实现接口并使用依赖关系注入进行注册。在项目中,创建一个目录,然后在该文件夹中创建一个类,该文件夹调用以下内容:IDataEntryRepositoryMultipleDbContextDemo.MongoDbDataElementsMongoDataElementRepository.cs
using MultipleDbContextDemo.DataElements;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
namespace MultipleDbContextDemo.MongoDb.DataElements
{
public class MongoDataElementRepository : MongoDbRepository<MultipleDbContextDemoMongoDbContext, DataElement, Guid>, IDataElementRepository
{
public MongoDataElementRepository(IMongoDbContextProvider<MultipleDbContextDemoMongoDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
}
这就是我们现在需要添加到MongoDb项目的全部内容。下一步是确保 Web 项目了解新的数据库上下文。
使用对新 MongoDb 项目的引用更新 Web 项目模块。
为了使服务在以后需要时可用,我们需要使主项目模块知道新的MongoDb项目。
编辑 以包MongoDb 项目参考MultipleDbContextDemo.Web.csproj
在编辑器中,打开文件并在标记底部添加以下行:MultipleDbContextDemo.Web.csproj
<ItemGroup>
<ProjectReference Include="..\MultipleDbContextDemo.MongoDb\MultipleDbContextDemo.MongoDB.csproj" />
</ItemGroup>
添加对文件的引用MultipleDbContextDemoWebModule.cs
在编辑器中,打开文件并添加以下行:MultipleDbContextDemoWebModule.cs
• 在顶部的 using 子句中,添加:using MultipleDbContextDemo.MongoDb;
• 直接在类声明的上方,在现有的 DependsOn
语句上方(如果您愿意,也可以将其添加到现有语句中),添加:[DependsOn(typeof(MultipleDbContextDemoMongoDbModule))]
重要说明:您计划用于“默认”连接字符串的任何 DBMS 都必须在 DependsOn 语句中排在最后
添加这些内容后,您的项目现在应该知道新的 DbContext,并且它应该已准备好在代码中使用。接下来,我们需要更新数据库迁移器项目,以便它也知道新的 DbContext。
使用对新 MongoDb 项目的引用更新数据库迁移器项目模块。
这些步骤应该与我们刚刚在Web项目中执行的操作相同,我们只需要使Db移动器项目知道新的MongoDb项目。
编辑文件以包含MongoDb项目引用MultipleDbContextDemo.DbMigrator.csproj
在编辑器中,打开文件并在标记底部添加以下行:MultipleDbContextDemo.DbMigrator.csproj
<ItemGroup>
<ProjectReference Include="..\MultipleDbContextDemo.MongoDb\MultipleDbContextDemo.MongoDB.csproj" />
</ItemGroup>
添加对文件的引用MultipleDbContextDemoDbMigratorModule.cs
在编辑器中,打开项目内的文件并添加以下行:MultipleDbContextDemoDbMigratorModule.csMultipleDbContextDemo.DbMigrator
• 在顶部的 using 子句中,添加:using MultipleDbContextDemo.MongoDb;
• 直接在类声明的上方,在现有的 DependsOn
语句上方(如果您愿意,也可以将其添加到现有语句中),添加:[DependsOn(typeof(MultipleDbContextDemoMongoDbModule))]
重要说明:您计划用于“默认”连接字符串的任何 DBMS 都必须在 DependsOn 语句中排在最后
生成解决方案并运行 Db 迁移器来设定数据库的种子。
生成解决方案以确保不会收到任何错误,然后右键单击该项目并选择“调试 ->启动新实例”。这将运行调试程序并为数据库设定种子。MultipleDbContextDemo.DbMigrator
如果查看数据库,您将看到所有 AbpModule 的标准数据元素都在 SQL 数据库中:
该表仅存在于 MongoDb 实例中:DataElement
您还会注意到 MongoDb 中的数据元素具有嵌套对象,这与我们在数据种子类中概述的完全一样:
此时,您应该能够在 App 服务中引入或通过依赖关系注入,并从 mongodb 进行读/写。IDataElementRepositoryDataElementManager