我正在尝试找到一种方法来管理资源的生命周期,而不依赖于使用它的组件。以下示例显示了我的方法。它还没有像预期的那样工作,但它表明了我的意图。
class GlobalService
{
static int instanceCounter = 0;
public int instanceId = ++instanceCounter;
public void DoWhatever() { }
}
class Consumer
{
private readonly Func<Owned<GlobalService>> _globalServiceFactory;
public Consumer(Func<Owned<GlobalService>> globalServiceFactory)
{
_globalServiceFactory = globalServiceFactory;
}
public void UseService()
{
using (var service = _globalServiceFactory())
{
service.Value.DoWhatever();
Console.WriteLine("service.instanceId = {0}\n", service.Value.instanceId);
}
}
}
class Program
{
static ILifetimeScope rootScope;
static ILifetimeScope globalServiceScope;
static ILifetimeScope consumerScope;
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<GlobalService>().InstancePerMatchingLifetimeScope("globalServiceScope");
builder.RegisterType<Consumer>().InstancePerMatchingLifetimeScope("consumerScope");
var container = builder.Build();
this.rootScope = container.BeginLifetimeScope();
this.globalServiceScope = rootScope.BeginLifetimeScope("globalServiceScope");
this.consumerScope = globalServiceScope.BeginLifetimeScope("consumerScope");
var consumer = consumerScope.Resolve<Consumer>();
consumer.UseService(); // service.instanceId is 1
ResetServiceScope();
consumer.UseService(); // service.instanceId is 1, but I want it to be 2
}
static void ResetServiceScope()
{
globalServiceScope.Dispose();
globalServiceScope = rootScope.BeginLifetimeScope("globalServiceScope");
}
}
我希望实现Consumer
中的工厂始终返回当前活动的GlobalService
实例,其实例在其他地方被控制,以便Consumer
不需要知道任何有关如何GlobalService
已重新加载。
我不确定我是否真的误用了Autofac(或一般的DI)因为我刚开始使用它(和一般的DI)。无论我是否滥用它,我现在都会被困在这里,并且非常感谢有人指出我正确的方向。
答案 0 :(得分:0)
我很确定你是在滥用LifetimeScopes。由于consumerScope
是从globalServiceScope
创建的,因此当您处置globalServiceScope
时,您也会处置consumerScope
。从技术上讲,任何从这两个范围解决的服务都不应再被访问。例如,如果Consumer
已实施IDisposable
,则会在您处置globalServiceScope
时将其处理。
我不确定你到底想要做什么,所以我不能建议一个更好的方法。
编辑:所以亚历山大指出,当外部容器处理时,我错误的是内部容器被丢弃,但这是因为一个已知的错误,没有简单的修复,不是因为它是有效的行为。如果有人修复了该错误,则上述用法会中断。编辑2:我调整了你的代码以引入一个ReloadableSingleton
类,因为我认为这就是你想要实现的目标。我添加了一些Debug.Assert代码只是为了验证它是否有效。显然,你可以把它拿回来。如果你使用Func<>
接口,你可以消除Consumer中的IGlobalService
要求,并注册了一个将每个方法调用推迟到单例的实现,但我不想编写所有这些,以防万一偏离轨道。这有帮助吗?
using System;
using System.Diagnostics;
using Autofac;
using Autofac.Builder;
namespace Demo
{
internal class GlobalService
{
private static int instanceCounter = 0;
public int instanceId = ++instanceCounter;
public void DoWhatever() {}
}
internal class Consumer
{
private readonly Func<GlobalService> _globalServiceFactory;
public Consumer(Func<GlobalService> globalServiceFactory)
{
_globalServiceFactory = globalServiceFactory;
}
public int UseService()
{
var service = _globalServiceFactory();
service.DoWhatever();
Console.WriteLine("service.instanceId = {0}\n", service.instanceId);
return service.instanceId;
}
}
internal class Program
{
private const string ConsumerTag = "consumerScope";
private const string GlobalServiceTag = "globalServiceScope";
private static ILifetimeScope rootScope;
private static ILifetimeScope globalServiceScope;
private static ILifetimeScope consumerScope;
private static void Main()
{
var builder = new ContainerBuilder();
// root scope:
builder.RegisterReloadableSingleton<GlobalService>();
// global service scope:
builder.RegisterType<GlobalService>().InstancePerMatchingLifetimeScope(GlobalServiceTag);
builder.RegisterSingletonReloader<GlobalService>().InstancePerMatchingLifetimeScope(GlobalServiceTag);
// consumer scope:
builder.RegisterReloadableSingletonAccessor<GlobalService>().InstancePerMatchingLifetimeScope(ConsumerTag);
builder.RegisterType<Consumer>().InstancePerMatchingLifetimeScope(ConsumerTag);
using (var container = builder.Build())
using (rootScope = container.BeginLifetimeScope())
{
ReloadGlobalService();
consumerScope = rootScope.BeginLifetimeScope(ConsumerTag);
var consumer = consumerScope.Resolve<Consumer>();
Debug.Assert(consumer.UseService() == 1, "1");
ReloadGlobalService();
Debug.Assert(consumer.UseService() == 2, "2");
consumerScope.Dispose();
globalServiceScope.Dispose();
}
}
private static void ReloadGlobalService()
{
if (globalServiceScope != null)
globalServiceScope.Dispose();
globalServiceScope = rootScope.BeginLifetimeScope(GlobalServiceTag);
globalServiceScope.ReloadSingleon<GlobalService>();
}
}
internal class ReloadableSingleton<T>
{
public T Instance { get; set; }
}
internal static class ReloadableSingletonExtensions
{
public static void ReloadSingleon<T>(this IComponentContext scope)
{
scope.ResolveKeyed<Action>("reloader:" + typeof(T))();
}
public static IRegistrationBuilder<ReloadableSingleton<T>, SimpleActivatorData, SingleRegistrationStyle>
RegisterReloadableSingleton<T>(this ContainerBuilder builder)
{
return builder.RegisterInstance(new ReloadableSingleton<T>()).SingleInstance();
}
public static IRegistrationBuilder<Func<T>, SimpleActivatorData, SingleRegistrationStyle>
RegisterReloadableSingletonAccessor<T>(this ContainerBuilder builder)
{
return builder.Register<Func<T>>(c =>
{
var singleton = c.Resolve<ReloadableSingleton<T>>();
// It's important to register the func and not the instance directly. Otherwise you'd get the original
// instance every time, even after the Instance has been modified.
return () => singleton.Instance;
});
}
public static IRegistrationBuilder<Action, SimpleActivatorData, SingleRegistrationStyle>
RegisterSingletonReloader<T>(this ContainerBuilder builder)
{
return builder.Register<Action>(c =>
{
var context = c.Resolve<IComponentContext>();
return () =>
{
var singleton = context.Resolve<ReloadableSingleton<T>>();
var newInstance = context.Resolve<T>();
singleton.Instance = newInstance;
};
})
.Keyed<Action>("reloader:" + typeof (T));
}
}
}
答案 1 :(得分:0)
编辑:在ResetGlobalServiceInstance()
中,删除了builder.Update(serviceContainer);
并添加了由于内存泄漏而导致和创建新容器实例(Update()
不会破坏之前的GlobalService
实例)。
我只是找到了一个符合我需求的解决方案,并且不需要添加额外的管理类。所需要的只是两个单独的IContainer
而不是一个。这是结果代码:
class GlobalService
{
private static int instanceCounter = 0;
public int instanceId = ++instanceCounter;
}
class Consumer
{
private readonly Func<GlobalService> _globalServiceFactory;
public Consumer(Func<GlobalService> globalServiceFactory)
{
_globalServiceFactory = globalServiceFactory;
}
public int UseService()
{
var service = _globalServiceFactory();
return service.instanceId;
}
}
class Program
{
static IContainer consumerContainer;
static IContainer serviceContainer;
static void Main(string[] args)
{
var serviceBuilder = new ContainerBuilder();
serviceBuilder.RegisterType<GlobalService>().SingleInstance();
serviceContainer = serviceBuilder.Build();
var consumerBuilder = new ContainerBuilder();
consumerBuilder.RegisterType<Consumer>();
consumerBuilder.Register(c => serviceContainer.Resolve<GlobalService>());
consumerContainer = consumerBuilder.Build();
var consumer = consumerContainer.Resolve<Consumer>();
int serviceId = consumer.UseService();
Debug.Assert(serviceId == 1, "Call 1, instance 1");
serviceId = consumer.UseService();
Debug.Assert(serviceId == 1, "Call 2, still instance 1");
ResetGlobalServiceInstance();
serviceId = consumer.UseService();
Debug.Assert(serviceId == 2, "Call 3, now instance 2");
serviceId = consumer.UseService();
Debug.Assert(serviceId == 2, "Call 4, still instance 2");
}
public static void ResetGlobalServiceInstance()
{
var builder = new ContainerBuilder();
builder.RegisterType<GlobalService>().SingleInstance();
builder.Dispose();
serviceContainer = builder.Build();
}
}
虽然这解决了我现在的问题,但我很欣赏有关潜在问题的反馈,例如在一个应用程序中使用多个IContainer
或者通常在此处进行重大的DI / Autofac滥用。