构造函数中的Autofac依赖关系解析与使用方法中的解析

时间:2017-04-05 17:49:52

标签: c# dependency-injection autofac

在开始提问之前,以下是我的申请背景:

  • 应用程序:使用ASP.NET MVC和C#的Web应用程序
  • ORM:实体框架
  • 依赖注入容器:Autofac

考虑我有一个只作为数据库表的查找类的类。我的数据库包含大约350个表。以下是一个简化的示例,它准确显示了我的应用程序中的结构。

(如果你认为你已经有了这个场景,你可以直接跳到底部的问题。)

这是查找类

public class Lookup
{
    private readonly IRepository<Table001> _table001Repository;
    // repositories Table002 to Table349
    private readonly IRepository<Table350> _table350Repository;

    public Lookup(
        IRepository<Table001> table001Repository,
        // parameters table002Repository to table349Repository
        IRepository<Table350> table350Repository)
    {
        _table001Repository = table001Repository;
        // assignments for table002Repository to table349Repository
        _table350Repository = table350Repository;
    }

    public Table001 GetTable001(/* fields for looking up Table001 */)
    {
        // lookup logic using the repository for Table001, namely _table001Repository
    }

    // other lookup methods for Table002 to Table349

    public Table350 GetTable350(/* fields for looking up Table350 */)
    {
        // lookup logic using the repository for Table350, namely _table350Repository
    }
}

现在,使用此Lookup类的示例类:

public class MyClass
{
    private readonly ILookup _lookup;

    public MyClass(ILookup lookup)
    {
        _lookup = lookup;
    }

    public Method()
    {
        Table001 table001 = _lookup.GetTable001(/* fields */);
        Table002 table002 = _lookup.GetTable002(/* fields */);
        Table003 table003 = _lookup.GetTable003(/* fields */);
        ...
    }
}

以这种方式注册Autofac:

// Lookup class registration
builder.RegisterType<Lookup>().As<ILookup>().InstancePerLifetimeScope();

// generic registration for Repositories
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

问题:

如您所见,实际使用Lookup类的类仅使用350种查找方法中的三种方法。但是在LookupMyClass字段)中实例化_lookup类时,所有350个存储库也被解析,实例化350个存储库。现在我关心的是性能/效率。以下哪两个选项在性能方面更好,为什么?

  1. 解决现在正在进行的依赖关系,即在构造函数中使用自动注入。
  2. 仅在实际使用存储库的方法中解析存储库,而不是在构造函数中自动注入。例如,GetTable001()将变为此(字段_table001Repository将从Lookup类中删除,因此其构造函数中的参数table001Repository也是如此:

    public Table001 GetTable001(/* fields for looking up Table001 */)
    {
        IRepository<Table001> table001Repository = container.Resolve<IRepository<Table001>>();
        // lookup logic
    }
    
  3. 在选项1中,在解析_lookup时实例化所有350个存储库,例如,类MyClass仅使用三种查找方法。第二种方法是否优于第一种方法?或者,第一个选项实际上是否优于第二个表现?此外,解决方案的范围(InstancePerLifetimeScopeInstancePerDependency等)是否会有所不同?

    感谢。

    PS:如果您认为最好在SoftwareEngineering发布此问题,请发表评论。在这里提出这个问题的原因是SO所拥有的广泛受众。

2 个答案:

答案 0 :(得分:1)

这里有几点不同的事情需要考虑:

首先,还有其他任何使用ILookup实例的东西吗?如果是这样,您已经创建了这350个存储库的实例,因此在这方面它并不重要。

其次,注入成员的非构造函数解析(例如,使用ServiceLocator)被认为是反模式(http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/)。因此,如果可能的话,我会尽量避免。 (正如文章中所述)最大的原因是它隐藏了消费者的类依赖性。

第三,您是否有任何理由无法创建仅包含必要存储库作为依赖项的新服务?如果您可以创建一个IMyClassLookup服务来实现ILookup接口,该接口只包含您需要的必要存储库,那么这绝对是最简单,最干净的方法。然后,您只需注册新服务并在构造函数中使用它:

新界面

public interface IMyClassLookup : ILookup
{
}

新类

public class MyClassLookup : IMyClassLookup
{
    private readonly IWhateverRepository _whateverRepository;

    public MyClassLookup(IWhateverRepository whateverRepository)
    {
        _whateverRepository = whateverRepository;
    }

    // IMyClassLookup implementations here that use _whateverRepository
}

依赖注册

builder.RegisterType<MyClassLookup>().As<IMyClassLookup>().InstancePerLifetimeScope();

您的班级

public class MyClass
{
    private readonly IMyClassLookup _myClassLookup;

    public MyClass(IMyClassLookup myClassLookup)
    {
        _myClassLookup = myClassLookup;
    }

    public Method()
    {
        Table001 table001 = _myClassLookup.GetTable001(/* fields */);
        Table002 table002 = _myClassLookup.GetTable002(/* fields */);
        Table003 table003 = _myClassLookup.GetTable003(/* fields */);
        ...
    }
}

答案 1 :(得分:1)

似乎 查找 类本身是 服务定位器模式

IRepository<Table001> table001Repository = container.Resolve<IRepository<Table001>>();

服务定位器几乎没有问题。其中一个是它没有跟随 不打电话给我们,我们称之为原则 。它直接询问我们需要的东西,而不是把它们交给我们。

使用服务定位器,我们必须检查代码,搜索检索所需服务的反复无常的调用。构造函数注入允许我们通过IntelliSense查看构造函数或远程查看依赖项 - 所有这些依赖项。

解决方案

理想情况下,您希望将IRepository<Table001>注入MyClass。我也在我的projects中使用了类似的方法。

public class MyClass
{
    private readonly IRepository<Table001> _table001;

    public MyClass(IRepository<Table001> table001)
    {
       _table001 = table001;
    }
}

如果您发现自己将大量依赖项注入单个类,则可能是该类违反了 单一责任原则 。您可能希望将其分为不同的类。

  

&#34;创建一个对象实例是.NET Framework非常快速的事情。您的应用程序可能存在的任何性能瓶颈   将出现在其他地方。&#34; - Mark Seemann Dependency Injection in .Net的作者