WCF REST未异步处理

我们目前正在IIS中为我们的站点实现一个新的WCF REST服务,在许多页面上,我们可能会异步使用JQuery进行一些AJAX调用。 问题是似乎WCF(在服务器端)同步执行。

在页面加载时,我们对3种不同的方法进行3次单独调用。 使用日志记录,我可以看到它们都在大约5ms内相互访问global.asax文件。 从那里,日志记录显示按退出global.asax的顺序执行的所有操作(不一定是我们通过javascript从页面调用的顺序)。 我希望每次调用都能收到自己的线程并单独返回。 即使使用调试器附加,我也可以看到它不会执行下一个方法,直到我逐步执行当前方法。

以下是我为实现使用异步模型而实现的三种方法的操作合同。

[OperationContract(AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/ListUserPreferences" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginListUserPreferences(AsyncCallback callback, object state); Result<List<Data.EnumerationItem>> EndListUserPreferences(IAsyncResult asyncResult); [OperationContract(Name = "GetUserSecure", AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/GetUser" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginGetUser(AsyncCallback callback, object state); Result EndGetUser(IAsyncResult asyncResult); [OperationContract(AsyncPattern = true)] [WebInvoke( Method = "POST" , UriTemplate = "/ListWithAttributes" , BodyStyle = WebMessageBodyStyle.Wrapped , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] IAsyncResult BeginListWithAttributes(int index, int pageSize, AsyncCallback callback, object state); Result<PagedCollection> EndListWithAttributes(IAsyncResult asyncResult); 

以下是服务中某个实现的示例。

  public IAsyncResult BeginGetUser(AsyncCallback callback, object state) { var asyncResult = new CompletedAsyncResult<Result>(state); asyncResult.Result = new Result(); asyncResult.Result.Value.UserId = Guid.Empty; asyncResult.Result.Value.DisplayName = "asdfasd"; asyncResult.IsCompleted = true; callback(asyncResult); return asyncResult; } public Result EndGetUser(IAsyncResult asyncResult) { return ((CompletedAsyncResult<Result>)asyncResult).Result; } 

以下是我们在服务实现类上的属性。

 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)] 

任何人都可以提供一些见解,了解为什么这些是同步执行和我需要做什么,或者至少指出我需要做什么,让它们异步执行?

UPDATE

我接受了一些Matt的回答,并将我的逻辑移到了End函数调用,然后关注他如何更紧密地将大文件上传到自托管WCF Rest服务 。 但是,我无法使用他的技术调用End方法。 我也开始认为我会以错误的方式解决这个问题。 因为查看日志,我的自定义身份validation服务正在每次服务调用之前执行,这发生在任何异步方法操作甚至触发之前。 经过进一步调查后,我查看了进入IIS然后执行操作的每个请求的ThreadId。 看起来WCF只使用1个工作线程…句点。 我一次向IIS发送了多少请求并不重要。 例如,如果我发送3个请求,我可以看到它们都在不同的时间进入(彼此之间的毫秒数)并且都获得了自己的线程。 但是,似乎WCF只是将它们全部排队并按顺序执行它们,因为它们都在同一个线程上执行,包括身份validation服务调用。

从你的例子来看,它看起来就像你在“开始”调用完成之前所做的所有工作一样; 这是我在学习异步模式时发现的一个常见错误。

虽然我不熟悉它在WCF中的应用程序,但Async模型更典型的是一个Begin方法,它启动一个新线程,其中IAsyncResult对象由新线程更新。 要“完成”操作,当IsCompleted设置为true ,原始调用者应该将原始IAsyncResult对象传递回相应的End方法,该方法返回结果。 一个简单的实现如下:

  static Func getUser; public static IAsyncResult BeginGetUser(AsyncCallback callback, object state) { getUser = () => { Thread.Sleep(2000); return "finished"; }; return getUser.BeginInvoke(callback, state); } public static string EndGetUser(IAsyncResult asyncResult) { return getUser.EndInvoke(asyncResult); } 

对它的调用可能如下所示:

 var result = BeginGetUser(null, null); string value = EndGetUser(result); 

当然,这是一个微不足道的案例:引用http://kennyw.com/work/indigo/258 ,“如果你没有做”本地异步“的事情,那么你不应该使用AsyncPattern = true” 。

幸运的是,使用C#5.0或Microsoft发布的Async CTP,.Net异步模式可能会成为过去。

为了使事物变得异步,通常将它与其他异步事物组合在一起。 如果您只使用异步方法执行同步操作,那么使用异步模式没有任何意义。 例如,如果您的所有代码都在线程池上工作,那么您还没有完成任何操作,因为您已经通过移动代码异步将线程交还给线程池,但是通过启动您的代码将其从ASP.NET中删除在那里工作。

如果要进行数据库调用以获取用户,并且数据库支持异步操作,则可以构建如下内容:

 public IAsyncResult BeginGetUser(AsyncCallback callback, object state) { var taskFunc = Task.Factory.FromAsync(db.BeginGetUser, db.EndGetUser); return taskFunc.ContinueWith(task => { var reader = task.Result; reader.Read(); return new Data.User { DisplayName = reader["displayName"] as string, UserId = Guid.Parse(reader["userId"] as string), } } ); } public Result EndGetUser(IAsyncResult asyncResult) { return (Task)(asyncResult).Result; } 

让我强调一下:异步编程很棘手。 使用Task它会变得更容易一些,但你仍然必须绕过延续。 调试是一件苦差事,一个错误的举动,你的代码就会消失,你不知道为什么。 如果你错过了一个例外,它将出现在终结器线程上,你不一定知道原因。 此外,为了进行异步编程,您必须从头到脚进入multithreading编程,这种编程充满了危险。

在尝试进入异步之前,请确保您真正了解正在发生的事情。 如果你的代码本质上是同步的,那么使它成为异步并不会给你带来很大的好处, 除非它是一个长时间运行的进程并且你可以启动另一个线程来处理它。 人们使用异步处理程序,以便IIS(或任何底层Web服务器)可以让其线程返回服务其他请求; 正如我之前提到的那样,如果你只是从线程池中取出一个线程,你就是从IIS窃取该线程,你将看不到任何可扩展性的好处。 但是,如果您只是疯狂地创建新线程,那么您就会遭遇拒绝服务攻击。