【问题标题】:Constructor Dependency Injection in Base Class基类中的构造函数依赖注入
【发布时间】:2011-09-02 14:15:20
【问题描述】:

我正在使用实体框架构建一个存储库基类,所有实体存储库都将继承该基类。我想使用 Ninject 的依赖注入在基类中注入DatabaseContext。我认为构造函数注入是正确的方法,但是在派生类中使用构造函数注入进行此操作,我必须将参数传递给基类中的构造函数,而我不想要它。因此,Setter 注入更合适?

这是我的代码:

public abstract class BaseRepository<TEntity> : IDisposable 
    where TEntity : class
{
    private readonly DatabaseContext _databaseContext;

    protected BaseRepository(DatabaseContext databaseContext)
    {
        this._databaseContext = databaseContext;
    }
}

在上面的示例中使用构造函数注入,在我的派生类中,我必须将 databaseContext 对象传递给基类,我不喜欢对我的所有派生类都这样做:

public class UsuarioRepository : BaseRepository<IUsuario>,
    IUsuarioRepository
{
    public UsuarioRepository(DatabaseContext databaseContext)
        : base(databaseContext)
    {
    }
}

Setter Injection 而不是 Constructor Injection 是解决这个问题的好方法吗?最好的方法是什么?

更新:

使用 Setter 注入,我的派生类将没有构造函数:

public class UsuarioRepository : BaseRepository<IUsuario>, IUsuarioRepository
{
}

我的上下文只是所有应用程序中的一个。我不需要派生类来传递上下文对象,但我喜欢将它注入基类中,以便将来使用模拟进行测试。

我解决问题:

对不起,我对这个问题感到困惑,但我正在解决我建立工厂的问题:

public abstract class BaseRepository<TEntity> : IBaseRepository<TEntity>
   where TEntity : class
{
    private readonly ContextFactory _contextFactory = new ContextFactory();

    protected DatabaseContext CurrentContext
    {
        get
        {
            return this._contextFactory.GetCurrentContext();
        }
    }
 }

而我的派生类将如下所示:

public class UsuarioRepository : BaseRepository<IUsuario>, IUsuarioRepository
{
}

我的工厂就是这样:

public class ContextFactory
{
    public DatabaseContext GetCurrentContext()
    {
        return new DatabaseContext();
    }
}

【问题讨论】:

  • 我不太明白你的问题。为什么你不喜欢通过构造函数将 DatabaseContext 传递给基类?您是否试图避免将上下文注入派生类?我看不出上面的代码有什么问题,所以我不确定你具体要解决什么“问题”。
  • 我的派生类有一个基类的参数。使用 Setter 注入不会发生这种情况,我想知道在这种情况下使用 setter 注入是否是一个好方法。
  • 将参数传递给基类有什么问题?

标签: c# .net dependency-injection inversion-of-control


【解决方案1】:

属性注入意味着依赖是可选的,而构造函数注入意味着依赖是必需的。相应地选择一种模式。

在超过 95% 的情况下,构造函数注入是正确的模式。不喜欢怎么办?

【讨论】:

  • 我不喜欢在派生类中将参数传递给基类,使用 setter 注入不会发生这种情况。
  • @Acaz 个人偏好不谈,构造函数注入确实是您场景中唯一合适的解决方案。
  • @Acaz 这样做的问题是,如果BaseRepository&lt;TEntity&gt; 需要DatabaseContext 的实例来操作,它是一个依赖项,您不应该允许使用实例化基类的实例无效状态。构造函数用于创建应该可用的类型的实例。使用设置注入只是需要开发人员准确地知道他们需要将DatabaseContext 设置为类型初始化中的附加步骤。对于开发人员来说,理想情况下并不是最好的事情。很简单,如果是依赖,就使用构造函数注入。
  • 但问题是使用构造函数注入,我的所有派生类都将具有:基(对象)代码。我想对所需的依赖项使用 Setter Injection。这篇文章就是我要说的:davybrion.com/blog/2009/11/…
  • @Acaz Souza:是的,他们需要那个,因为依赖是必需的。由于需要依赖,让编译器强制执行这个不变量是一件好的事情。
【解决方案2】:

