Blazor Server.NET7 - 依赖注入 - Entity Framework Core - 如何在特定操作上获取新实例

问题描述 投票:0回答:2

这是我的设置:使用 .NET 7 的 Blazor 服务器应用程序。

我正在使用 .NET 的内置依赖注入。

...
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

//Models
builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>();

//Database
builder.Services.AddTransient<IDbConnectionString, DbConnectionString>();
builder.Services.AddTransient<IIMP_GG_Context, IMP_GG_Context>();
...

我使用 Entity Framework Core 连接到数据库。 现在我遇到的情况是,在我的应用程序中,用户可以在不同的环境(“实时”、“测试”、“开发”)之间进行选择。 环境的改变应该改变正在使用的数据库的连接。

所选环境保存在

Environment
对象中,并且只有一个属性
enum
。环境设置是
Scoped

builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>();

这是我的应用程序的当前结构,基于一个示例 (

ProductStatus
)。 UI 已注入一个控制器对象。这是一个例子(参见
_ProductStatusController
):

public partial class Filter
{
    ...
    [Inject] private IProductStatusController _ProductStatusController { get; set; }
    
    protected override void OnInitialized()
    {
        base.OnInitialized();
        GetAllProductStatus();
    }

    private void GetAllProductStatus()
    {
        var productStatus = _ProductStatusController.GetAllProductStatus();
        ...
    }
    ...
}

控制器已注入存储库。

public class ProductStatusController : IProductStatusController
{
    private IProductStatusRepository _ProductStatusRepository;

    public ProductStatusController(IProductStatusRepository productStatusRepository)
    {
        _ProductStatusRepository = productStatusRepository;
    }

    public List<Vorlauf> GetAllProductStatus()
    {
        return _ProductStatusRepository.GetAll();
    }
}

存储库 (

ProductStatusRepository
) 看起来像这样。这是实体框架核心上下文对象 (
_Context
)(也已注入)。

public class ProductStatusRepository : IProductStatusRepository
{
    private IIMP_GG_Context _Context;

    public ProductStatusRepository(IIMP_GG_Context impGgContext)
    {
        _Context = impGgContext;
    }

    public List<Vorlauf> GetAll()
    {
        return _Context.VorlaufTable.Where((vorlauf) => vorlauf.Art == 62).ToList();
    }
}

使用哪个连接取决于对象

Environment
。 这是
Context
类:

public class IMP_GG_Context : DbContext, IIMP_GG_Context
{
    public DbSet<Vorlauf> VorlaufTable { get; set; }
    ... //more tables

    DbConnection _Connection;
    IDbConnectionString _DbConnectionString;
    IEnvironment _Environment;

    public IMP_GG_Context(IDbConnectionString dbConnectionString)
    {
        _DbConnectionString = dbConnectionString;
        Database.SetCommandTimeout(600);
    }


    public IMP_GG_Context(DbConnection connection, IDbConnectionString dbConnectionString)
    {
        _DbConnectionString = dbConnectionString;
        _Connection = connection;
    }

    ~IMP_GG_Context()
    {
    }

    private void OnEnvironmentChanged(object sender, PropertyChangedEventArgs e)
    {
        OnConfiguring(new DbContextOptionsBuilder());
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (_Connection == null)
        {
            string connection = _DbConnectionString.GetImpGgConnectionString();
            optionsBuilder.UseSqlServer(connection);
        }
        else
        {
            optionsBuilder.UseSqlServer(_Connection);
        }
    }
}

_DbConnectionString
属性提供了一种根据所选环境获取正确连接字符串的方法。

我认为,我现在遇到的问题是,在创建 UI 组件时,

_ProductStatusController
对象就被创建了一次。因此数据库连接只会创建一次,并且不会在环境发生变化时更新。有什么办法解决这个问题吗?我可以做什么来重新创建
_ProductStatusController
对象?


更新

我尝试了 Svyatoslav Danyliv 提供的解决方案。但它实际上将我选择的环境重置为默认值(=“测试”)。

这是我在过滤器 UI 中使用的新代码:

