Асинхронный метод в синтаксисе запроса

Я учусь с asp.net, async/await, и у меня есть эта проблема:

У меня есть функция для IEnumerable<T> list, Этот список я заполняю с использованием синтаксиса запроса и выглядит так:

private IEnumerable<SomeModel> GetPersons(int categoryId) {
IEnumerable<SomeModel> persons = from g in db.Categories
                               join c in db.Persons on g.PersonTypeId equals c.PersonTypeId
                               where g.CategoryId == categoryId
                               select new SomeModel
                               {
                                   PersonName = c.FirstName + " " + c.LastName,
                                   //....etc.
                                   //And here I need call asynchronous function something like this:
                                   IsAdmin = GetPermission(c.Email)
                               }
    if (persons.Any()) {
        return persons;
    }

    return Enumerable.Empty<SomeModel>();
}

В SomeModel есть IsAdmin как бул (когда я пытался Task<bool> в GetPermission я использую Task<bool> в SomeModel). В GetPermission() это:

private bool GetPermission(string email) {
    var user = SomeMembershipService.GetUser(email); //SomeMembershipService is Interface with Tasks and so on.
    var roles = SomeMembershipService.GetRoles(user.Id); //user.Id is as string
    bool result = false;
    if (roles != null) {
        var adm = roles.Result.FirstOrDefault(x => x.Name.Contains("Admin"));
        result = adm != null;
    }
    return result;
}

Я попытался написать это с помощью async / await и Task, но обе мои попытки были ложными. Поэтому я подумал, что мне нужно позвонить GetPermission() вне IEnumerable<SomeModel> persons поэтому я добавляю этот блок кода в состояние после. Итак, код выглядит так:

private IEnumerable<SomeModel> GetPersons(int categoryId) {
    IEnumerable<SomeModel> persons = from g in db.Categories
                                   join c in db.Persons on g.PersonTypeId equals c.PersonTypeId
                                   where g.CategoryId == categoryId
                                   select new SomeModel
                                   {
                                       PersonName = c.FirstName + " " + c.LastName
                                       //....etc.
                                   }
    if (persons.Any()) 
    {
        //new code
        foreach (var p in persons) 
        {
            p.IsAdmin = GetPermission(p.Email);
        }
        //end of new code
        return persons;
    }

    return Enumerable.Empty<SomeModel>();
}

Но это тоже неправильно. Может быть, я плохо понимаю идентичность asp.net и async / await и их использование... Можете ли вы помочь мне - что я должен сделать? Потому что сейчас GetPermission называется слишком поздно, поэтому приложение вылетает. Когда я звоню GetPermission в запросе лиц функция вызывается слишком поздно (после заполненного списка лиц). Я понятия не имею, как продолжить.

GetUser() как есть public IUser GetUser(string username) а также GetRoles() как есть public async Task<IEnumerable<IRole>> GetRoles(string userId), Я уверен, что эти два метода работают нормально, я использую их в других кодах и никаких проблем с ними. Так что это должно быть где-то в коде выше, я думаю.

Извините, если это глупый вопрос, но я читал об этом лоте здесь и на MSDN, но не могу найти результат. Спасибо всем.


Почему я хочу использовать асинхронную функцию, как указано выше

Мне нужна эта функция, потому что, когда я делаю целую функцию как асинхронную задачу, другая функция, которая вызывает это, не работает должным образом.

У меня есть эта функция - это привязка данных для kendogrid ():

[HttpPost]
public ActionResult _PersonsBinding([DataSourceRequest]DataSourceRequest request, int id)
{
    DataSourceResult result = GetPersons(id).ToDataSourceResult(request);
    return Json(result);
}

Когда я делаю функцию IEnumerable<SomeModel> GetPersons как async Task<IEnumerable<SomeModel>> GetPersons функция привязки не знает ToDataSourceResult(), когда я делаю эту функцию также асинхронной. Если здесь могут быть проблемы, как я могу это исправить? Пожалуйста, будьте терпеливы со мной, я новичок...

2 ответа

Решение

Хорошо, я разрешаю это. Ошибка не была в коде выше, проблема была в async Task<IEnumerable<IRole>> GetRoles(string userId), Этот метод работает хорошо, но он не подходит для моего кода выше.

Асинхронное получение ролей выглядит следующим образом:

public async Task<IEnumerable<IRole>> GetRolesAsync(string userId)
{
    return await IdentityManager.Roles.GetRolesForUserAsync(userId);
}

Но мне пришлось использовать получение ролей, что в коде ниже. Я сделал новый метод в IMembershipService - public IEnumerable<IRole> GetRoles(string userId) (первый метод переименован в GetRolesAsync) и выглядит так:

public IEnumerable<IRole> GetRoles(string userId)
{
    return AsyncHelper.RunSync(() => IdentityManager.Roles.GetRolesForUserAsync(userId));
} 

Это возвращает общее IEnumerable<T>, По крайней мере, я могу использовать мою функцию GetPermissions в SQL-запрос. GetPermission() теперь выглядит так:

private bool GetPermission(string email) {
    var user = SomeMembershipService.GetUser(email); 
    var roles = SomeMembershipService.GetRoles(user.Id);

    bool result = false;
    if (roles != null) {
        var adm = roles.FirstOrDefault(x => x.Name.Contains("Admin"));
        result = adm != null;
    }
    return result;
}

в запросе sql я могу использовать теперь это:

....
select new SomeModel
                               {
                                   PersonName = c.FirstName + " " + c.LastName,
                                   IsAdmin = GetPermission(c.Email) // <-- This
                               }
....

спасибо всем, особенно Йеруну, который пытался помочь.

Ваша проблема не в асинхронности / ожидании. Ваша проблема в том, что вы выполняете запрос больше, чем один. Каждый foreach/ToList/ToArray и т. Д. Будет выполнять запрос с самого начала и создавать новые объекты. Поскольку глубоко внутри linq результаты выдаются с помощью yield return new { .....} и будут создаваться новые объекты. Вот почему вы теряете бит IsAdmin на следующем foreach. (вне метода)

if (persons.Any()) 
{
    // !HERE! <- foreach. this will run the query.
    foreach (var p in persons) 
    {
        p.IsAdmin = GetPermission(p.Email);
    }

    //Where you foreach in it's caller.
    return persons;
}

Вы должны материализовать запрос в первую очередь. Вы можете исправить это с помощью ToArray()/ToList(). Таким образом, запрос выполняется, повторяется и сохраняется как массив.

private IEnumerable<SomeModel> GetPersons(int categoryId) 
{
    IEnumerable<SomeModel> persons = (from g in db.Categories
                                     join c in db.Persons on g.PersonTypeId equals c.PersonTypeId
                                     where g.CategoryId == categoryId
                                     select new SomeModel
                                     {
                                        PersonName = c.FirstName + " " + c.LastName
                                        //....etc.
                                     }).ToArray();   // <------ here
    foreach (var p in persons) 
    {
        p.IsAdmin = GetPermission(p.Email);
    }

    //end of new code
    return persons;
}

Если вы хотите объединить GetPermission() и запрос, вы должны создать функцию SQL.

Другие вопросы по тегам