我认为没有“最好的方法”。

构造函数和 setter 注入并不完全等同,因为 setter 注入为您提供了更多的灵活性。以下是我在两者之间的选择:

假设您创建了一个对象 A,而 A 需要一个 B 对象才能工作。要问的问题是:A 是否有可能存在/被实例化而没有与之关联的 B? A 对象没有 B 是否是有效状态?有时 A 有一个空 B 是没有意义的,那么构造函数注入是更好的解决方案。如果可以稍后将 B 分配给 A,而不必在构建时分配,则使用 setter 注入。所有这些都取决于您尝试建模的领域,并且答案会因情况而异。

【讨论】:

  • 问题是,我不喜欢将派生类的参数传递给基类构造函数。
【解决方案3】:

真的,我看不出将参数传递给基类有什么问题。但如果这是绝对要求,您总是可以这样做:

public abstract class BaseRepository<TEntity> : IDisposable 
    where TEntity : class
{
    protected BaseRepository()
    {
    }

    protected abstract DatabaseContext GetContext();
}

现在您的派生类可以如下所示:

public class UsuarioRepository : BaseRepository<IUsuario>,
    IUsuarioRepository
{
    private DatabaseContext _databaseContext;

    public UsuarioRepository(DatabaseContext databaseContext)
    {
        _databaseContext = databaseContext;
    }

    protected override DatabaseContext GetContext()
    {
        return _databaseContext;
    }
}

现在您可以在不向基本构造函数传递参数的情况下进行构造函数注入。但其实是一回事。

老实说,我不喜欢 setter 注入的想法,因为它看起来像 DatabaseContext 是必需的依赖项。对于所需的依赖项,进行构造函数注入。如果它是可选的,那么无论如何,继续进行设置器注入。

编辑:

由于我们在 cmets 中进行了长时间的交谈,我对您要完成的工作有了更好的了解。

如果您想将派生类与 DatabaseContext 分离,最好采用不同的设计方式。派生类与其基类的依赖关系很难解耦,如果你认为它们应该解耦,那么你将有更好的设计,完全不使用继承。

public class BaseRepository<TEntity> : IDisposable 
    where TEntity : class
{
    public BaseRepository(DatabaseContext context)
    {
    }
}

现在您的派生类(不再派生)将如下所示:

public class UsuarioRepository : IUsuarioRepository
{
    public UsuarioRepository(BaseRepository<IUsario> repository)
    {
    }

    // Implement other methods here
}

现在您可以拥有一个使用 DatabaseContext 的基类,而您的派生类不再依赖它。

【讨论】:

  • 我的上下文只是所有应用程序之一!我不需要派生类来传递上下文。
  • @Phil 如果该参数在所有应用程序中只有一个,为什么我会将参数传递给我的基类构造函数?为什么不直接传入基类?
  • @Acaz 因为它是必需的依赖项。构造函数的目的是确保在构造时将所有必需的依赖项提供给类 - 换句话说,构造函数防止对象在没有正确必需依赖项的情况下能够存在。我的问题是......为什么不将参数传递给基本构造函数?这样做有什么问题?
  • @Acaz 我也不明白一些事情:为什么 DatabaseContext 是应用程序中唯一的一个很重要?这与将引用传递给构造函数没有任何关系。
  • @Phil 为什么不呢?因为 DatabaseContext 对象在所有应用程序中只有一个。所有派生类都需要使用唯一的 DatabaseContext 对象,没有其他对象。因为直接将 DatabaseContext 对象传递给基类要好得多,所以我不再需要担心 DatabaseContext 对象了。如果我明天想替换 DatabaseContext 对象,我将只在一个地方进行,避免在所有派生类中进行更改。你明白吗?
【解决方案4】:

所有三种方式1.构造函数,2.Setter和3.接口注入都有其优点和缺点,取决于使用哪一种情况。如果我在你的位置,我会选择 setter,尽管界面也是一个不错的选择。

请求您阅读这篇文章 http://www.devx.com/dotnet/Article/34066

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-14
    • 2011-02-02
    • 1970-01-01
    • 2018-06-17
    • 2013-01-30
    • 2018-01-17
    • 2019-04-20
    相关资源
    最近更新 更多