【问题标题】:Use of async method on Windows Universal 8.1在 Windows Universal 8.1 上使用异步方法
【发布时间】:2015-12-17 04:20:38
【问题描述】:

我有一个页面(主页)和 SQLite 数据库。我需要先使用 SQLite 数据库,然后更新我的页面控件。
这是我的页面代码:

public MainPage()
    {           
        this.InitializeComponent();

        // Get SQLite and language setting
        SQLiteData(); 

        // Update controls in page
        GetConnectionList(); 
    }

这是 SQLiteData() 的代码:

public async void SQLiteData()
    {
        conn = new SQLiteAsyncConnection("Setting.db");
        await conn.CreateTableAsync<SettingTable>();
        await conn.CreateTableAsync<DataPlanTable>();

        ....
     }

因为我在 SQLiteData() 中使用了 'await',所以调试器在未完成的情况下运行 GetConnectionList() 并从 SQLite 获取数据,所以我的应用程序出现错误。
我该如何解决?可以帮到我吗?
谢谢。

【问题讨论】:

  • 你真的应该研究一下设计模式。在这种情况下,MVVM 以及服务和存储库模式。

标签: c# windows-phone-8.1 windows-8.1


【解决方案1】:

关键是您将 SQLiteData 声明为异步,但在 MainPage 构造函数中您没有“等待” SQLiteData 任务完成。

您应该等待 SQLiteData 完成,但是您不能在构造函数内部轻松地做到这一点......而且您也不应该这样做。 Can constructors be async?

请将耗时和异步任务从构造函数移到类中的其他方法中,一个关键原因是您可以实现一些更好的错误处理例程,并且如果连接失败,您可以重新执行连接逻辑第一次。

public async InitialiseDataConnections()
{           
    // Get SQLite and language setting
    await SQLiteData(); 

    // Update controls in page
    GetConnectionList(); 
}    

现在您有了一个可以从构造函数调用的单一用途方法...但更好的解决方案是从页面类的 OnNavigatedTo 事件处理程序中调用它。

与对对象执行操作相比,创建对象实例应该相对轻量级,这是编程中的普遍期望。这是一个有趣的讨论:How much work should be done in a constructor

在通用应用程序中,预计轻量级配置在构造函数中处理,并且大部分初始化“逻辑”会延迟到页面实际导航到 (OnNavigatedTo)。尤其是当我们考虑数据访问场景时,数据尽可能保持最新可能很重要,但还有另一个重要事件,当导航到页面时导致向后导航或在应用程序恢复后。在这些事件中,您可能能够从之前的序列化状态中获取一些状态或配置,而不是重新初始化所有可能是一个耗时的过程。

所以这里的“最佳实践”是使用 OnNavigatedTo 来处理任何页面初始化,而不是创建页面控件。您可以访问 NavigationEventArgs,这将帮助您以用户期望的状态重新创建页面。这是其他开发人员首先查看页面背后的业务逻辑的地方,并且您可以将 OnNavigatedTo 处理程序声明为异步!

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    await InitialiseDataConnections();
}

【讨论】:

    【解决方案2】:

    一种方法是将异步初始化移出 OnNavigatedTo 中的 ctor。但是,您必须意识到 OnNavigatedTo 将在其异步部分完成之前将控制权返回给框架。这可能没问题,但您需要考虑如何处理错误。

    不过,我会对这段代码的结构有所不同:

    ...
    
    // The UI will use data binding to PageState to show 
    // UI specific for the initialization phase.
    public PageState PageState {get; private set {.../*include property change notification */...};}
    
    // The UI will use data binding to ErrorMessage as needed.
    public string ErrorMessage {get; private set {.../*include property change notification */...};}
    
    ...
    
    protected async override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
    
        await InitializeAsync();
    }
    
    private async Task InitializeAsync()
    {
        try
        {
            // Have the UI bind to 
            PageState = PageState.InitializingInProgress;
    
            await SQLiteData(); 
    
            GetConnectionList();
    
            ...
    
            PageState = PageState.InitializedOk;
        }
        catch(...)
        {
            PageState = PageState.InitializedWithError;
            ErrorMessage = ...
        }
    }
    

    }

    【讨论】:

    • 代码对我来说很难。我读了但我不明白为什么我会出错。当我得到错误?重要吗?
    • 我指的是运行 SQLite 代码时可能出现的潜在错误。如果代码太难,那么从这个开始 - 只需在 OnNavigatedTo 中移动初始化的异步部分。还要小心 - 第二次运行应用程序时,您可能会失败,因为您正在尝试创建已经存在的表。您可能需要添加更多代码以避免在第二次运行时创建相同的表。
    • 复制此代码时,PageState 出现错误?直到现在我才使用 pageState。
    • 暂时忽略 try/catch、PageState 和 ErrorMessage。您甚至可以删除 InitializeAsync 并将对它的调用替换为我称之为“await SQLiteData();”的两行。和“GetConnectionList();”。您的主要问题现在已经解决,因为您现在可以使用 await 调用 SQLiteData。
    • 在 GetSqlite 我检查重复表。我将我的主页设为 emuty,并在 onnavigateto 中使用我的代码。它工作得很好,但我有问题。我更改了我的应用程序语言,因此我需要在初始化之前加载 GetSQLite 以检查选择的语言并进行更改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多