简体   繁体   中英

c# winforms How do I use async await using LINQ

I want to use async await function to group by age. It's working fine without aync await, but I want use asyn await. If I exclude ToList() in firstline...from u in db.Patient.ToList()... then I get problem with CalculateAge function with error LINQ Entities does not recognize in the method CalculateAge(system.String).... That's why use it there. CalculateAge function is to calculate age and here is code function

private int CalculateAge(string birthday)
        {
            int age;
            if (string.IsNullOrWhiteSpace(birthday)) return 0;

            DateTime empBirthday = Convert.ToDateTime(birthday);
            DateTime today = DateTime.Today;
            age = today.Year - empBirthday.Year;
            if (empBirthday > today.AddYears(-age))
                age--;
            return age;
        }

This work fine without async await

private void btnByAge_Click(object sender, EventArgs e)
{
    var result = (from u in db.Patient.ToList() // 
                  join a in db.Analys on u.PatientId equals a.PatientId
                  where a.Status == Active
                  group u by CalculateAge(u.DOB.ToString()) into g
                  select new
                  {
                      Age = g.Key,
                      Amount = g.Count(),
                  }).Tolist();

    if (result != null)
    {
        dgvResult.DataSource = result;
    }
}

But not this with async await

private async void btnByAge_Click(object sender, EventArgs e)
{
    var result = (from u in db.Patient.ToListAsync()
                  join a in db.Analys on u.PatientId equals a.PatientId
                  where a.Status == Active
                  group u by CalculateAge(u.DOB.ToString()) into g
                  select new
                  {
                      Age = g.Key,
                      Amount = g.Count(),
                  });

    var data = await result.ToListAsync();
    if (data != null)
    {
        dgvResult.DataSource = data;
    }
}

Error I get is Error CS1936 Could not find an implementation of the query pattern for source type 'Task>'. 'Join' not found... I have used even Using.System.Data.Linq, but the same problem. Please Help, thank you in advance!

All you need to do to fix the error is to put an await before the db.Patient.ToListAsync() . However it would be better to not even do that ToList so that the join can be done in the DB. Also note you don't need the null check as the results will never be null (maybe empty or maybe an exception will be thrown, but they should never be null)

var dobs = await (from u in db.Patient
                  join a in db.Analys on u.PatientId equals a.PatientId
                  where a.Status == Active
                  select u.DOB).ToListAsync();
var data = dobs
    .GroupBy(x => CalculateAge(x.toString())
    .Select(grp => new
    {
        Age = grp.Key,
        Amount = grp.Count(),
    })
    .ToList();

dgvResult.DataSource = data;    

That will do the join and filter on Status and select the DOBs in the DB and get the dobs asynchronously. Then you can do the group by in memory since CalculateAge cannot be translated to SQL. But an even better idea is to do the age calculation in the DB.

var now = DateTime.UtcNow(); // or .Now() if you don't store UTC in the DB
var result =  from u in db.Patient
              join a in db.Analys on u.PatientId equals a.PatientId
              where a.Status == Active
              let age = now.Year - u.DOB.Year -
                  ((u.DOB.Month < now.Month || 
                      (u.DOB.Month == now.Month && u.DOB.Day < now.Day))
                  ? 1 : 0)
              group u by age into g
              select new
              {
                  Age = g.Key,
                  Amount = g.Count(),
              };

dgvResult.DataSource = await result.ToListAsync();    

That determines the age by subtracting the DOB year from the current year and then subtracting 1 if the current day is before the Month and Day of the DOB.

Also if you plan to use CalculateAge then just pass the DateTime to it instead of formatting it to a string and then parsing that back to a DateTime . You can pass a DateTime? if it's possible that it might be null .

private int CalculateAge(DateTime? birthday)
{
    int age;
    if (!birthday.HasValue) return 0;

    DateTime today = DateTime.Today;
    age = today.Year - birthday.Value.Year;
    if (birthday.Value > today.AddYears(-age))
        age--;
    return age;
}

Nothing special you can achieve this code in C#,look at below code.

private async void button1_Click(object sender, EventArgs e)
    {
        var query = (from p in db.Patient
                     join An in db.Analys on p.PatientId equals An.PatientId
                     where An.Status == Active
                     select p.DOB)



       var Result=await query.GroupBy(x => CalculateAge(x.toString())
           .Select(g => new {
               Age = g.Key,
               Amount = g.Count(),
           }).ToListAsync();
 dgvResult.DataSource = Result
    }    

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