无法从类库访问 appsettings.json

Posted

技术标签:

【中文标题】无法从类库访问 appsettings.json【英文标题】:Cannot access appsettings.json from class library 【发布时间】:2021-11-12 08:22:14 【问题描述】:

我一直在学习本教程,以便从我的类库中的 MVC 项目访问我的 appsettings.json。 geek-tutorial

我的类库中有一个这样的类

using dapper;

public class SqlDataAccess : IConfigManager

    private readonly IConfiguration _configuration;
    public SqlDataAccess(IConfiguration configuration)
    
        this._configuration = configuration;
    

    public List<T> LoadData<T>(string sql)
    
        using (IDbConnection cnn = new SqlConnection(GetConnectionString()))
        
            return cnn.Query<T>(sql).ToList();
        
    

    public int SaveData<T>(string sql, T data)
    
        using (IDbConnection cnn = new SqlConnection(GetConnectionString()))
        
            return cnn.Execute(sql, data);
        
    

    public string GetConnectionString(string connectionName = "URLShortnerDB")
    
        return this._configuration.GetConnectionString(connectionName);
    

界面:

public interface IConfigManager

    string GetConnectionString(string connectionName);

我在我的 mvc startup.cs 中添加了services.AddSingleton&lt;IConfigManager, SqlDataAccess&gt;();

但是现在我想使用我的 SqlDataAccess 类并调用另一个类的方法,例如:

public static class ShortUrlProcessor

    
    public static ShortURLModel GetOriginalURL(string shortUrl)
            
        string sql = $@"SELECT * FROM dbo.shorturl WHERE shortUrl = ' shortUrl '";
        var originalURLEnum = SqlDataAccess.LoadData<ShortURLModel>(sql); //<--- problem
        return originalURLEnum.First();
    

但是SqlDataAccess 没有被实例化,为了实现var _sqldataaccess = SqlDataAccess(),我需要传入一个在类的构造函数中定义的参数。我不知道要传递什么?我在这个 ShortUrlProcessor 类中没有任何 IconfigurationManager。我知道这样做的原因是依赖注入,但是我仍然不明白这一切是如何工作的?

【问题讨论】:

使 ShortUrlProcessor 成为非静态的,然后执行您遵循的相同构造函数注入器模式,以便 SqlDataAccess 类接收 IConfiguration 实例。您可能需要在您的 IoC 容器中注册 ShortUrlProcessor。您似乎对依赖注入概念不熟悉 - 我强烈推荐 Deep Dive Into Dependency Injection And Writing Quality Decoupled Code by Miguel Castro。这是帮助理解这些概念的非常有用的资源。 一个很好的通用指导是避免使用静态类并避免通过“new”关键字直接调用对象的构造函数(可能是愚蠢的 DTO 对象除外)。这有助于保持您的方法为依赖注入正确结构化,并最终使它们可进行单元测试。 您的代码可能容易受到 SQL 注入攻击。 (几乎)永远不要使用字符串连接来形成 SQL 查询。这是一个向量,使不受信任的输入成为 SQL 语句的一部分。相反,您应该使用参数化查询。 This page 解释了如何使用 ADO.NET、Entity Framework 或 Dapper 安全地执行 SQL。 @mason 感谢您的评论,我已经从 ShortUrlProcessor 中的所有内容中删除了静态,但是我没有完全理解这有什么帮助,我确实对依赖注入和 ASP.NET 非常陌生.您的第一条评论是否有可能被分解为进一步的步骤?谢谢。编辑:感谢深潜视频提示 如果你的类是静态的,那么你不能使用静态构造函数进行正常的依赖注入(通过构造函数注入),因此它不能轻易地使用由 DI 容器创建的对象。并且是静态的会迫使其他类直接引用它,这违反了SOLID 中的依赖倒置原则。其他需要 GetOriginalUrl 的类应该是针对抽象(接口)而不是具体实现(类)的代码。您使用 SqlDataAccess 和 IConfiguration 正确地做到了。 【参考方案1】:

您已经很接近了,但您需要解决一些问题。 SqlDataAccess 实现 IConfigManager。为什么?那提供什么?相反,您应该让它实现一个接口,允许它公开其他类所依赖的功能。

public interface ISqlDataAccess

    List<T> LoadData<T>(string sql);

    int SaveData<T>(string sql, T data);  

更改您的 SqlDataAccess 类以实现此接口...

public class SqlDataAccess : ISqlDataAccess

