1 “System.InvalidOperationException: The ConnectionString property has not been initialized.”异常
1.1 异常产生原因:
一般情况下启动项中存在appsettings.json文件,且该文件中持久化存储着指定数据库软件指定数据库的连接设定数据,当前程序启动时已经完成了EntityFrameworkCore中间件的实例化,所有不会产生上述异常。但是如果启动项中不存在appsettings.json文件,且定数据库软件指定数据库的连接设定数据是通过前端页面设定时,如果在控制器类的构造方法中直接使用EntityFrameworkCore中间件实例时,这里就会由于连接字符串参数实例的缺失,直接使用构建方法中的EntityFrameworkCore中间件实例就会出现上述异常。
1.2 解决方案:
以动态方式实例化EntityFrameworkCore中间件。
注意:
以动态方式实例化EntityFrameworkCore中间件时,控制器类的构造方法中不能定义与EntityFrameworkCore中间件实例相关的定义;否则EntityFrameworkCore中间件的动态实例化操作将不能被执行,例如:
public MulDatabaseTestController(EFCoreContext context, INopFileProvider fileProvider)
{
_context = context;
_fileProvider = fileProvider;
}
2 “System.InvalidOperationException: The service collection cannot be modified because it is read-only”异常
2.1 异常产生原因:
在动态对EntityFrameworkCore中间件进行实例化时,如果使用“AddDbContext”内置方法,则会出现上述异常。
2.2 解决方案:
使用“AddDbContextPool” 内置方法替换“AddDbContext”内置方法就能解决上述异常。
3 解决步骤
3.1 Framework.Infrastructure.Extensions.ServiceCollectionExtensions. AddEFCoreContextByCreateJosn
/// <param name="services">.Net(Core)框架内置依赖注入容器实例。</param>
/// <summary>
/// 【通过新建数据库连接字符串添加EFCore上下文】
/// <remarks>
/// 摘要:
/// 动态对“EntityFrameworkCore”中间件进行实例化,为当前程序通过“EntityFrameworkCore”中间件实例与指定数据库软件中指定数据库的CURD交互操作,提供实例支持。
/// </remarks>
/// </summary>
public static void AddEFCoreContextByCreateJosn(this IServiceCollection services)
{
//注意:在动态对EntityFrameworkCore中间件进行实例化时,必须使用“AddDbContextPool” 内置方法替换“AddDbContext”内置方法,
//否则就会出现异常:“System.InvalidOperationException: The service collection cannot be modified because it is read-only”。
services.AddDbContextPool<EFCoreContext>(options => {
//从单例实例的字典成员实例中获取当前程序所有配置相关数据。
AppSettings _appSettings = Singleton<AppSettings>.Instance;
//从应用配置类实例中获取数据库连接相关数据。
DataConfig _dataConfig = _appSettings.Get<DataConfig>();
//说明:如果想要“EntityFrameworkCore”中间件支持多数据库软件,则把选择条件中的所有中间件都注入到依赖注入到.Net(Core)框架内置容器即可,
//选择条件来限定当前程序只支持所设定的1个数据库软件,当然“DataConfig”类与“appsettings.json”文件也必须为支持多数据库软件进行重构。
if (_dataConfig.DataProvider.ToString().Equals("sqlserver", StringComparison.InvariantCultureIgnoreCase))
{
//通过“DbContextOptionsBuilder”实例中的参数实例,为“Microsoft.EntityFrameworkCore.SqlServer”中间件的实例化提供参数实例,
//最终把“Microsoft.EntityFrameworkCore.SqlServer”中间件实例,依赖注入到.Net(Core)框架内置容器中。
//IIS发布部署连接字符串必须使用“SQL Server身份认证”数据库连接方式,才能实现发布部署程序与数据库的CURD的操作。
options.UseSqlServer(_dataConfig.ConnectionString);
}
else if (_dataConfig.DataProvider.ToString().Equals("mysql", StringComparison.InvariantCultureIgnoreCase))
{
// 实现“Microsoft.EntityFrameworkCore”中间件实例与“MySql”数据库的连接。
options.UseMySql(_dataConfig.ConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
}
});
}
3.2 重构Framework.Infrastructure.DbStartup.ConfigureServices
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
//抽离“EntityFrameworkCore”中间件实例的依赖注入操作,为当前程序通过“EntityFrameworkCore”中间件实例与指定数据库软件中指定数据库的CURD交互操作,提供实例支持。
//services.AddEFCoreContext();
//动态对“EntityFrameworkCore”中间件进行实例化,为当前程序通过“EntityFrameworkCore”中间件实例与指定数据库软件中指定数据库的CURD交互操作,提供实例支持。
services.AddEFCoreContextByCreateJosn();
}
3.3 WebApi.Controllers.NoJsonDataBaseTestController
using Core.Infrastructure;
using Data.Configuration;
using Data;
using Microsoft.AspNetCore.Mvc;
using WebApi.Models;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class NoJsonDataBaseTestController : ControllerBase
{
#region 拷贝构造方法与变量
private readonly INopFileProvider _fileProvider;
public NoJsonDataBaseTestController(INopFileProvider fileProvider)
{
_fileProvider = fileProvider;
}
#endregion
/// <summary>
/// 【异步动态已经生数据库?--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 获取 1个值false(生成失败)/true(成功生成),该值指示是否通过动态“EntityFrameworkCore”中间件实例已经在指定数据库软件中成功生成了指定数据库及其表。
/// </remarks>
/// <returns>
/// 返回:
/// 1个值false(生成失败)/true(成功生成)。
/// </returns>
[HttpGet]
public async Task<MessageModel<bool>> CreateJsonEFCoreAsync()
{
DataSettingsManager.SaveSettings(new DataConfig
{
DataProvider = DataProviderType.SqlServer,
ConnectionString = "Data Source=.;Initial Catalog=ShopDemo;Integrated Security=False;Persist Security Info=False;User ID=zz;Password=zz;MultipleActiveResultSets=true;Trust Server Certificate=True"
}, _fileProvider);
//DataSettingsManager.SaveSettings(new DataConfig
//{
// DataProvider = DataProviderType.MySql,
// ConnectionString = "Server=localhost;User ID=root;Password=zhoujian;Database=ShopDemo;Allow User Variables=True"
//}, _fileProvider);
//动态实例化EntityFrameworkCore中间件。
EFCoreContext _context = EngineContext.Current.Resolve<EFCoreContext>();
MessageModel<bool> _messageModel = new MessageModel<bool>();
bool _isCreatedDatabase = await _context.CreateDatabaseAndTableAsync();
_messageModel.Response = _isCreatedDatabase;
if (_isCreatedDatabase)
{
_messageModel.Success = true;
_messageModel.Message = "已经成功在数据库软件中新建指定数据库及其表!";
}
else
{
_messageModel.Success = false;
_messageModel.Message = "数据库软件中新建指定数据库及其表失败!";
}
return _messageModel;
}
}
}
按F5执行程序不管在 “Microsoft SQL Server”数据库软件中,还是在“MySql”数据库软件中都能自动生“ShopDemo” 数据库及其表。
对以上功能更为具体实现和注释见230125_017shopDemo(新建连接字符串实例化EntityFrameworkCore中间件)。