Async WCF call with ChannelFactory and CreateChannel

前端 未结 3 1695
南旧
南旧 2021-02-06 00:47

I work on project where web application hosted on web server calls WCF services hosted on the app server. Proxy for WCF calls is created by ChannelFactory and calls are made via

3条回答
  •  情深已故
    2021-02-06 01:47

    You can automatically generate new interface that contains async versions of methods from original interface using T4 and use it in ChannelFactory without changing interface on server side.

    I used NRefactory to parse original and generate new C# source code and AssemblyReferences.tt to use nuget packages in T4 template:

    <#@ 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.Name += ".Client";
    }
    
    
    foreach (var methodDeclaration in syntaxTree.Descendants.OfType())
    {
        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()#>​
    

    You pass your interface file name to template:

    using System.Collections.Generic;
    using System.ServiceModel;
    
    namespace MyProject
    {
        [ServiceContract]
        interface IUserService
        {
            [OperationContract]
            List GetAllUsers();
        }
    }
    

    To get new one:

    using System.Threading.Tasks;
    using System.Collections.Generic;
    using System.ServiceModel;
    
    namespace MyProject.Client
    {
        [ServiceContract]
        interface IUserService
        {
            [OperationContract]
            List GetAllUsers ();
    
            [OperationContract]
            Task> GetAllUsersAsync ();
        }
    }
    

    Now you can put it in factory to use channel asynchroniously:

    var factory = new ChannelFactory("*");
    var channel = factory.CreateChannel();
    var users = await channel.GetAllUsersAsync();
    

提交回复
热议问题