访问 DBContext 而不一直注入它

Posted

技术标签:

【中文标题】访问 DBContext 而不一直注入它【英文标题】:Accessing DBContext without injecting it all the way down 【发布时间】:2018-03-15 23:45:36 【问题描述】:

我在使用/通过反射的整个依赖注入时遇到了一些问题。

场景如下;

用户通过我们的身份服务器通过 AzureAD 进行身份验证 如果用户不在本地数据库中,请将用户连同其他一些信息一起保存

我一直在重构我的逻辑,但我仍然无法解决这个难题。

目前这是我的连锁店:

OWIN 启动:

我指定了在 OnTokenValidated 事件触发后运行的方法:ProfileEvents.ValidatedToken

services.AddAuthentication()
                .AddOpenIdConnect("oidc", o =>
                
                    o.ClientId = $"configuration["Globals:AzureApplication:AppId"]";
                    o.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                    o.Authority = $"https://login.microsoftonline.com/configuration["Globals:AzureApplication:TenantId"]";
                    o.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
                    o.Events = new OpenIdConnectEvents()
                    
                        OnTokenValidated = ProfileEvents.ValidatedToken
                    ;
                );

DatabaseContext 是这样添加的:

    public void ConfigureServices(IServiceCollection services)
    
    .... // other stuff

    services.Configure<Config.ConnectionStrings>(configuration.GetSection("ConnectionStrings"));

    services.AddDbContext<ModelContext>(options => options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
    

我的 ValidatedToken 方法如下所示:

 internal static class ProfileEvents
    
        internal static Task ValidatedToken(TokenValidatedContext context)
        
            var dbContext = new ProfileDAL();
            dbContext.SaveToDb(context.Principal);
            return Task.FromResult(0);
        
    

最后我的 ProfileDAL 看起来像这样:

public class ProfileDAL
    
       internal async Task SaveToDb(ClaimsPrincipal principal)
        
            var nameClaim = principal.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn");
            var emailClaim = principal.FindFirstValue("email");
            // TODO: Save user..
        
     

现在我转向哪种方式,我要么必须使用并通过链传递 IOptions,以便 ModelContext 可以覆盖“OnConfiguring”以实际获取连接字符串,要么我必须传递上下文。

真的没有办法访问 DI 之外的任何一个连接字符串吗?

对我来说,我觉得这样的事情可以解决我当前的所有问题:

  public partial class ModelContext : DbContext
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        
            optionsBuilder.UseSqlServer( ** Magic way to access connection string in appsettings ** );
        
     

【问题讨论】:

我可能不明白。但最好是注入连接字符串,而不是从 appsettings 访问。如果您的班级从 appsettings 读取,那么您依赖于 appsettings。如果你的类需要一个连接字符串,那么它应该依赖于一个连接字符串,而不是 appsettings 反过来提供一个连接字符串。 整个想法是将连接字符串保留在应用程序设置中,以便在可能的五年内有人不必经历这一切并手动替换我将在任何地方传递的字符串。在 .NET 4.5.6 及更低版本中,您只需调用配置管理器来访问来自 web/app.config 的值。尚未在 .NET Core 中找到类似的替代品。 明白。问题/问题是我们访问 web.config 的哪里。最好注入字符串或暴露字符串的接口。这样你的类只依赖于字符串。你的班级不在乎它来自哪里。无论将该连接字符串注入到您的类中 - 都可以从 web.config 中获取它。 不希望直接从类中引用ConfigurationManager。如果您想编写两个测试并且每个测试都需要不同的值怎么办?如果注入值,则很容易。如果班级正在阅读 web.config,则不可能。或者您在不同的运行时场景中需要不同的值。或者您想将代码移植到不存在 web.config 的环境中。 【参考方案1】:

当您谈到“传递链条”时,这应该没有必要。如果我们使用依赖注入容器,那么我们不必将依赖项传递给一个类,也不必将依赖项传递给另一个类,依此类推。事实上,这是我们通过使用容器来避免的一部分。

我们不必这样做,因为每个类都将其依赖项作为抽象(通常是接口)接收。这意味着一个类不能将某些东西传递给它的依赖项,因为它甚至不知道该依赖项需要什么。

与其将某些东西从一个类传递到另一个类,就像在一个链中一样,每个单独的类都通过它需要的依赖关系来解决。链中的所有类都不负责为其依赖项提供构造函数参数。

当我们使用像ProfileEvents 这样的静态类时,这变得更加困难。您不能将ProfileDAL 注入其中,因为它是静态的。然后,因为 ProfileDAL 没有得到“新” - new ProfileDAL() - 没有办法注入它的依赖项。

这反过来意味着您不会将构造函数注入与ProfileDAL 一起使用,并且您将寻找其他方法来创建和传递其依赖项。但是使用 DI 容器应该可以防止这种情况发生。在依赖关系图中,一切都遵循相同的模式,每个类在其构造函数中获取其依赖关系,而不知道或关心 那些 类有什么依赖关系。

【讨论】:

我读过的关于 DI 和 EF 的 MSDN 文章做了以下事情:

以上是关于访问 DBContext 而不一直注入它的主要内容,如果未能解决你的问题,请参考以下文章

注入 DbContext 时无法访问 ASP.NET Core 中已释放的对象

C#:使用 IQueryable 注入 DbContext 时,无法访问 ASP.NET Core 中的已处置对象

如何处理数据加载器/GraphQL 嵌套查询中的并发 DbContext 访问?

DbContext上的ObjectDisposedException

将 DbContext 与依赖注入一起使用

带有私有 NuGet 包的 Blazor 依赖项注入和 EF/DbContext