C# 使用ChannelFactory和CreateChannel进行异步WCF调用
我从事的项目是,托管在web服务器上的web应用程序调用托管在应用服务器上的WCF服务。WCF调用的代理由ChannelFactory创建,并通过channel进行调用,例如: (省略使用块) 有没有办法使用ChannelFactory和Channel async进行调用?我没找到。我知道我可以通过svcutil/Add服务引用生成异步方法,但我不想这样做。另外,我不想通过添加异步方法来更改AppServer上的服务接口(IUserService)C# 使用ChannelFactory和CreateChannel进行异步WCF调用,c#,.net,wcf,async-await,C#,.net,Wcf,Async Await,我从事的项目是,托管在web服务器上的web应用程序调用托管在应用服务器上的WCF服务。WCF调用的代理由ChannelFactory创建,并通过channel进行调用,例如: (省略使用块) 有没有办法使用ChannelFactory和Channel async进行调用?我没找到。我知道我可以通过svcutil/Add服务引用生成异步方法,但我不想这样做。另外,我不想通过添加异步方法来更改AppServer上的服务接口(IUserService) 有没有办法用ChannelFactory异步调
有没有办法用ChannelFactory异步调用方法?谢谢。不幸的是,没有 从svcutil获得的异步方法是基于接口在代理中生成的。在原始WCF频道中没有这样的内容
唯一的方法是更改服务引用以进行本机异步调用,这是您不希望的,或者在通道周围创建您自己的包装器,并像生成的代理那样自行实现它们。不幸的是,这是不可能的,这是有充分理由的
CreateChannel
返回实现所提供接口的对象(IUserService
,在您的示例中)。此接口不支持异步,所以无法使用正确的方法返回对象
有两种可能的解决方案:
svcutil
为您编写代理)您可以使用从原始接口自动生成包含异步版本的方法的新接口,并在
ChannelFactory
中使用它
我曾经解析原始代码并生成新的C#源代码,并在T4模板中使用nuget包:
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ include file="AssemblyReferences.tt" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="ICSharpCode.NRefactory.CSharp" #>
<#@ output extension=".cs"#>
<#
var file = System.IO.File.ReadAllText(this.Host.ResolvePath("IUserService.cs"));
if(!file.Contains("using System.Threading.Tasks;"))
{ #>
using System.Threading.Tasks;
<# } #>
<#
CSharpParser parser = new CSharpParser();
var syntaxTree = parser.Parse(file);
foreach (var namespaceDeclaration in syntaxTree.Descendants.OfType<NamespaceDeclaration>())
{
namespaceDeclaration.Name += ".Client";
}
foreach (var methodDeclaration in syntaxTree.Descendants.OfType<MethodDeclaration>())
{
if (methodDeclaration.Name.Contains("Async"))
continue;
MethodDeclaration asyncMethod = methodDeclaration.Clone() as MethodDeclaration;
asyncMethod.Name += "Async";
if (asyncMethod.ReturnType.ToString() == "void")
asyncMethod.ReturnType = new SimpleType("Task");
else
asyncMethod.ReturnType = new SimpleType("Task", typeArguments: asyncMethod.ReturnType.Clone());
methodDeclaration.Parent.AddChild(asyncMethod, Roles.TypeMemberRole);
}
#>
<#=syntaxTree.ToString()#>
using System.Collections.Generic;
using System.ServiceModel;
namespace MyProject
{
[ServiceContract]
interface IUserService
{
[OperationContract]
List<User> GetAllUsers();
}
}
使用System.Threading.Tasks;
将接口文件名传递给模板:
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ include file="AssemblyReferences.tt" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="ICSharpCode.NRefactory.CSharp" #>
<#@ output extension=".cs"#>
<#
var file = System.IO.File.ReadAllText(this.Host.ResolvePath("IUserService.cs"));
if(!file.Contains("using System.Threading.Tasks;"))
{ #>
using System.Threading.Tasks;
<# } #>
<#
CSharpParser parser = new CSharpParser();
var syntaxTree = parser.Parse(file);
foreach (var namespaceDeclaration in syntaxTree.Descendants.OfType<NamespaceDeclaration>())
{
namespaceDeclaration.Name += ".Client";
}
foreach (var methodDeclaration in syntaxTree.Descendants.OfType<MethodDeclaration>())
{
if (methodDeclaration.Name.Contains("Async"))
continue;
MethodDeclaration asyncMethod = methodDeclaration.Clone() as MethodDeclaration;
asyncMethod.Name += "Async";
if (asyncMethod.ReturnType.ToString() == "void")
asyncMethod.ReturnType = new SimpleType("Task");
else
asyncMethod.ReturnType = new SimpleType("Task", typeArguments: asyncMethod.ReturnType.Clone());
methodDeclaration.Parent.AddChild(asyncMethod, Roles.TypeMemberRole);
}
#>
<#=syntaxTree.ToString()#>
using System.Collections.Generic;
using System.ServiceModel;
namespace MyProject
{
[ServiceContract]
interface IUserService
{
[OperationContract]
List<User> GetAllUsers();
}
}
使用System.Collections.Generic;
使用System.ServiceModel;
名称空间MyProject
{
[服务合同]
接口服务
{
[经营合同]
列出GetAllUsers();
}
}
要获得新的:
using System.Threading.Tasks;
using System.Collections.Generic;
using System.ServiceModel;
namespace MyProject.Client
{
[ServiceContract]
interface IUserService
{
[OperationContract]
List<User> GetAllUsers ();
[OperationContract]
Task<List<User>> GetAllUsersAsync ();
}
}
使用System.Threading.Tasks;
使用System.Collections.Generic;
使用System.ServiceModel;
名称空间MyProject.Client
{
[服务合同]
接口服务
{
[经营合同]
列出GetAllUsers();
[经营合同]
任务GetAllUsersAsync();
}
}
现在,您可以将其放在工厂中异步使用通道:
var factory = new ChannelFactory<MyProject.Client.IUserService>("*");
var channel = factory.CreateChannel();
var users = await channel.GetAllUsersAsync();
var-factory=newchannelfactory(“*”);
var channel=factory.CreateChannel();
var users=await channel.GetAllUsersAsync();
感谢您的回复。关于如何在频道周围创建自定义包装器,您有任何提示/链接吗?我做了一些调查,但没有发现任何东西。嘿,我能让T4正常工作,但我在T4s是个彻头彻尾的傻瓜。。。如何让生成的C#代码运行以允许我访问MyProject.Client.IUserService
?如果我执行string content=myT4.TransformText()代码>我得到了新的界面,但我真的不确定从哪里开始there@philrT4在IDE中使用,而不是在运行时使用。在Visual Studio中右键单击*.tt文件,然后转到运行自定义工具,该工具将生成一个*.cs文件,您可以在解决方案中需要粘贴的位置复制粘贴该文件。老实说,这个答案不是很有用,因为它只减少了一些拷贝粘贴;您仍然需要为GetAllUsersAsync()
或任何等效函数编写代码。