2014-05-09 13 views
6

Pracuję nad projektem, w którym aplikacja internetowa jest hostowana na serwerach internetowych wywołania usług WCF hostowanych na serwerze aplikacji. Proxy dla WCF połączeń jest tworzony przez ChannelFactory i połączenia są za pośrednictwem kanału, przykładowo:Asyncowe wywołanie WCF z ChannelFactory i CreateChannel

(z pominięciem użyciu bloku)

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

var users = channel.GetAllUsers(); 

Jeśli dobrze rozumiem to również wywołać poprzez kanał jest asynchroniczny i gwint na serwerze WWW jest bezczynnie podczas żądania i po prostu poczekaj na odpowiedź.

Chciałbym, aby połączenia asynchronicznie tak:

var users = await channel.GetAllUsersAsync(); 

Czy istnieje sposób, jak uczynić rozmowę z ChannelFactory i kanałów async? Nie znalazłem żadnego. Wiem, że mogę generować asynchroniczne metody przez svcutil/Add service reference, ale nie chcę tego robić. Nie chcę też zmieniać interfejsu usługi na serwerze aplikacji (IUserService), dodając metody asynchroniczne.

Czy jest jakiś sposób wywołania metod async z ChannelFactory? Dzięki.

Odpowiedz

3

Niestety nie, nie ma.

Metody asynchroniczne uzyskiwane z svcutil są generowane w proxy na podstawie interfejsu użytkownika. W surowym kanale WCF nie ma nic takiego.

Jedynym sposobem jest zmiana odwołania do usługi na natywne połączenia asynchroniczne, których nie chcesz, lub utworzenie własnego folderu wokół kanału i zaimplementowanie go samodzielnie, tak jak robi to wygenerowany serwer proxy.

+0

dziękuję za odpowiedź. Czy masz jakąś wskazówkę/link o tym, jak tworzyć niestandardowe opakowania wokół kanału? Zbadałem sprawę, ale niczego nie znalazłem. – Michal

4

Niestety, nie jest to możliwe i istnieje ku temu dobry powód. CreateChannel zwraca obiekt implementujący podany interfejs (IUserService w twoim przykładzie). Ten interfejs nie jest zgodny z interfejsem asynchronicznym, więc nie ma możliwości zwrócenia obiektu za pomocą poprawnych metod.

Istnieją dwa możliwe rozwiązania:

  1. Stwórz własną proxy, który jest w stanie połączyć się z usługi WCF. Oznacza to, że musisz napisać swój własny serwer proxy (lub pozwolić mu zrobić to za Ciebie).
  2. Upewnij się, że IUserService jest interfejsem asynchronicznym, który zwraca zadania. Jest to obsługiwane w WCF 4.5 i nowszych. Tego często używam. Główną wadą jest to, że sprawia, że ​​twoja usługa jest nieco skomplikowana i musisz wywołać metody asynchroniczne (co również może być uznane za zaletę).
6

można automatycznie wygenerować nowy interfejs, który zawiera wersje async metod z wykorzystaniem oryginalnego interfejsu T4 i używać go w ChannelFactorywithout changing interface on server side.

użyłem NRefactory do analizowania oryginału i wygenerować nowy kod źródłowy C# i AssemblyReferences.tt używać pakietów Nuget w T4 Szablon:

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

zdać nazwę pliku interfejsu do szablonu:

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

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

Aby uzyskaj nowe:

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

Teraz możesz to zrobić n fabryka do korzystania z kanału asynchronicznie:

var factory = new ChannelFactory<MyProject.Client.IUserService>("*"); 
var channel = factory.CreateChannel(); 
var users = await channel.GetAllUsersAsync(); 
Powiązane problemy