【问题标题】:Async method in query syntax查询语法中的异步方法
【发布时间】:2013-10-07 14:58:40
【问题描述】:

我学习了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&lt;bool&gt; 时,我在 SomeModel 中使用 Task&lt;bool&gt;)。 在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 进行写入,但我的两次尝试都是错误的。所以我认为我必须在IEnumerable&lt;SomeModel&gt; persons 之外调用GetPermission(),所以我将这段代码添加到条件之后。所以代码看起来像这样:

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&lt;IEnumerable&lt;IRole&gt;&gt; GetRoles(string userId) 相同。我敢肯定,这两种方法都可以正常工作,我在其他代码中使用它们并且没有问题。所以我认为它必须在上面的代码中。

很抱歉,如果这是个愚蠢的问题,但我在此处和 msdn 上阅读了很多内容,但找不到结果。谢谢大家。


为什么我要像上面那样使用异步函数

我想要那里的函数,因为当我将整个函数作为异步任务时,另一个调用它的函数不能正常工作。

我有这个功能 - 它是 kendogrid () 的数据绑定:

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

当我将函数 IEnumerable&lt;SomeModel&gt; GetPersons 设为 async Task&lt;IEnumerable&lt;SomeModel&gt;&gt; GetPersons 时,绑定函数不知道 ToDataSourceResult(),当我将此函数设为异步时。如果这里有问题,我该如何解决? 请耐心等待我,我是新手...

【问题讨论】:

  • 你为什么要查看if (persons.Any()),如果你返回personsEnumerable.Empty&lt;SomeModel&gt;();不会有什么不同
  • 仅返回人员存在一些问题。我不记得为什么,但这有帮助。这是给肯多格的。但我认为这不是我的问题的原因。
  • 你能告诉我,为什么你想要那里的异步功能,对我来说似乎没有意义!为什么不将整个函数放在异步任务中?

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


【解决方案1】:

您的问题不在于异步/等待。您的问题是因为您执行的查询不止一个。每个 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-Function。

【讨论】:

  • 我尝试了第二个示例,但应用程序崩溃了。在第一种情况下,我按照您所写的方式进行了尝试,但没有任何变化。
  • 第一个是显示 foreach 点的示例。您在第二个中遇到了什么异常?
  • 日志或调试中没有错误。它在 GetPermissions() 中在线“冻结” - 这里 ` var adm = roles.Result.FirstOrDefault(x => x.Name.Contains("Admin")); ` 很长一段时间后,我只得到连接超时(我确定连接是正确的)。
  • 直接拨打GetPermission(string email)会怎样?
  • 同样的事情 - “冻结”,在调试中无法跳转到下一步,没有错误。我将尝试使用 await 制作 GetRoles() 并将 GetPermission() 制作为异步任务,但我觉得没啥变化……去试试吧
【解决方案2】:

好的,我解决了。 上面的代码中没有错误,问题出在async Task&lt;IEnumerable&lt;IRole&gt;&gt; GetRoles(string userId) 中。这种方法效果很好,但对我上面的代码不好。

异步获取角色如下所示:

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

但我必须使用以下代码中的获取角色。 我在 IMembershipService 中创建了新方法 - public IEnumerable&lt;IRole&gt; GetRoles(string userId)(第一个方法重命名为 GetRolesAsync),如下所示:

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

这将返回常见的IEnumerable&lt;T&gt;。至少我可以在 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,他试图提供帮助。

【讨论】:

    猜你喜欢
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多