在MVC 4 Web应用程序中异步调用webservice

时间:2013-04-22 09:06:27

标签: asp.net asp.net-mvc-4 webservice-client

我正在构建我的第一个真正的MVC4应用程序,我遇到了以下问题。

我有一个“用户”类的模型。它的数据是通过异步调用webservice获得的:

public sealed class AdminDMSEntities
{
    public List<User> UserList { get; private set; }

    public AdminDMSEntities()
    {
        this.UserList = new List<User>(0);
        ServiceClient client = new ServiceClient();
        client.GetUsersCompleted += (s, e) => 
        {
            if (e.Result == null)
                throw new ArgumentNullException("No users were retrieved");

            UserList = new List<User>(0);
            e.Result.ForEach(w => this.UserList.Add(new User(w.Guid, w.TrusteeType, w.Username, w.Email, w.LastLogin, w.PasswordChanged, w.IsUsingTempPassword)));               
        };

        client.GetUsersAsync();
    }        
}

我打算使用这个类,因为我会使用从DbContext派生的类(如果我可以使用我不能使用的实体框架)。到目前为止,我只有班级用户。

我在UsersController中使用tis类,如下所示:

public class UsersController : Controller
{
    private AdminDMSEntities adminEntities = new AdminDMSEntities();
    //
    // GET: /User/

    public ActionResult Index()
    {
        return View(adminEntities.UserList);
    }
}

问题是我最终会遇到InvalidOperationException,因为控制器没有等待异步调用完成,并且在用户正确填充之前将UserList传递给视图。

我暂时可以同步调用,但很可能以后我会被迫使用异步调用,所以我想知道如何确保,控制器将等待异步调用完成之前UserList传递给view ...

提前致谢

编辑:我已尝试使用AsyncController的方法,如下所示,目前我已将此添加到AdminDMS实体类:

public static async Task<AdminDMSEntities> AdminDMSEntitiesAsync()
    {
        AdminDMSEntities result = null;
        Task<AdminDMSEntities> getUsersAsyncTask = Task.Factory.StartNew(() => new AdminDMSEntities());
        await getUsersAsyncTask;
        return result;
    }

这是对控制器的更改:

public class UsersController : AsyncController
{
    private AdminDMSEntities adminEntities = null;
    //
    // GET: /User/

    public async Task<ActionResult> Index()
    {
        if (adminEntities == null)
        {
            adminEntities = await AdminDMSEntities.AdminDMSEntitiesAsync();
        }
        return View(adminEntities.UserList);
    }
}

结果是adminEntities包含该类的实例,但列表中没有用户(应该有11个)。

EDIT2:由于我被告知创建新任务不是正确的事情,我从代码中选择了第一个建议的方法removin AdminDMSEntities类。感谢Darin帮助我:)

1 个答案:

答案 0 :(得分:1)

您可以使用asynchronous controller。我们的想法是让您的控制器派生自AsyncController类而不是Controller类。该类提供了允许您执行异步操作的方法。

例如:

public class MyController: AsyncController
{
    public void IndexAsync()
    {
        AsyncManager.OutstandingOperations.Increment();
        var client = new SomeClient();
        client.GetUsersCompleted += (s, e) => 
        {
            UserList = new List<User>();
            AsyncManager.Parameters["users"] = e.Result.Select(
                w => new User(
                    w.Guid, 
                    w.TrusteeType, 
                    w.Username, 
                    w.Email, 
                    w.LastLogin, 
                    w.PasswordChanged, 
                    w.IsUsingTempPassword
                )
            )
            .ToList();
            AsyncManager.OutstandingOperations.Decrement();            
        };
        client.GetUsersAsync();
    }

    public ActionResult IndexCompleted(IEnumerable<User> users)
    {
        return View(users);
    }
}

如果您使用的是.NET 4.5,您甚至可以利用新的async关键字进一步简化异步代码。如果您将数据访问层重构为新模式(即返回任务),则可以这样做:

public class MyController: AsyncController
{
    public async Task<ActionResult> Index()
    {
        var client = new SomeClient();
        var users = await client.GetUsersAsync().Select(
            w => new User(
                w.Guid, 
                w.TrusteeType, 
                w.Username, 
                w.Email, 
                w.LastLogin, 
                w.PasswordChanged, 
                w.IsUsingTempPassword
            )
        )
        .ToList();
        return View(users);
    }
}