我们有一些遗留应用程序假设我们无法更改该SiteSettings类,因为完整的项目编码数千行会打扰。所以我们想用DI解决问题。我在此处创建了POC应用,您可以在global asax
中看到有评论//HOW CAN I PASS TenantId HERE so it will be same for this complete httprequest life.
遗留代码:
public class OrderController
{
public static string CompleteOrder()
{
return SiteSettings.Instance.DefaultTimeZone();
}
}
public class SiteSettings
{
public ITenantSettings TenantSettings { get; set; }
private static SiteSettings _instance;
private SiteSettings() { }
public static SiteSettings Instance => _instance ?? (_instance = new SiteSettings());
public string DefaultTimeZone()
{
return TenantSettings.DefaultTimeZone();
}
}
新的注射类
public interface ITenantSettings
{
string DefaultTimeZone();
}
public class TenantSettings : ITenantSettings
{
private readonly int _tenantId;
public TenantSettings(int tenantId)
{
_tenantId = tenantId;
}
public string DefaultTimeZone()
{
return "USA Time For Tenant ID " + _tenantId.ToString();
}
}
全球ASAX
public class Global : HttpApplication, IContainerProviderAccessor
{
// Provider that holds the application container.
static IContainerProvider _containerProvider;
// Instance property that will be used by Autofac HttpModules
// to resolve and inject dependencies.
public IContainerProvider ContainerProvider => _containerProvider;
protected void Application_Start(object sender, EventArgs e)
{
// Build up your application container and register your dependencies.
var builder = new ContainerBuilder();
builder.RegisterType<TenantSettings>().As<ITenantSettings>().InstancePerRequest();
_containerProvider = new ContainerProvider(builder.Build());
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
int id = 0;
int.TryParse(HttpContext.Current.Request.QueryString["id"], out id);
var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
var cp = cpa.ContainerProvider;
cp.RequestLifetime.InjectProperties(SiteSettings.Instance);
//HOW CAN I PASS TENANTID HERE so it will be same for this complete httprequest life.
}
}
默认ASPX
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(OrderController.CompleteOrder());
}
}
错误:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'CoreLibrary.Tenants.TenantSettings' can be invoked with the available services and parameters:
Cannot resolve parameter 'Int32 tenantId' of constructor 'Void .ctor(Int32)'.
答案 0 :(得分:4)
您可以使用WithParameter
,在这种情况下,我会建议ResolvedParameter
:
builder.RegisterType<TenantSettings>()
.As<ITenantSettings>()
.InstancePerRequest()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(int) && pi.Name == "tenantId",
(pi, ctx) => int.Parse(HttpContext.Current.Request.QueryString["id"])));
实际上你需要比int.Parse(HttpContext.Current.Request.QueryString["id"])
更有弹性的东西,但这会给你一种解决方案的味道
如果我们要注入依赖项,我们需要删除行_instance ?? (_instance = new SiteSettings())
。在我的示例SiteSettings
中,现在有一个static Initialise
方法,此方法用于构造 SiteSettings.Instance
的值。
目前我们只对注入ITenantSettings
感兴趣,因为我们希望ITenantSettings
的生命范围(每个请求)小于SiteSettings
(单一)的范围,我们应该注入代表(Func<ITenantSettings>
)。
public class SiteSettings
{
private static SiteSettings _instance;
private Func<ITenantSettings> _tenantSettingsFactory;
private SiteSettings(Func<ITenantSettings> tenantSettingsFactory)
{
_tenantSettingsFactory = tenantSettingsFactory;
}
public static void Initialise(Func<ITenantSettings> tenantSettingsFactory)
{
_instance = new SiteSettings(tenantSettingsFactory);
}
public ITenantSettings TenantSettings { get { return _tenantSettingsFactory(); } }
public static SiteSettings Instance
{
get {
if (_instance == null) throw new InvalidOperationException();
return _instance;
}
}
public string DefaultTimeZone()
{
return TenantSettings.DefaultTimeZone();
}
}
这是一个测试,展示了你的要求:
[Fact]
public void Demonstrate_TenantSettingsFactory_AlwaysResolvesCurrentTenantId()
{
int tenantId = 0;
var builder = new ContainerBuilder();
builder.RegisterType<TenantSettings>()
.As<ITenantSettings>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(int) && pi.Name == "tenantId",
(pi, ctx) => tenantId));
var container = builder.Build();
SiteSettings.Initialise(container.Resolve<ITenantSettings>);
tenantId = 1;
Assert.Equal("USA Time For Tenant ID 1", SiteSettings.Instance.DefaultTimeZone());
tenantId = 2;
Assert.Equal("USA Time For Tenant ID 2", SiteSettings.Instance.DefaultTimeZone());
}
注意我在使用单元测试项目时删除了InstancePerRequest
和HttpContext.Current
。