当然,将它与您的 DI 容器连接起来。

services.AddTransient<ISqlDataAccess, SqlDataAccess>();

现在,任何需要运行 SQL 的类都可以依赖 ISqlDataAccess 接口,利用构造函数注入来获取 ISqlDataAccess 的实例。因为我们已经告诉 DI 容器在存在 ISqlDataAccess 依赖项时提供一个 SqlDataAccess 实例,所以它会在您的应用程序中很好地连接起来。

然后我们遇到了 ShortUrlProcessor 的问题。您将该类声明为静态的。这很糟糕,因为它很难使用构造函数注入来获取其依赖项,并且任何其他需要调用其方法的类都必须直接这样做,而不是通过抽象。这违反了SOLID 的依赖倒置原则。由于可维护性和可测试性,我们应该始终努力编写 SOLID 代码,因此我们需要解决这个问题。

public class ShortUrlProcessor : IShortUrlProcessor
    
    readonly ISqlDataAccess _dataAccess;

    public ShortUrlProcessor(ISqlDataAccess dataAccess)
    
        _dataAccess = dataAccess;
    
    
    public ShortURLModel GetOriginalURL(string shortUrl)
            
        string sql = $@"SELECT * FROM dbo.shorturl WHERE shortUrl = ' shortUrl '";
        var originalURLEnum = _dataAccess.LoadData<ShortURLModel>(sql); //<--- problem
        return originalURLEnum.First();
    

我们需要一个接口,这样其他类就不必直接依赖 ShortUrlProcessor...

public interface IShortUrlProcessor

    ShortURLModel GetOriginalURL(string shortUrl);

当然,我们需要将它注册到我们的 DI 容器中。

services.AddTransient<IShortUrlProcessor, ShortUrlProcessor>();

然后任何需要访问 ShortUrlProcessor 功能的类都可以通过抽象 IShortUrlProcessor 来实现。你提到你有一个控制器调用它,所以我们也把它连接起来。

public class MyController()

    readonly IShortUrlProcessor _shortUrlProcessor;

    public MyController(IShortUrlProcessor shortUrlProcessor)
    
        _shortUrlProcessor = shortUrlProcessor;
    

    public ActionResult SomeActionMethod()
    
        var model = _shortUrlProcessor.GetOriginalURL("asdf");
        return View(model);
    

我们不必为控制器创建接口,因为控制器会被框架调用。而且我们不必将控制器与 DI 容器连接起来,因为框架会为我们处理这些。

通过执行所有这些操作,我们可以轻松地单独测试各个方法。还有一些改进的地方(我在 cmets 中提到的 SQL 注入攻击需要修复),但这是朝着正确方向迈出的一大步。

【讨论】:

@CodeMuppet 您的 SqlDataAccess 是对 Dapper 的精简包装。它并没有为您提供太多。您是否考虑过使用存储库模式?您可以创建一个 UrlRepository 类,而不是公开通用的 LoadData 和 SaveData 方法,它会有 LoadUrlViaShortUrl(string shorUrl) 和 SaveUrl 等方法。这样可以防止 SQL 实现细节泄漏到 ShortUrlProcessor 中,这不应该关心如何访问 IUrlRepository。你现在拥有它的方式,就是我们所说的“泄漏抽象”。 感谢您的建议。您是否只是说在 sqldataccess 和 shorturlprocessor 之间创建另一个层,称为 LoadUrlViaShortUrl,我在其中存储我在 shorturlprocessor 中编写的所有特定查询? @CodeMuppet 不,不是另一层。替换现有层。您的 SqlDataAccess 类是对 Dapper 的薄包装,它是一个泄漏的抽象。摆脱它,并将其替换为新的类(和接口),例如 SqlServerUrlRepository 和 IUrlRepository。让您的 ShortUrlProcessor 依赖于 IUrlRepository。这样,ShortUrlProcessor 就不必知道如何访问数据,并且 SQL 实现细节最终不会泄露到 ShortUrlProcessor。用于保存/加载的 SQL 语句应该在 SqlServerUrlRepository 中。 我明白了,这是有道理的。再次感谢

以上是关于无法从类库访问 appsettings.json的主要内容,如果未能解决你的问题,请参考以下文章

从类库中访问文件

从类库访问 Asp.net-core 中的 appsetting.json

从类库注册 Web API 控制器

asp.net MVC - 我可以从类库中引用视图吗?

从类库转换为 WCF 需要采取哪些步骤?

从类库项目中的 App.config 中读取