Results 1 to 5 of 5

Thread: Async in ViewModel - best practices

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2016
    Location
    Slovenia
    Posts
    575

    Async in ViewModel - best practices

    Hello everyone,

    I'm using View-model first approach. In project that I'm currently working on I have a lot of cases where I need to fetch data from DB. Data is being used for binding controls (DataGrid, Textbox's Text etc...).

    Mostly I need that to be done immediately when view is displayed - like open a view with filled DataGrid or showing notifications with Text data.

    To do It smoothly in UI I had to create some sort of asynchronous solutions. Because I like things short and simple, I did this:

    Example of how my code works (not including .xaml, there might be some typo mistakes):

    Code:
    public class MyModel //Model class
    {
        public string Name { get; set; }
        public string SurName  { get; set; }
    }
    
    public class MainWindowViewModel : BaseViewModel // Viewmodel, class from which I call data (BaseViewmodel class just inherits INotifyProperty interface)
    {
       private bool loading_animation; //Boolean for showing loading animation
       private readonly MyProcedures _procedures 
    
       public MainWindowViewModel()
       {
           _procedures= new MyProcedures();
    
            Task.Run(()=> Load_data());
       }
        
       //Property which holds data for binding to UI controls   
       private ObservableCollection<MyModel> _persons;
       public ObservableCollection<MyModel> Persons
       {
           get { return _persons; }
           set { _persons = value; OnPropertyChanged(); }
       }
         
       private void Load_data()
       {
          loading_animation=true; //start animation in UI
      
          Person = _procedures.GetAllEmployees(12).Result; //Get all employees from department 12
    
          if(Person==null)
          {
              loading_animation=false; 
              return;
          }
          else
          {
             loading_animation=false;
          }
       }
    }
    
    public class MyProcedures //Class, which holds DB logic
    {
                  
       public Task<ObservableCollection<MyModel>> GetAllEmployees(int department_id)
       {
           var employees_list = new ObservableCollection<MyModel>();
    
           try
           {
              using (OracleConnection conn = new OracleConnection(conn_string))
              {
                 conn.Open();
    
                 using (OracleCommand cmd = new OracleCommand("MySchema.MyProcedure",conn))
                 {
                     cmd.CommandType = CommandType.StoredProcedure;
                     cmd.Parameters.Add("department_in", OracleDbType.Decimal).Value = department_id;
                     cmd.Parameters.Add("result", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
                              
                     using (OracleReader rdr = new OracleReader(cmd.ExecuteReader()))
                     {
                        while (await rdr.ReadAsync())
                        {
                           employees_list.Add(new MyModel()
                           {
                               Name= rdr[0] as string ?? null,
                               Surname= rdr[1] as string ?? null
                            }
                        }    
                     }
                 }       
            }
            return employees_list;  
          }
          catch Exception ex
          {
            MessageBox.Show(ex.Message);
                
             return null;  
          }
    }
    In short – I fill ObservableCollection of type T in async/await Task in my ViewModel constructor with Task.Run. That results in a smooth UI display with loading animation, and when data is obtained from DB, loading animation stops and my UI controls get's updated with binding.

    If anything goes wrong when querying DB I get error back, and whole code stops.

    So, generally speaking, is this approach good ? Or is there a better way of doing It – considering deadlocks,memory leaks or any kind of hidden errors that I'm not aware of?
    Last edited by LuckyLuke82; Sep 12th, 2020 at 06:52 AM.

  2. #2
    Member
    Join Date
    Jul 2019
    Location
    Ahmedabad
    Posts
    57

    Re: Async in ViewModel - best practices

    Hello, @Luckey
    Please follow this information, To Async in ViewModel - best practices

    You should only use Task.Run if you have CPU-bound or blocking work that you want to move off the UI thread. That's not the case here, so the direct call is the most natural.

    Taking them in turn:

    Code:
    await Task.Run(async () => { await ViewMode.loadData(); });
    This option will execute loadData on a thread pool thread. This may not work very well, since loadData is updating the UI. Even if it does happen to work, it's probably unnecessary since loadData is an asynchronous method.

    Furthermore, it adds async state machine overhead for no reason.

    Code:
    await Task.Run(() => { ViewModel.loadData(); });
    This option has all the same problems, except it's slightly more efficient since it doesn't have the async state machine overhead. But it's still updating VM properties on a background thread and using a background thread unnecessarily.

    public async void loadData()

    This one's the worst of all. It inherits the same problems as the others: updating VM properties on a background thread and using an unnecessary background thread. To that, it adds the problems of an async void. One problem is that NavigationHelper_LoadState cannot catch any exceptions from loadData. Another problem is that loadData is not easily testable.

    So just use the simple approach and call it directly:
    Code:
    await ViewModel.loadData().ConfigureAwait(false);
    < advertising removed by moderator >

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2016
    Location
    Slovenia
    Posts
    575

    Re: Async in ViewModel - best practices

    Hi Sherin,

    thanks for response. I agree with you on certain points, but not all.

    You should only use Task.Run if you have CPU-bound or blocking work that you want to move off the UI thread. That's not the case here, so the direct call is the most natural.
    It is CPU bound work here. Because I'm showing animation while fetching data from DB. And getting DB data also isn't completely IO bound work, from my experience.

    Usually when you just call some query in async you can achieve good results, but when you have some time-consuming query things get laggy in the UI. That happens because a simple "async" doesn't go to another thread, but only preserves free "space" in current syncronization context. At least that is how I understand complete theory behind async.

    public async void loadData()
    Bad example, I agree, that was my typo. I ussually only use async Tasks, I use async void only when I have to - e.g. for button_click event .

    So just use the simple approach and call it directly:
    Code:
    await ViewModel.loadData().ConfigureAwait(false);
    I tried that allready in previous projects, but It did not give me desired results. What I ussually do in all apps is to have somekind of loading animation (circles etc.) to show user that data is being fetched, while not blocking UI thread. So app is still responsive (you can move window, minimize It etc.), and when job is done, you can continue to work with app. Another side thing is that I've read bad things about "ConfigureAwait(false)". I'm not sure where and when was that, but I've dropped out of that solution.

  4. #4
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,106

    Re: Async in ViewModel - best practices

    No point in engaging that poster for a follow-up, as all they did was copy/paste content from this post verbatim:

    https://stackoverflow.com/questions/...e-mvvm-pattern

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2016
    Location
    Slovenia
    Posts
    575

    Talking Re: Async in ViewModel - best practices

    Quote Originally Posted by OptionBase1 View Post
    No point in engaging that poster for a follow-up, as all they did was copy/paste content from this post verbatim:

    https://stackoverflow.com/questions/...e-mvvm-pattern

    😁 Thanks, good to know. Never imagened that this of people also exist here.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width