4

我正在尝试抽象/封装以下代码,因此所有客户端调用都不需要重复此代码。例如,这是从视图模型 (MVVM) 到 WCF 服务的调用:

using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding()))
{
    var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];
    IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));    
    this.Applications = new ObservableCollection<Application>(prestoService.GetAllApplications().ToList());
}

我最初的重构尝试是这样做的:

public static class PrestoWcf
{
    public static IPrestoService PrestoService
    {
        get
        {
            using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding()))
            {
                var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];    
                return channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
            }
        }
    }
}

这使我的视图模型现在只需一行代码即可进行调用:

this.Applications = new ObservableCollection<Application>(PrestoWcf.PrestoService.GetAllApplications().ToList());

但是,我收到一个错误,WcfChannelFactory已经处理了。这是有道理的,因为当视图模型尝试使用它时,它确实被释放了。但是,如果我删除了using,那么我没有正确处理WcfChannelFactory. 请注意,在调用时WcfChannelFactory嵌入自身。这就是视图模型在处理后尝试使用它的原因/方式。WcfClientProxyCreateChannel()

如何抽象此代码,以使我的视图模型调用尽可能简单,同时正确处理WcfChannelFactory?我希望我解释得足够好。

编辑 - 解决了!

根据牛排的回答,这样做了:

public static class PrestoWcf
{
    public static T Invoke<T>(Func<IPrestoService, T> func)
    {
        using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding()))
        {
            var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];

            IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
            return func(prestoService);
        }
    }
}

这是视图模型调用:

this.Applications = new ObservableCollection<Application>(PrestoWcf.Invoke(service => service.GetAllApplications()).ToList());
4

2 回答 2

7

类似以下内容可能会有所帮助

public static void UsePrestoService(Action<IPrestoService> callback)
{
    using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding()))
    {
        var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];
        IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));  
        //Now you have access to the service before the channel factory is disposed.  But you don't have to worry about disposing the channel factory.
        callback(prestoService);
    }
}

UsePrestoService(service => this.Applications = new ObservableCollection<Application>(service.GetAllApplications().ToList()));

边注:

我没有在一次性用品上使用这种模式,因为我最近没有发现对一次性用品有太多需求。但是,从理论上讲,我认为我喜欢这种模式,在使用一次性用品时采用在 using 块内执行的回调,原因有两个:

  1. 这很简单
  2. 它迫使 IDisposables 的消费者正确地处理实例......虽然我同意(我认为)C# 的团队在 IDisposables 未在所有执行路径中处理时不引发编译器警告,但它仍然有点令人担忧。
于 2013-05-06T02:25:24.813 回答
0

你确定在那里使用服务定位器模式吗?除此之外它是一种反模式,通过使用Invoke<T>and Func<T, TResult>,我认为在未来的使用中会有一些混乱。此外,我认为这种方式不会将服务的使用分离到另一层。

我认为这种方法通过返回结果,比使用Func<T, TResult>.

public static class PrestoWcf
{
    public static IEnumerable<Application> PrestoService
    {
        get
        {
            IEnumerable<Application> appList;
            using (var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding()))
            {
                var endpointAddress = ConfigurationManager.AppSettings["prestoServiceAddress"];    
                appList = prestoService.GetAllApplications().ToArray(); //you can ignore the .ToArray, I just not sure whether the enumerable still keep the reference to service
            }
            return appList;
        }
    }
}

更干净,但我仍然不建议使用静态方法。

于 2013-05-06T04:43:29.917 回答