How to call async method from MainWindow?

有些话、适合烂在心里 提交于 2020-05-27 13:08:18

问题


So I wrote a quick async method to get some data into a DataTable from an Oracle db. How am I suppose to call this from MainWindow() without blocking UI thread? The async/wait model doesn't really make much sense there.

    async Task<DataTable> AccessOracleAsync()
    {
        DataTable dt;
        using(OracleConnection conn = new OracleConnection(ConfigurationManager.ConnectionStrings["connStr"].ConnectionString))
        using (OracleCommand cmd = new OracleCommand(@"SELECT * FROM myTbl", conn))
        {
            await conn.OpenAsync();
            using (var reader = await cmd.ExecuteReaderAsync())
            {
                dt = new DataTable();
                dt.Load(reader);                        
            }

        }
        return dt;
    }

回答1:


Without seeing your constructor, it's hard to say what might "make sense" there. But you have at least a couple of options here.

Option #1: put the call in the constructor.

In this case, you can't make the method async, and you most certainly don't want your continuation to do anything that needs to be done before the constructor returns. But you can still do effectively what C# would have done for you in an async method:

public MainWindow()
{
    InitializeComponent();

    AccessOracleAsync().ContinueWith(task => { /* do some other stuff */ },
        TaskScheduler.FromCurrentSynchronizationContext());
}

That will execute the continuation on the UI thread, as if you had written await AccessOracleAsync(); /* do some other stuff */. Add exception handling in the continuation method as necessary (i.e. inspect the task object and handle appropriately).

Option #2: put the call somewhere else.

It has been suggested to use the Loaded event, which you can do. That might look something like this:

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    await AccessOracleAsync();

    // do some other stuff
}

Note that if the latter seems appropriate, IMHO it's preferable. It lets the C# compiler do the heavy-lifting for you, including dealing with exceptions (you can just wrap the call in try/catch and everything "just works").




回答2:


Try creating a handler for the Laoded event, marking it as async and call your AccessOracleAsync method from there.




回答3:


I've just done this and boy was it a struggle! you can't just call your method as suggested in the other answer as the calling method or constructor will end and the task will be lost.

I did two things

1: bind the window loaded event rather than doing it in the constructor

2: you MUST call Task.Run if you want to run in a background process, regardless of whether your method is async or not

eg

    public async void Window_Loaded(object sender, RoutedEventArgs e)
    {
        Task.Run(()=>this.AccessOracleAsync().ContinueWith(function to deal with results));
    }

furthermore, if you need to get back to the foreground thread to update the UI with your results you will want to enable your collection for thread safety

    public MainVM()
    {
        this.results= new ObservableCollection<resultType>();
        BindingOperations.EnableCollectionSynchronization(results, _itemsLock);
    }
    private static object _itemsLock = new object();
    public ObservableCollection<resultType> results{ get; set; }


来源:https://stackoverflow.com/questions/29014906/how-to-call-async-method-from-mainwindow

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!