[英]Resolving Autofac dependencies from inside of classes, without parameterized constructors
我面臨一個與依賴注入有關的小問題。 我有一個程序,該程序使用了有關依賴項注入的最新技術規則(從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,每個都有自己的生命周期)。
如果實際上調用了User的相關屬性,則該實現還具有僅在實際需要時才實例化UserRelatedInfoService
的優點(請注意,真實世界中的示例要復雜得多,與此相關的某些操作可能會產生成本)
在實際示例中,我在許多程序集中都有這種情況,每個程序集都需要能夠以相同的方式解決依賴關系。
我的問題是:無需修改User
構造函數,而實例化某個User
的所有對象的構造函數,是否有避免這種情況的干凈方法?
例如通過某種“動態解析”依賴性?
請注意, User
與我的Program
類不在同一程序集中,因此我不能將原始Container
作為公共屬性來訪問。
我認為一種解決方案是保留DependencyLocator
類,但刪除其內容,並僅將其Container
屬性分配給main中創建的Container
屬性。
編輯:僅供參考,到目前為止,我只是遵循我自己的建議並修改了DependencyLocator以避免它重建其自己的容器,並在其上設置在應用程序入口點構建的最終容器。 這樣做很容易,並且避免了原始問題中指出的大多數問題。 至少,代碼將始終使用相同的容器。
謝謝閱讀!
對於需要按類型進行運行時解析的此類極端情況,可以注冊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>>();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.