MVC 4 web应用程序异步调用webservice
问题描述:
我正在构建我的第一个真正的MVC4应用程序,并遇到以下问题。MVC 4 web应用程序异步调用webservice
我有一个“用户”类的模型。它的数据通过异步调用获得Web服务:
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这样的:
public class UsersController : Controller
{
private AdminDMSEntities adminEntities = new AdminDMSEntities();
//
// GET: /User/
public ActionResult Index()
{
return View(adminEntities.UserList);
}
}
的问题是,我将最终出现InvalidOperationException,因为控制器不等待异步调用完成并通过在UserList的观点之前,它是正确填充用户。
我可以暂时调用同步,但很可能以后我会*使用异步调用,所以我想知道如何确保该控制器将等待异步调用完成通过之前的UserList查看...
在此先感谢
编辑:我曾尝试与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)。编辑2:由于我被告知创建新任务不是正确的事情,所以我从代码中选择了第一个建议的方法removin AdminDMSEntities类。感谢Darin帮助我:)
答
您可以使用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);
}
}
它看起来像我需要的东西,但我不能让它运行。我的GetUsersAsync方法来自于自动生成的服务引用,它返回“void”,所以我使用了我的原始文章中的构造函数的代码,并且我必须使用'await Task.Factory.StartNew(()=> client.GetUsersAsync ());'不幸的是null仍然被传递给视图。 – Erchi 2013-04-22 10:28:38
你不应该创建一个新的任务。您应该直接在控制器操作中使用自动生成的客户端实例。并订阅我的答案中显示的'GetUsersCompleted'事件。 – 2013-04-22 10:42:58
我删除了以前的评论,我刚刚找到自动生成方法的定义。我会尝试你的方法,然后回复你 – Erchi 2013-04-22 10:54:47