棘手的IDisposable问题

时间:2013-05-06 02:19:47

标签: c# dispose

我正在尝试抽象/封装以下代码,因此所有客户端调用都不需要重复此代码。例如,这是一个从视图模型(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。请注意,WcfChannelFactoryWcfClientProxy被调用时会嵌入CreateChannel()。这就是视图模型在处理后尝试使用它的原因/方式。

如何正确处理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());

2 个答案:

答案 0 :(得分: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()));

旁注:

我没有将这种模式用于一次性用品,因为我最近没有找到太多需要的一次性用品。但是,从理论上讲,我认为我喜欢这种模式,在使用一次性装置时,在一个使用区块内执行回调,原因有两个:

  1. 很简单
  2. 它迫使IDisposables的消费者处置     实例正确......虽然我同意(我认为)与C#的团队     不处理IDisposable时不要引发编译器警告     在所有执行路径中,它仍然有点令人担忧。

答案 1 :(得分:0)

您确定在那里使用服务定位器模式吗?除了它是反模式,使用Invoke<T>Func<T, TResult>,我认为将来使用会有一些混乱。此外,我不认为这种方式会将服务的使用与另一层分开。

我认为通过返回结果,这种方法比使用Func<T, TResult>具有更多的SOC。

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

清洁,但我仍然不建议使用静态方法。