pla*_*t1k 8 .net c# wcf upgrade
我们有一个用.NET 3.5编写的应用程序(控制台应用程序"服务器"自托管一堆WCF服务和一些WPF客户端).我想将"服务器"应用程序升级到.NET 4.6.为了进行测试,我只是要更改运行时并在4.6中添加一些子项目,其余项目为3.5.在顶级项目中,我将目标更改为4.6,并确保app.config文件中包含此内容:
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
</startup>
Run Code Online (Sandbox Code Playgroud)
WCF服务和其他支持项目都在解决方案中,无论如何我都没有修改它们.
我也没有修改WPF客户端.
在我们的一些服务中,我们在服务器上实现异步开始/结束模式.这对我来说是新的,因为我使用async/await模式学习了WCF(虽然我一般都熟悉begin/end).客户端上的任何异步要求都由调用代码决定.
服务
public interface IMyServiceCallback
{
void UnrelatedNotifyClientsMethod(Notification message);
}
[ServiceContract(CallbackContract = typeof(IMyServiceCallback))]
public interface IMyService
{
// ...
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginFetchSomething(int howMany, AsyncCallback callback, object state);
FetchSomethingResult EndFetchSomething(IAsyncResult result);
// ...
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyService : IMyService
{
// ...
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public IAsyncResult BeginFetchSomething(int howMany, AsyncCallback callback, object state)
{
AsyncResult<FetchSomethingResult> something = new AsyncResult<FetchSomethingResult>(callback, state);
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler((sender, args) =>
{
try
{
FetchSomethingResult resultData = Database.FetchSomethingQuery(howMany);
something.Result = resultData;
something.Complete(true);
}
catch (Exception e)
{
Log(e);
something.HandleException(e, false);
}
backgroundWorker.Dispose();
});
backgroundWorker.RunWorkerAsync();
return something;
}
public FetchSomethingResult EndFetchSomething(IAsyncResult result)
{
AsyncResult<FetchSomethingResult> something = result as AsyncResult<FetchSomethingResult>;
something.AsyncWaitHandle.WaitOne();
return something.Result;
}
// ...
// other similar methods
// ...
}
public class AsyncResult : IAsyncResult
{
// ...
public void Complete(bool completedSynchronously)
{
lock (this.Mutex)
{
this.IsCompleted = true;
this.CompletedSynchronously = completedSynchronously;
}
this.SignalCompletion();
}
protected void SignalCompletion()
{
(this.AsyncWaitHandle as ManualResetEvent).Set();
ThreadPool.QueueUserWorkItem(d => { this.InvokeCallback(); });
}
protected void InvokeCallback()
{
if (this.Callback != null)
{
this.Callback(this);
}
}
public void HandleException(Exception e, bool completedSynchronously)
{
lock (this.Mutex)
{
this.IsCompleted = true;
this.CompletedSynchronously = completedSynchronously;
this.Exception = e;
}
this.SignalCompletion();
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
客户端(由Visual Studio"添加服务引用"生成)
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class MyServiceClient : System.ServiceModel.DuplexClientBase<NameSpace.ServiceClients.IMyServiceClient>, NameSpace.ServiceClients.IMyServiceCliene
{
// ...
public NameSpace.ServiceClients.FetchSomethingResult FetchSomething(int howMany)
{
return base.Channel.FetchSomething(howMany);
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
在客户端上,我们有一些通用类来包装和公开我们的服务,调用可以在主线程上进行,但通常是通过后台工作者在后台线程上进行的.只需将服务器应用程序从.NET 3.5升级到4+并且不进行任何更改,就不再调用服务器上的End方法.我已经确认Begin方法返回,并且worker调用.Complete()并调用回调,但之后没有任何反应.1分钟后,客户端将在呼叫时抛出超时异常.
我们的代码库相当庞大且复杂,在阅读.NET 4迁移说明后,我并没有想到会有任何行为上的变化.
编辑:我已经包含了微软服务跟踪实用程序的屏幕截图,显示了在更新(顶部,3.5)和之后(底部,4.6)之前对FetchProductVersion的相同调用.
编辑2/3:在某些情况下,失败似乎不一致.
您必须正确设置IAsyncResult.CompletedSynchronously属性,false因为您IAsyncResult以异步方式完成。与 .NET Framework 4.6 中的 WCF 相反,.NET Framework 3.5 中的 WCF 在这种情况下只是忽略它。
所以something.Complete(true);改成something.Complete(completedSynchronously: false);.
IAsyncResult.CompletedSynchronously您可以通过在 getter 中放置断点来检查谁以及何时使用:
bool _completedSynchronously = false;
public bool CompletedSynchronously
{
get
{
// Set breakpoint here.
return _completedSynchronously;
}
set
{
_completedSynchronously = value;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您使用 .NET Framework 3.5 启动 WCF 服务,您会注意到它从未被实际调用。所以旧版本的 WCF 会直接忽略它。如果您使用 .NET Framework 4.6 启动服务,您将看到 WCF 内部类大量使用它DispatchOperationRuntime。特别是通过InvokeBegin()和功能。InvokeCallback()
| 归档时间: |
|
| 查看次数: |
550 次 |
| 最近记录: |