Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从IoC容器加载重对象会阻塞UI_C#_Wpf_Nhibernate_Async Await_Castle Windsor - Fatal编程技术网

C# 从IoC容器加载重对象会阻塞UI

C# 从IoC容器加载重对象会阻塞UI,c#,wpf,nhibernate,async-await,castle-windsor,C#,Wpf,Nhibernate,Async Await,Castle Windsor,我们有一个WPF应用程序(.NET 4.0,由于Windows Server 2003和XP的兼容性而无法更改)使用BCL支持异步/等待。 对于DI和方面,我们使用Castle IoC,为了访问Oracle数据库,我们使用NHibernate。我们的问题如下: 我已经实现了一个UoW模式,在某种程度上,它必须接收ISessionFactory。由于我们使用async/await,用户调用的第一个业务操作将第一次加载ISessionFactory单例,这可能需要几秒钟的时间,因为映射(即使我将该信

我们有一个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(),
有没有办法在不阻塞用户界面的情况下做到这一点?
当应用程序启动时,我还尝试创建一个任务,使其成为container.Resolve(),但如果用户在登录屏幕上速度足够快,则该对象尚未加载。

您只需存储任务而不是该任务的结果(即
任务
而不是
ISessionFactory
)并在需要结果时等待该任务:

Component.For<Task<ISessionFactory>>().UsingFactoryMethod(
    k => 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());
    }).LifestyleSingleton(),

您应该做的是为依赖于
惰性
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();
    }
}
公共类LazySessionFactoryProxy:ISessionFactory
{
私人只读工厂;
公共LazySessionFactoryProxy(惰性工厂){
这个工厂=工厂;
}
公共会话OpenSession(){
返回这个.factory.Value.OpenSession();
}
}
可按以下方式进行注册:

var lazy = new Lazy<ISessionFactory>(() => ...);

container.Register(Component.For<ISessionFactory>() 
    .Instance(new LazySessionFactoryProxy(lazy)));

Task.Factory.StartNew(() => lazy.Value);
var lazy=newlazy(()=>…);
container.Register(Component.For())
.Instance(新LazySessionFactoryProxy(lazy));
Task.Factory.StartNew(()=>lazy.Value);
通过在组合根目录中创建代理类,您可以让应用程序的其余部分忽略会话工厂中的性能瓶颈。将
惰性
任务
注入每个使用者意味着您需要在整个应用程序中进行彻底的更改(违反),并意味着您正在从抽象(违反)中泄漏实现细节(创建特定会话工厂的成本很高)

您甚至可以创建一个
LazySessionFactoryProxy
,当一些代码调用
OpenSession
时,它将显示一个等待屏幕,并在工厂初始化时自动关闭它。有趣的是,这一切都是可能的,而无需更改应用程序中的一行代码;只需更改作文根的连接即可


请注意,DI容器不支持“异步解析”的问题,但拥有异步解析根本没有用处。构建您的“因为”和“慢”部分应该按照我展示的方式隔离,而不是用它污染应用程序

您仍然在注册方法中等待任务完成,因此不会获得任何收益。代码仍然是同步的是的,我知道。这就是我所说的“它显然用Task.WaitAll(Task)阻塞了用户界面”。为什么必须返回
ISessionFactory
,而不是它的一个任务?你需要的是IoCs目前不支持的异步解析(我想)。一种解决方法是注册一个Lazy并强制它在一次性任务中初始化eg。这样,只有当工厂wan在第一个表单请求时还没有初始化时,您才会阻止它。为什么不将整个
组件移动到一个新线程?好的,我做了一些测试,可以多次等待相同的任务。我会修改代码,但这肯定是解决方案。我的印象是,即使成功等待多次,也会抛出异常(我相信当代码试图获取等待者时,这是一个空引用异常),但当时我正在拦截方法调用并更改任务,因此它可能与我的代码有关。让我做一些测试,但我确信这解决了我的问题,并将其标记为解决方案。@JoãoSimões sure。。如果您有问题,请告诉我。我尝试了此解决方案,但需要异步获取会话。我的UoW有一个关联的会话(或者如果EF是一个上下文),我真的不希望我的存储库必须进行“return(wait GetSession())。Where(p=>p.Name.Contains(nameFilter))@JoãoSimões您可以使用
GetSession(),但是需要逻辑来知道对象是否已经加载。我不介意我们的代码知道会话是一个沉重的对象,应该异步加载,但是每次需要时都需要使用异步,这会让我一直感到困扰。我将使用@Steve suggestion,特别是因为它对代码的影响最小,但这仍然是一个非常有效的解决方案,谢谢!Castle IoC已延迟加载该对象。这是一个好的解决方案,它不能解决初始“Task.Factory.StartNew(()=>lazy.Value);”的问题在我需要“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);