简体   繁体   中英

Dynamically search with interrupts,via Tasks C#

I'm working with Db(via SQLite.NET PCL ,not async version). At current moment i have a listview with some data(taken from db),also i got a searchbar/entry(its nvm),where user can input some values and then,via LINQ i'm going to make a query and update SourceItems of my List.

So the problems comes with performance,because my DB got million records and simple LINQ query working very slow.In other words,when user enter too fast some data,application gets huge lags and sometimes can Crash .
To resolve this problem,some things come in my mind(theoretically solution):

1)Need to put method(which i make query into db) on Task(to unlock my main UI thread)

2)Initialize timer,then turn on and :

  • if 1 second passed ,then => run my method(query) on Task(similiar to background thread)
  • if 1 second doesn't passed ,then exit from anonymous method.

Something like that(similar) or any suggestions.Thanks!

UPD:
So to be honest,i tried too much and didnt get a good result
BTW ,my current code(Snippets) :
1) My SearchMethod

public void QueryToDB(string filter)
        {
            this.BeginRefresh ();

            if (string.IsNullOrWhiteSpace (filter))
            {
                this.ItemsSource = SourceData.Select(x => x.name); // Source data is my default List of items
            }
            else 
            {
                var t = App.DB_Instance.FilterWords<Words>(filter); //FilterWords it's a method,where i make direct requests to the database 
                this.ItemsSource = t.Select(x => x.name); 
            }
            this.EndRefresh ();
        }

2)Searchbar.TextChanged (anonymous method)

searchBar.TextChanged +=async (sender, e) => 
                {
                    ViewModel.isBusy = true;  //also i got a indicator,to show progress,while query working
                    await Task.Run(()=> //my background,works fine
                    {
                        listview.QueryToDB(searchBar.Text);
                    });
                    ViewModel.isBusy = false; // after method is finished,indicator turn off

                };

The main problem is how to implement this part(with these case's),where 1 second passed and only then i'm going to make query to update my sourceItems of list(everytime,when user inputs some value into searchbar,this trigger(timer) must refresh again to zero).

Any help will be appreciated,thanks!
PS Sorry for my eng. skills!

One way to do it is to combine async Task.Run and CancellationTokenSource :

CancellationTokenSource cancellationTokenSource;

searchView.TextChanged += async (sender, e) => 
{
    if (cancellationTokenSource != null) cancellationTokenSource.Cancel();
    cancellationTokenSource = new CancellationTokenSource();
    var cancellationToken = cancellationTokenSource.Token;

    var searchBar = (sender as SearchBar);
    if (searchBar != null)
    {
        string searchText = searchBar.Text;
        try
        {
            await Task.Delay(650, cancellationToken);

            if (cancellationToken.IsCancellationRequested) return;

            var searchResults = await Task.Run(() => 
            {
                return ViewModel.Search(searchText);
            });

            if (cancellationToken.IsCancellationRequested) return;

            ViewModel.YouItems.Repopulate(searchResults);
        }
        catch (OperationCanceledException)
        {
            // Expected
        }
        catch (Exception ex)
        {
            Logger.Error(ex);
        }
    }
};

You want to wait before actually performing your search. Killing a search task in midway can cause undefined behavior.

You want to save the current search filter and compared it again 1 second later. If that hasn't changed, do the search. Otherwise, abort :

searchBar.TextChanged += async (sender, e) => 
{
    var filter = searchBar.Text;
    await Task.Run(() =>
    {
        Thread.Sleep(1000);
        if (filter == searchBar.Text)
            listview.QueryToDB(searchBar.Text);
    });
};

As to keep the view model updated, move your isBusy assignments inside QueryToDB because that is when your view model is truly busy :

public void QueryToDB(string filter)
{
    this.BeginRefresh ();
    ViewModel.isBusy = true;

    // do your search

    ViewModel.isBusy = false;
    this.EndRefresh ();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM