查询语法中的异步方法

时间:2013-10-01 12:37:24

标签: c# asp.net async-await asp.net-identity linq-query-syntax

我学习了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为bool(当我在GetPermission中尝试Task<bool>时,我在SomeModel中使用Task<bool>。 在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身份和异步/等待以及它们的使用...... 你能帮助我 - 我该怎么办?因为现在,调用GetPermission太晚了,所以app崩溃了。当我在人员查询中调用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 个答案:

答案 0 :(得分:0)

您的问题与async / await无关。您的问题是因为您执行的查询多于一个。每个foreach / ToList / ToArray等都将从头开始执行查询,并将创建新对象。因为linq内部的结果是`yield return new {.....}'并且会创建新对象。这就是为什么你在下一个foreach上失去了IsAdmin位的原因。 (方法之外)

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函数。

答案 1 :(得分:0)

好的,我解决了。 上面的代码中没有错误,问题出现在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>。至少我可以在sql查询中使用我的函数GetPermissions。 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
                               }
....
感谢所有人,特别是Jeroen,他们试图提供帮助。