如何在不依赖于实现的情况下使用接口的实现?

How do I use my implementation of an interface without creating a dependency on the implementation?

我正在尝试使用我从研究 SOLID 原则中获得的知识来制作一个简单的 ASP.NET Webform。

我将我的解决方案设置为 3 个项目:主 asp.net webforms 项目、Data Access Interfaces class 库项目和 Data Access class 库项目(具有 Data Access Interfaces 项目中接口的实现)。

我在 Data Access Interfaces 程序集中有一个 ICoinStorage 接口,看起来像这样(Coin 只是一个位于 Data Access Interfaces 中的 DTO class组装):

public interface ICoinStorage
{
    void Persist(IEnumerable<Coin> coins);
}

并且该接口的实现在名为 CoinSqlServerStorageData Access 程序集中使用 ADO.NET,如下所示:

public class CoinSqlServerStorage : ICoinStorage
{
    private string sqlConnectionString;

    public CoinSqlServerStorage(string connectionStringName)
    {
        sqlConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
    }

    public void Persist(IEnumerable<Coin> coins)
    {
        using (var connection = new SqlConnection(sqlConnectionString))
        using (var command = new SqlCommand() { Connection = connection })
        {
            foreach (var coin in coins)
            {
                command.Parameters.AddWithValue("@Name", coin.Name);
                command.Parameters.AddWithValue("@Weight", coin.Weight);
                command.Parameters.AddWithValue("@Thickness", coin.Thickness);
                command.Parameters.AddWithValue("@Value", coin.Value);
                command.Parameters.AddWithValue("@Condition", (int)coin.Condition);

                command.CommandText = "INSERT INTO Coins (Name, Weight, Thickness, Value, ConditionID) " +
                                      "VALUES (@Name, @Weight, @Thickness, @Value, @Condition);";

                command.Connection.Open();
                command.ExecuteNonQuery();
            }
        }
    }
}

我的问题是:如何在 webforms 项目中使用 CoinSqlServerStorage class 而不创建对 Data Access 程序集的依赖? 我想这样做,以便用户可以访问 InsertCoin.aspx 页面来定义新硬币并将新硬币存储在数据库中...

当我准备在 InsertCoin.aspx 页面的 Page.Load 事件中创建 CoinSqlServerStorage class 的实例时,我的问题出现了,但意识到这会创建对 Data Access 程序集的依赖,而不是仅仅依赖于 Data Access Interfaces 程序集...

我该如何继续?

在这种情况下,您可以再创建一个项目并调用它,例如 Data Access DI,它将引用 Data AccessData Access Interfaces 项目。该项目将有责任向所有其他项目(将需要)提供 Data Access Interfaces 所需的实现。

但即使在这种情况下,您也会有两个依赖项:Data Access InterfacesData Access DI - 第一个将提供接口,第二个 - 将提供实现。

Data Access DI 项目会将您的其他项目与实现隔离开来,即使您将拥有多个 Data Access Whatever 项目,例如:Data Access MongoData Access SqlData Access Raven

How do I use the CoinSqlServerStorage class in the web forms project without creating a dependency on the Data Access assembly?

好吧,您可以使用支持 XML 配置的 DI 容器。这将允许您仅在 运行 时间绑定到数据访问程序集。

话虽如此,但在大多数情况下这并不是一件好事。

稍微回过头来说,web form项目引用数据访问库就可以了

Web 表单项目是一个特殊的项目,它不仅仅是一个 class 库。 Web 表单项目构成了它自己的应用程序,应用程序需要引用它使用的所有其他 class 库,而不仅仅是包含接口的库。

基本上,Web 表单项目必须包含 Composition Root,而 Composition Root 需要了解所有内容。

您可能想查看 this answer(和问题)以进行相关讨论。

How do I use the CoinSqlServerStorage class in the webforms project without creating a dependency on the Data Access assembly?

为什么你认为这是一个问题?如果您真的这样做,请在编译解决方案后查看您的 bin 文件夹。您的可执行文件有很多依赖项。

SOLID 与程序集引用无关。出色地。依赖倒置原则可能。

DIP 说:

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.

在您的例子中,高级模块(Web 表单项目)引用了低级模块 (DAL)。而您的下层模块依赖于抽象(您的 dal 接口项目)。

非常好。

但是,我通常将 DAL 接口添加到我的业务层项目中,因为驱动 DAL 接口设计的是 BL 中的需求。