Alexandr
Alexandr

Reputation: 1901

Using keywords async/await in database queries

I have a local database in Windows Phone 8 app. The app includes a lot of queries to the database and I don't want bad effect on the responsiveness of UI.

For example I have a table of users and method to get a user from database by id.

Current variant

public class CacheDataContext : DataContext
{
    public static string DBConnectionString = "Data Source=isostore:/Cache.sdf";

    public CacheDataContext(string connectionString)
            : base(connectionString) { }

    public static AutoResetEvent OperationOnDatabaseUser = new AutoResetEvent(true);

    public Table<User> UserItems;
}

public class CacheDataContextUser : CacheDataContext
{
    public CacheDataContextUser(string connectionString)
        : base(connectionString) { }

    public User GetUser(string id)
    {
        try
        {
            OperationOnDatabaseUser.WaitOne();
            using (CacheDataContext context = new CacheDataContext(DBConnectionString))
            {
                //find user in the data base and return 
            }
        }
        finally
        {
            OperationOnDatabaseUser.Set();
        }
    }
}

I need ensure safety of the data if at the same time on database allow different requests to add, update, delete data. For this I use AutoResetEvent. Not sure what I'm doing it right, but so far no problems.

I can get user from the database:

using (DataBaseUser = new CacheDataContextFriends(ConnectionString))
{
   var user = DataBaseUser.GetUser(id);
}

Async/await

But I want work with the database using keywords async/await.

public class CacheDataContextUser : CacheDataContext
{
    public CacheDataContextUser(string connectionString)
        : base(connectionString) { }

    private object threadLock = new object();

    public Task<User> GetUser(string id)
    {       
        using (CacheDataContext context = new CacheDataContext(DBConnectionString))
        {
            var result = await Task<User>.Factory.StartNew(() =>
                {
                    lock (threadLock)
                    {
                       //find user in the data base and return  
                    }                  
                });
            return result;          
        }       
    }
}

I'm afraid to rewrite the method as described above, because I'm not sure it's right. Please tell me what the problem may be. My main goal is to improve the responsiveness of the app.

Upvotes: 0

Views: 2637

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456917

First, AutoResetEvent is the wrong thing to use for exclusive access. In Windows, an "event" like this is a message from one thread to another; in this case, you just want a simple lock:

public class CacheDataContext : DataContext
{
  public static string DBConnectionString = "Data Source=isostore:/Cache.sdf";

  public CacheDataContext(string connectionString)
        : base(connectionString) { }

  protected static readonly object OperationOnDatabaseUser = new object();

  public Table<User> UserItems;
}

public class CacheDataContextUser : CacheDataContext
{
  public CacheDataContextUser(string connectionString)
    : base(connectionString) { }

  public User GetUser(string id)
  {
    lock (OperationOnDatabaseUser)
    {
        using (CacheDataContext context = new CacheDataContext(DBConnectionString))
        {
            //find user in the data base and return 
        }
    }
  }
}

If you want to improve your responsiveness, then call your data context methods using Task.Run, i.e.:

var user = await Task.Run(() => GetUser(id));

Do not expose asynchronous methods on your data context unless the actual database access is asynchronous (i.e., using EF6, not Task.Run).

Upvotes: 6

Related Questions