在开始提问之前,以下是我的申请背景:
考虑我有一个只作为数据库表的查找类的类。我的数据库包含大约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种查找方法中的三种方法。但是在Lookup
(MyClass
字段)中实例化_lookup
类时,所有350个存储库也被解析,实例化350个存储库。现在我关心的是性能/效率。以下哪两个选项在性能方面更好,为什么?
仅在实际使用存储库的方法中解析存储库,而不是在构造函数中自动注入。例如,GetTable001()
将变为此(字段_table001Repository
将从Lookup
类中删除,因此其构造函数中的参数table001Repository
也是如此:
public Table001 GetTable001(/* fields for looking up Table001 */)
{
IRepository<Table001> table001Repository = container.Resolve<IRepository<Table001>>();
// lookup logic
}
在选项1中,在解析_lookup
时实例化所有350个存储库,例如,类MyClass
仅使用三种查找方法。第二种方法是否优于第一种方法?或者,第一个选项实际上是否优于第二个表现?此外,解决方案的范围(InstancePerLifetimeScope
,InstancePerDependency
等)是否会有所不同?
感谢。
PS:如果您认为最好在SoftwareEngineering发布此问题,请发表评论。在这里提出这个问题的原因是SO所拥有的广泛受众。
答案 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的作者