【发布时间】:2018-09-06 16:29:46
【问题描述】:
我面临一个与依赖注入相关的小问题。我有一个程序,使用了最先进的依赖注入规则,从 ASP.NET MVC 项目的示例中使用,以及一些控制台程序。
但它也包含某种“服务定位器”反模式。
我将尝试用一个非常简单的控制台项目来说明它:
using System;
using Autofac;
using System.Collections.Generic;
using System.Linq;
namespace AutofacIssue
{
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<UserService>();
var users = service.CreateSomeUsers();
var info = users.First().GetSomeInfo;
Console.WriteLine(info.Something);
}
}
}
internal class MyModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<UserService>();
builder.RegisterType<UserRelatedInfoService>();
}
}
internal class UserRelatedInfoService
{
public UserInfo GetForUser(int id)
{
return new UserInfo("Various infos for " + id);
}
}
internal class UserService
{
public IEnumerable<User> CreateSomeUsers()
{
return Enumerable.Range(1, 10).Select(r => new User(r)); // Remark "new User()" is called from many various spaces !
}
}
internal class UserInfo
{
// Some things
public string Something;
public UserInfo(string someThings)
{
this.Something = someThings;
}
}
/// <summary>
/// "Service locator" antipattern
/// </summary>
class DependencyLocator
{
public static IContainer Container { get; }
static DependencyLocator()
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
Container = builder.Build();
}
}
internal class User
{
public User(int id)
{
this.Id = id;
}
public int Id { get; private set; }
/// <summary>
/// Here's my problematic property using the DependencyLocator
/// </summary>
public UserInfo GetSomeInfo
{
get
{
UserRelatedInfoService userRelatedInfoService = DependencyLocator.Container.Resolve<UserRelatedInfoService>();
return userRelatedInfoService.GetForUser(Id);
}
}
}
}
anti 模式允许编写非常小的代码运行良好,但违反了 DI 的一些原则(由于“服务定位器”+ 重复的 Container,每个都有自己的生命周期)。
这个实现还有一个优点是,仅在实际需要时实例化UserRelatedInfoService,如果实际调用了 User 的相关属性(请记住,现实世界的示例要复杂得多,并且一些与此相关的操作可能要收费)
在现实世界的示例中,我在许多程序集中都有这种情况,每个程序集都需要能够以相同的方式解决依赖关系。
我的问题是:不修改User 构造函数,以及实例化一些User 的所有对象的构造函数,有没有一种干净的方法来避免这种情况?
例如通过某种“动态解析”依赖关系?
请注意User 与我的Program 类不在同一个程序集中,因此我无法将原始Container 作为公共属性访问。
我认为的一个解决方案是保留 DependencyLocator 类,但删除其内容,并将其 Container 属性分配给 main 中创建的属性。
编辑:仅供参考,到目前为止,我只是按照自己的建议修改了 DependencyLocator 以避免它重建自己的容器,并在其上设置在应用程序入口点构建的最终容器。这是一个简单的更改,它避免了原始问题中指出的大多数问题。 至少,代码将始终使用同一个容器。
感谢阅读!
【问题讨论】:
-
为什么不能修改
User的构造函数?否则你能修改User类的代码吗?是否只是为了避免重写使用相同对象的其他代码,也许没有依赖注入? -
@ColinYoung :因为我可能有不止一个类“用户”有这种问题,而且因为“用户”是一个业务类,而不是服务。所以它可以有几个具有不同签名的构造函数,主要接受像原始类型这样的“数据”参数。
-
看看 Autofac 中的delegate factories。我认为这可能会让你想出一个更优雅的解决方案。
标签: c# .net dependency-injection autofac