我们有一个WPF应用程序(.NET 4.0,由于Windows Server 2003和XP兼容性而无法更改),该应用程序使用BCL支持异步/等待。
对于DI和方面,我们使用Castle IoC,而要访问Oracle数据库,则使用NHibernate。我们的问题如下:
我已经实现了一个UoW模式,该模式有时必须接收一个ISessionFactory。由于我们使用async
/ await
,因此用户调用的第一个业务操作将首次加载ISessionFactory单例,这可能需要花费几秒钟的时间(由于映射(即使我将该信息存储在文件中)),并且async / await将在UI上执行,从而阻止应用程序。我要实现的目标是将ISessionFactory加载到单独的线程上,试图防止UI阻塞。由于这种首次加载可能在任何给定的时间在任何屏幕上发生,因此我认为IoC容器应该是放置此逻辑的理想场所,但是我无法防止UI阻塞。
这是正常的注册:
Component.For<ISessionFactory>().UsingFactoryMethod(
k =>
Fluently.Configure()
.Database(
() => OracleClientConfiguration.Oracle10.ConnectionString(
c => c.Is(k.Resolve<ISifarmaConnectionString>().Value))
#if DEBUG
.ShowSql().FormatSql()
#endif
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ISifarmaUnitOfWork>())
#if DEBUG
.ExposeConfiguration(c => c.SetInterceptor(new SqlStatementInterceptor()))
#endif
.BuildSessionFactory()).LifestyleSingleton(),
这是尝试过的方法(因为我返回的是具体类型,而不是我不能在这里等待的
Task<ISessionFactory>
,这显然用Task.WaitAll(task)
阻止了UI): Component.For<ISessionFactory>().UsingFactoryMethod(
k =>
{
var task =
Task.Factory.StartNew(
() =>
Fluently.Configure()
.Database(
() => OracleClientConfiguration.Oracle10.ConnectionString(
c => c.Is(k.Resolve<ISifarmaConnectionString>().Value))
#if DEBUG
.ShowSql().FormatSql()
#endif
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ISifarmaUnitOfWork>())
#if DEBUG
.ExposeConfiguration(c => c.SetInterceptor(new SqlStatementInterceptor()))
#endif
.BuildSessionFactory());
Task.WaitAll(task);
return task.Result;
}).LifestyleSingleton(),
有什么方法可以在不阻止UI的情况下执行此操作?
当应用程序启动时,我还尝试创建一个创建
container.Resolve<ISessionFactory>()
的任务,但是如果用户在登录屏幕上足够快,则该对象尚未加载。 最佳答案
您应该做的是为ISessionFactory
创建一个依赖于Lazy<ISessionFactory>
的代理类。该代理可以注入需要ISessionFactory
的任何人,并且您可以在初始化期间在后台线程中触发自己创建ISessionFactory
:
public class LazySessionFactoryProxy : ISessionFactory
{
private readonly Lazy<ISessionFactory> factory;
public LazySessionFactoryProxy(Lazy<ISessionFactory> factory) {
this.factory = factory;
}
public ISession OpenSession() {
return this.factory.Value.OpenSession();
}
}
可以注册如下:
var lazy = new Lazy<ISessionFactory>(() => ...);
container.Register(Component.For<ISessionFactory>()
.Instance(new LazySessionFactoryProxy(lazy)));
Task.Factory.StartNew(() => lazy.Value);
通过在合成根目录内创建代理类,您可以使应用程序的其余部分忽略会话工厂中的性能瓶颈。向每个使用者注入
Lazy<ISessionFactory>
或Task<ISessionFactory>
意味着您需要在整个应用程序中进行彻底更改(违反OCP),并且意味着您泄漏了实现细节(事实是特定会话工厂的创建成本很高)超出抽象(违反DIP)。您甚至可以创建一个
LazySessionFactoryProxy
,当某些代码调用OpenSession
时将显示一个等待屏幕,并在工厂初始化时自动将其关闭。有趣的是,这一切都是可能的,而无需在应用程序中更改一行代码。只需更改合成根的布线即可。请注意,DI容器不支持“异步解析”的问题,但是使用异步解析根本没有用。之所以构建您的object graphs should be really fast是因为应该以我展示的方式隔离injection constructors should be simple和较慢的部分,而不是用它污染应用程序。