【问题标题】:Returning user defined objects using dependency injection使用依赖注入返回用户定义的对象
【发布时间】:2015-03-30 13:59:14
【问题描述】:

我正在使用 Ninject 进行依赖注入,并且在传递基本类型时它运行良好,但我不确定在开始传递自定义对象类型后构建应用程序的最佳方式。

这是一个简单的例子。我有 4 个项目:主要、项目 A、项目 B 和接口。主要参考 ProjectA、ProjectB 和 Interfaces 以及 Project A 和 ProjectB 都参考 Interfaces。

在接口中,我定义了一个接口 IClassA,它有一个返回 int 的方法。

public interface IClassA
{
    int ReturnFundamental();
}

在 ProjectA 中,我有实现 IClassA 的 ClassA。

public class ClassA : IClassA
{
    public int ReturnFundamental()
    {
        return 1;
    }
}

ProjectB 包含 ClassB,它接受 IClassA 作为基于构造函数的依赖注入。它还提供了一个方法,允许我调用在 IClassA 中定义并在 ClassA 中实现的方法。

public class ClassB
{
    private readonly IClassA classA;
    public ClassB(IClassA classA)
    {
        this.classA = classA;
    }
    public void GetObject()
    {
        Console.WriteLine(classA.ReturnFundamental());
    }
}

Main 项目设置 Ninject,将 IClassA 绑定到 ClassA,实例化 ClassB 的实例并调用 ClassB 上的方法。

static void Main(string[] args)
{
    IKernel kernel = new StandardKernel();
    kernel.Bind<IClassA>().To<ClassA>();
    var classB = kernel.Get<ClassB>();
    classB.GetObject();
    Console.Read();
}

这一切都很好,ClassB可以调用ClassA中的方法并返回值。但是,如果我不想返回基本类型,而是想返回我定义的自定义对象,那么最好的方法是什么?

如果我没记错的话,IClassA、ClassA 和 ClassB 都需要对该对象的引用,并且在与 ClassA 或 ClassB 相同的项目中定义它会导致使用依赖注入来避免耦合。

所以我能想到的唯一解决方案是:

  1. 声明接口项目中的所有对象

  2. 再创建一个项目,把所有用来在接口间传递数据的对象都放上去。

  3. 创建一个新项目,其中包含类之间的单个交互所需的所有对象,其中每个交互都有自己的项目,它引用包含它需要的所有对象。

我看到第一个选项的问题是不清楚哪些类使用了哪些对象,我担心接口项目会变得太大,在一个位置包含太多信息。

第二个选项与第一个选项有相同的问题,即不清楚哪些类使用哪些对象,但至少它将接口与仅用于传递数据的对象分开。然而,它会在项目之间添加更多引用,因为(假设每个接口至少传递一个对象)每个接口和每个实现该接口或使用该接口的类都需要对新项目的引用。

第三个选项至少明确了哪些类交互需要哪些对象,因为它们都被组合在一起,但是它会导致创建大量项目。

我想有一个很好的解决方案,因为它一定是一个非常常见的问题,但我不确定大型项目的最佳实施。

【问题讨论】:

    标签: c# architecture dependency-injection ninject


    【解决方案1】:

    无需创建界面项目。 通常所做的是将实现(-details)声明为内部的,将接口声明为公共的。例如。

    public class PersonInfo
    {
        string FirstName { get; set; }
        string Surname { get; set; }
        Address Address { get; set; }
    }
    
    public interface IPersonSearchService
    {
        IEnumerable<PersonInfo> Search(string keyword);
    }
    
    internal class PersonSearchService : IPersonSearchService
    {
        ....
    }
    

    现在你添加一个 NinjectModule:

    public class PersonSearchServiceModule : NinjectModule 
    {
        public override void Load()
        {
            this.Bind<IPersonSearchService>().To<PersonSearchService>();
        }
    }
    

    在组合根(应用程序项目、主项目、..whatchamacallit)中,您可以通过kernel.Load&lt;PersonSearchServiceModule&gt;() 或通过kernel.Load(IEnumerable&lt;Assembly&gt;) 重载的一个(或多个)程序集的所有Module 显式加载模块。

    使用Feature-Namespaces 也是一个好主意。

    【讨论】:

    • 但是每个必须调用这个接口的项目都必须引用整个项目,包括类本身。这不会破坏 DI 的解耦点吗?如果相关,我将接口分离到自己的项目中的想法来自this microsoft tutorial
    • 这样做到底有什么问题? ;-) 当然我同意这与将接口放在单独的项目中完全不同,有时这样做可能是必要或明智的,但在绝大多数情况下情况并非如此。将接口放在单独的项目中很有用的一个示例是,当您定义扩展点的接口时,其 实现 对应用程序来说是未知的并且可以稍后添加(--> 插件!)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    • 2022-06-15
    • 2023-03-12
    相关资源
    最近更新 更多