private void GetAllProductStatus()
{
    using (var scope = _ServiceProvider.CreateScope())
    {
        var environment = scope.ServiceProvider.GetRequiredService<IEnvironment>();
        environment = _Environment;
        var productStatusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>();
        var productStatus = productStatusController.GetAllProductStatus();
        ...
    }
}

如您所见,此阶段环境设置正确(=“Dev”):

但是当我执行下一步时

var productStatusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>();

...环境正在重置为“测试”:

我在这里做错了什么吗?

c# dependency-injection entity-framework-core blazor-server-side .net-7.0
2个回答
0
投票

您可以自己控制示波器的使用寿命。

制作所有内容,而不是

Filter
范围:

//Models
builder.Services.AddScoped<IEnvironment, ApplicationLogic.Models.Environment>();

//Database
builder.Services.AddScoped<IDbConnectionString, DbConnectionString>();
builder.Services.AddScoped<IIMP_GG_Context, IMP_GG_Context>();

//Filter
builder.Services.AddTransient<IFilter, Filter>();

注入您的服务

IServiceProvider

public class Filter
{
    IServiceProvider _serviceProvider;

    public Filter(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider
    }

    public void GetAllProductStatus()
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            // istantiate IProductStatusController manually
            var statusController = scope.ServiceProvider.GetRequiredService<IProductStatusController>();
            var productStatus = statusController.GetAllProductStatus();
            ...

            // after disposing Scope - IProductStatusController, IIMP_GG_Context, IProductStatusRepository ect. will be disposed automatically
        }        
    }
}

无论如何,看起来您还有另一个与 Blazor 无关的问题 - 如何正确配置

DbContext
。自己打电话给
OnConfiguring
没有任何帮助。

添加适当的构造函数。其他和

OnConfiguring
覆盖应删除。

public class IMP_GG_Context: DbContext, IIMP_GG_Context
{
    public IMP_GG_Context(IDbConnectionString dbConnectionString) : base(GetOptions(dbConnectionString))
    {
    }

    private static DbContextOptions<IMP_GG_Context> GetOptions(IDbConnectionString dbConnectionString)
    {
        return new DbContextOptionsBuilder<IMP_GG_Context>()
            .UseSqlServer(dbConnectionString.GetImpGgConnectionString())
            .Options;
    }
}

0
投票

我现在已经可以工作了。这是我最终的解决方案:

首先我对我的

DbContext
做了一些改变:

public class IMP_GG_Context : DbContext, IIMP_GG_Context
{
    public DbSet<Vorlauf> VorlaufTable { get; set; }
    ... //more tables

    IDbConnectionString _DbConnectionString;

    [ActivatorUtilitiesConstructor]
    public IMP_GG_Context(DbContextOptions<IMP_GG_Context> options, IDbConnectionString dbConnectionString) : base(options)
    {
        _DbConnectionString = dbConnectionString;
    }

    ~IMP_GG_Context()
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_DbConnectionString.GetImpGgConnectionString());
    }
}

然后我更改了

Program.cs
中的配置以使用
AddDbContextFactory

...
builder.Services.AddDbContextFactory<IMP_GG_Context>(options =>
{
    var environment = new ApplicationLogic.Models.Environment();
    DbConnectionString dbConnectionString = new DbConnectionString(environment);
   options.UseSqlServer(dbConnectionString.GetImpGgConnectionString());
}, ServiceLifetime.Scoped);
...

...最后,我在加载数据时更改了存储库以在

IDbContextFactory
的帮助下获得“新鲜”实例:

public class ProductStatusRepository : IProductStatusRepository
{
    private IIMP_GG_Context _Context;
    private IDbContextFactory<IMP_GG_Context> _ContextFactory;

    public ProductStatusRepository(IDbContextFactory<IMP_GG_Context> dbContextFactory)
    {
        _ContextFactory = dbContextFactory;
    }

    public List<Vorlauf> GetAll()
    {
        _Context = _ContextFactory.CreateDbContext();
        return _Context.VorlaufTable.Where((vorlauf) => vorlauf.Art == 62).ToList();
    }
}

通过这些更改,将使用正确的环境。 我实际上不知道这是否是使用不同数据库服务器/环境的推荐方式。

© www.soinside.com 2019 - 2024. All rights reserved.