为手动生成的WCF(客户端)代理实现异步/等待模式



给定此接口

[ServiceContract]
public interface IProductService
{
    [OperationContract]
    Product Get(int id);
}

我想手动(也就是说,在VS中不使用scvutil或Add Service Reference)创建一个客户端代理。

我用以下方法

public class ProductService: IProductService
{
    readonly ChannelFactory<IProductService> factory;
    public ProductService()
    {
        factory = new ChannelFactory<IProductService>("*");
    }
    public Product Get(int id)
    {
        var channel = factory.CreateChannel();
        return channel.Get(id);
    }
}

我的问题是我还想要这个方法的异步/等待版本,只有在客户端,服务器端仍然是同步的。

我希望这是一个通用解决方案,因为我有很多这种方法和服务。

如果使用ChannelFactory允许异步等待,则接口需要返回TaskTask<T>

它将强制您的服务器端也返回一个任务,但如果您坚持保持同步,则可以与Task.CompletedTaskTask.FromResult同步返回任务(尽管如果您有选择,为什么要这样做)。

例如:

[ServiceContract]
interface IProductService
{
    [OperationContract]
    Task<Product> GetAsync(int id);
}
class ProductService : IProductService
{
    ChannelFactory<IProductService> factory;
    public ProductService()
    {
        factory = new ChannelFactory<IProductService>("*");
    }
    public Task<Product> GetAsync(int id)
    {
        var channel = factory.CreateChannel();
        return channel.GetAsync(id);
    }
}
class ProductAPI : IProductService
{
    public Task<Product> GetAsync(int id) => Task.FromResult(Get(id))
}

您实际上可以在不更改服务本身的情况下做到这一点。您可以简单地定义第二个接口,该接口包含方法的异步和Task返回版本,并标记有[ServiceContract(Name = "NameOfTheIterfaceWhichIsActuallyExposedOnTheServer")]

在您提到的示例中,您将使用GetAsync()操作定义第二个接口:

[ServiceContract(Name = "IProductService")]
public interface IProductServiceAsync
{
    [OperationContract]
    Task<Product> GetAsync(int id);
}

即使你的服务仍然实现并公开了IProductService,你也可以使用ChannelFactory<IProductServiceAsync>来调用它。只要方法名称与GetFoo/GetFooAsync模式匹配,一切都会正常工作。这就是Visual Studio中添加服务引用可以为您生成对同步服务的异步服务引用的方法。

请参阅使用ChannelFactory异步调用同步WCF方法,以获得有关其工作方式的更详细解释。

最新更新