2014-05-09 13 views
6

Tôi làm việc trên dự án nơi ứng dụng web được lưu trữ trên máy chủ web gọi các dịch vụ WCF được lưu trữ trên máy chủ ứng dụng. Proxy cho WCF cuộc gọi được tạo ra bởi ChannelFactory và các cuộc gọi được thực hiện thông qua kênh, ví dụ:Gọi WCF không đồng bộ với ChannelFactory và CreateChannel

(bỏ qua sử dụng khối)

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

var users = channel.GetAllUsers(); 

Nếu tôi hiểu nó cũng gọi qua kênh là async và chủ đề trên máy chủ web là nhàn rỗi trong khi yêu cầu và chỉ cần chờ phản hồi.

Tôi muốn thực hiện cuộc gọi async như thế này:

var users = await channel.GetAllUsersAsync(); 

Có cách nào làm thế nào để thực hiện cuộc gọi với ChannelFactory và các kênh truyền hình async? Tôi không tìm thấy gì cả. Tôi biết rằng tôi có thể tạo ra các phương thức async thông qua svcutil/Thêm tham chiếu dịch vụ nhưng tôi không muốn làm điều đó. Ngoài ra, tôi không muốn thay đổi giao diện dịch vụ trên máy chủ ứng dụng (IUserService) bằng cách thêm các phương thức không đồng bộ.

Có cách nào để gọi các phương thức không đồng bộ với ChannelFactory không? Cảm ơn.

Trả lời

3

Thật không may, không có.

Các phương pháp không đồng bộ bạn nhận được từ svcutil được tạo trong proxy dựa trên giao diện của bạn. Không có gì trong kênh WCF thô như thế này.

Cách duy nhất là thay đổi tham chiếu dịch vụ để có cuộc gọi không đồng bộ gốc, bạn không muốn hoặc tạo trình bao bọc của riêng mình quanh kênh và tự triển khai chúng như proxy được tạo.

+0

cảm ơn bạn đã trả lời. Bạn có mẹo/liên kết nào về cách tạo trình bao bọc tùy chỉnh quanh kênh không? Tôi đã thực hiện một số điều tra nhưng tôi không tìm thấy bất cứ điều gì. – Michal

4

Thật không may, điều này là không thể và có lý do khá tốt cho nó. CreateChannel trả về một đối tượng thực hiện giao diện được cung cấp (IUserService trong ví dụ của bạn). Giao diện này không phải là nhận thức không đồng bộ, vì vậy không có cách nào nó có thể trả về một đối tượng với các phương thức đúng.

Có hai giải pháp khả thi:

  1. Tạo proxy của riêng bạn mà có thể gọi các dịch vụ WCF. Điều này có nghĩa là bạn cần phải viết proxy của riêng bạn (hoặc để svcutil làm điều đó cho bạn).
  2. Đảm bảo IUserService là giao diện không đồng bộ trả về tác vụ. Điều này được hỗ trợ trong WCF 4.5 và sau đó. Đây là những gì tôi thường sử dụng. Hạn chế lớn nhất là nó làm cho dịch vụ của bạn phức tạp một chút và bạn được yêu cầu gọi các phương thức async (cũng có thể được coi là một lợi thế).
6

Bạn có thể tự động tạo ra giao diện mới có chứa các phiên bản async các phương pháp từ giao diện ban đầu sử dụng T4 và sử dụng nó trong ChannelFactorywithout changing interface on server side.

tôi đã sử dụng NRefactory để phân tích ban đầu và tạo ra mã nguồn C# mới và AssemblyReferences.tt để sử dụng các gói NuGet trong T4 mẫu:

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

Bạn vượt qua tên file giao diện của bạn để mẫu:

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

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

Để nhận mã mới:

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

Bây giờ bạn có thể đặt nó n nhà máy sử dụng kênh không đồng bộ:

var factory = new ChannelFactory<MyProject.Client.IUserService>("*"); 
var channel = factory.CreateChannel(); 
var users = await channel.GetAllUsersAsync(); 
Các vấn đề liên quan