2014-05-09 27 views
6

Lavoro su progetto in cui l'applicazione Web ospitata sul server Web chiama i servizi WCF ospitati sul server dell'app. Proxy per WCF chiamate è stato creato da ChannelFactory e chiamate vengono effettuate tramite il canale, ad esempio:Chiamata WCF asincrona con ChannelFactory e CreateChannel

(omettendo utilizzando blocco)

var factory = new ChannelFactory<IUserService>(endpointConfigurationName); 
var channel = factory.CreateChannel(); 

var users = channel.GetAllUsers(); 

Se ho capito bene chiamare attraverso il canale è asincrona e filo sul server web è inattivo durante la richiesta e attendere una risposta.

vorrei effettuare chiamata asincrona in questo modo:

var users = await channel.GetAllUsersAsync(); 

Esiste un modo come fare chiamata con ChannelFactory e canali asincrone? Non ho trovato nessuno. So che posso generare metodi asincroni via svcutil/Aggiungi riferimento servizio, ma non voglio farlo. Inoltre non voglio cambiare l'interfaccia del servizio sul server delle app (IUserService) aggiungendo metodi asincroni.

C'è un modo come chiamare metodi asincroni con ChannelFactory? Grazie.

risposta

3

Purtroppo no, non c'è.

I metodi asincroni ottenuti da svcutil vengono generati nel proxy in base all'interfaccia. Non c'è niente nel canale WCF grezzo come questo.

L'unico modo è di modificare il riferimento del servizio in modo da avere chiamate asincrone native, che non si desidera, o creare il proprio wrapper attorno al canale e implementarle come fa il proxy generato.

+0

grazie per la risposta. Hai qualche suggerimento/link su come creare wrapper personalizzati attorno al canale? Ho fatto delle indagini ma non ho trovato nulla. – Michal

4

Sfortunatamente, questo non è possibile e c'è una buona ragione per questo. CreateChannel restituisce un oggetto che implementa l'interfaccia fornita (IUserService nell'esempio). Questa interfaccia non è async-aware, quindi non è possibile restituire un oggetto con i metodi corretti.

ci sono due soluzioni possibili:

  1. creare il proprio proxy che è in grado di chiamare il servizio WCF. Ciò significa che devi scrivere il tuo proxy (o lasciare che lo svcutil lo faccia per te).
  2. Assicurarsi che IUserService sia un'interfaccia asincrona che restituisce le attività. Questo è supportato in WCF 4.5 e versioni successive. Questo è quello che uso spesso. Lo svantaggio principale è che rende il vostro servizio un po 'complicato e vi viene richiesto di invocare i metodi async (che potrebbe anche essere considerato un vantaggio).
6

È possibile generare automaticamente nuova interfaccia che contiene le versioni asincroni di metodi di interfaccia originale utilizzando T4 e utilizzarlo in ChannelFactorywithout changing interface on server side.

ho usato NRefactory analizzare originale e generare nuovo codice sorgente C# e AssemblyReferences.tt di utilizzare pacchetti Nuget nel template T4:

<#@ 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()#>​ 

a passare il nome del file di interfaccia per modello:

using System.Collections.Generic; 
using System.ServiceModel; 

namespace MyProject 
{ 
    [ServiceContract] 
    interface IUserService 
    { 
     [OperationContract] 
     List<User> GetAllUsers(); 
    } 
} 

Per get new one:

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(); 
    } 
} 

Ora puoi metterlo io n factory per utilizzare il canale in modo asincrono:

var factory = new ChannelFactory<MyProject.Client.IUserService>("*"); 
var channel = factory.CreateChannel(); 
var users = await channel.GetAllUsersAsync(); 
Problemi correlati