如何使用Xamarin Forms中的Prism / DryIoC处理和重新实例化单例?
我正在使用Azure移动应用程序获取离线数据。偶尔,我需要删除本地sqlite数据库并重新初始化它。不幸的是,MobileServiceClient偶尔会使数据库连接保持打开状态,并且没有任何方法可以将其关闭。建议的解决方案(https://github.com/Azure/azure-mobile-apps-net-client/issues/379)是处置MobileServiceClient。唯一的问题是在DryIoC中注册为单身人士。
我对DryIoC或Prism and Forms并不过分熟悉......但对于我的生活,我无法找到实现这一目标的方法。
我做了一个非常复杂的计划,几乎可以工作。
在我的ViewModel方法中,当我需要释放数据库时,我解雇了一个事件 -
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Publish(false);
然后在App.xaml.cs中,我连接了一个监听器和一个像这样的处理程序 -
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Subscribe(OnRegisterDatabaseEventPublished);
private void OnRegisterDatabaseEventPublished()
{
Container.GetContainer().Unregister<IAppMobileClient>();
Container.GetContainer().Unregister<IMobileServiceClient>();
Container.GetContainer().Register<IMobileServiceClient, AppMobileClient>(new SingletonReuse());
Container.GetContainer().Register<IAppMobileClient, AppMobileClient>(new SingletonReuse());
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Publish(register);
}
最后,回到ViewModel构造函数中,我有一个最终的侦听器,它处理从App.xaml返回的事件并完成处理。
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Subscribe(OnRegisterDatabaseCompletedEventPublished);
令人惊奇的是,这很有效。数据库能够被删除,一切都很好。但后来我导航到另一个页面和BOOM。 DryIoC表示它无法为该页面连接ViewModel。我假设取消注册/注册为所有注入提升了DryIoC ......那么我该如何完成需要完成的工作呢?
最终解决方案
非常感谢dadhi花时间提供帮助。你当然是一个集体行为,我现在正在考虑在其他地方使用DryIoC。
对于任何偶然发现此事的人,我都会在下面发布最终解决方案。我会尽可能地避免任何混淆。
首先,在我的App.xaml.cs中,我添加了一个注册数据库的方法。
public void RegisterDatabase(IContainer container)
{
container.RegisterMany<AppMobileClient>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true),
ifAlreadyRegistered: IfAlreadyRegistered.Replace,
serviceTypeCondition: type =>
type == typeof(IMobileServiceClient) || type == typeof(IAppMobileClient));
}
我只是在RegisterTypes中添加对该方法的调用,而不是直接在那里注册类型。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.GetContainer().Rules.WithoutEagerCachingSingletonForFasterAccess();
...
RegisterDatabase(containerRegistry.GetContainer());
...
}
另请注意,每个爸爸都需要为热切缓存添加规则。
稍后我需要在ViewModel中释放数据库...我通过重置本地db变量并向App.xaml.cs发送事件来解决问题
_client = null;
_eventAggregator.GetEvent<RegisterDatabaseEvent>().Publish(true);
在App.xaml.cs中,我订阅了该事件并将其绑定到以下方法。
private void OnRegisterDatabaseEventPublished()
{
RegisterDatabase(Container.GetContainer());
_eventAggregator.GetEvent<RegisterDatabaseCompletedEvent>().Publish(register);
}
这里我再次调用RegisterMany,与应用程序启动时完全相同。无需取消注册任何内容。使用setup和ifAlreadyRegistered参数(谢谢,爸爸!),DryIoC允许替换对象。然后我将一个事件提交回VM,让它知道数据库已经被释放。
最后,回到ViewModel,我正在收听已完成的事件。该事件的处理程序更新对象的本地副本。
_client = ((PrismApplication)App.Current).Container.Resolve<IAppMobileClient>();
然后我可以根据需要使用新对象。这是关键。如果没有将_client设置为null并在此处再次解析它,我实际上最终得到了2个对象副本,并且对方法的调用被击中了2x。
希望能帮助其他人寻求发布他们的Azure移动应用数据库!
答案 0 :(得分:2)
我不确定XF究竟是如何处理这些事情的。
但是在DryIoc中为了完全删除或替换服务,需要在setup: Setup.With(asResolutionCall: true)
注册。请阅读此处了解更多详情:https://bitbucket.org/dadhi/dryioc/wiki/UnregisterAndResolutionCache#markdown-header-unregister-and-resolution-cache
以下两个选项和注意事项适用于纯DryIoc ,可能不适用于XF。但它可能有助于解决方案。
public class Foo
{
public IBar Bar { get; private set; }
public Foo(IBar bar) { Bar = bar; }
}
public interface IBar {}
public class Bar : IBar {}
public class Bar2 : IBar { }
[Test]
public void Replace_singleton_dependency_with_asResolutionCall()
{
var c = new Container(rules => rules.WithoutEagerCachingSingletonForFasterAccess());
c.Register<Foo>();
//c.Register<Foo>(Reuse.Singleton); // !!! If the consumer of replaced dependency is singleton, it won't work
// cause the consumer singleton should be replaced too
c.Register<IBar, Bar>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true)); // required
var foo = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar>(foo.Bar);
c.Register<IBar, Bar2>(Reuse.Singleton,
setup: Setup.With(asResolutionCall: true), // required
ifAlreadyRegistered: IfAlreadyRegistered.Replace); // required
var foo2 = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar2>(foo2.Bar);
}
[Test]
public void Replace_singleton_dependency_with_UseInstance()
{
var c = new Container();
c.Register<Foo>();
//c.Register<Foo>(Reuse.Singleton); // !!! If the consumer of replaced dependency is singleton, it won't work
// cause the consumer singleton should be replaced too
c.UseInstance<IBar>(new Bar());
var foo = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar>(foo.Bar);
c.UseInstance<IBar>(new Bar2());
var foo2 = c.Resolve<Foo>();
Assert.IsInstanceOf<Bar2>(foo2.Bar);
}