我面临一个与依赖项注入有关的小问题。我有一个程序,使用了尊重性的最新依赖注入规则,该程序是从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);
}
}
}
}
使用反模式可以编写非常好的代码,但效果很好,但是违反了DI的某些原理(由于“服务定位器” +重复的Container,每个生命周期都有)。
该实现还有一个优点是,仅在实际需要时才实例化UserRelatedInfoService
,如果实际上调用了User的相关属性(请记住,真实世界中的示例要复杂得多,并且某些操作与可能会为此付出代价)
在现实世界的示例中,我在许多程序集中都有这种情况,每个程序集都需要能够以相同的方式解决依赖关系。
我的问题是:如果不修改User
构造函数,并且所有对象的构造函数实例化某个User
,是否有避免这种情况的干净方法?
例如通过某种“动态解析”依赖性?
请注意,User
与我的Program
类不在同一程序集中,因此我无法将原始Container
作为公共属性来访问。
我认为一种解决方案是保留类DependencyLocator
但删除其内容,而仅将其Container
属性分配给main中创建的属性。
edit:仅供参考,到目前为止,我只是遵循我自己的建议并修改了DependencyLocator以避免它重建自己的容器,并在其上设置在应用程序入口点构建的最终容器。这样做很容易,并且避免了原始问题中指出的大多数问题。 至少,代码将始终使用相同的容器。
感谢阅读!
答案 0 :(得分:0)
对于需要按类型进行运行时解析的此类极端情况,可以注册IServiceProvider或Func(或以object []作为输入参数的Func)
builder.Register(ctx => ctx as IServiceProvider ??
ctx.Resolve<ILifetimeScope>() as IServiceProvider)
.InstancePerLifetimeScope().AsSelf();
或
builder.Register(c =>
{
var scope = c.Resolve<ILifetimeScope>();
return (Func<Type, object>)(t => scope.Resolve(t));
}).As<Func<Type, object>>();