C# 创建WCF ChannelFactory<;T>;

C# 创建WCF ChannelFactory<;T>;,c#,.net,wcf,c#-4.0,channelfactory,C#,.net,Wcf,C# 4.0,Channelfactory,我正在尝试将现有的.NET远程处理应用程序转换为WCF。服务器和客户端都共享公共接口,所有对象都是服务器激活的对象 在WCF世界中,这类似于创建每次呼叫服务,并使用ChannelFactory创建代理。我正在为如何为ASP.NET客户端正确创建ChannelFactory而苦苦挣扎 出于性能原因,我希望缓存ChannelFactory对象,并在每次调用服务时创建通道。在.NET远程处理时代,曾经有RemotingConfiguration.getRegistereAllowKnownClient

我正在尝试将现有的.NET远程处理应用程序转换为WCF。服务器和客户端都共享公共接口,所有对象都是服务器激活的对象

在WCF世界中,这类似于创建每次呼叫服务,并使用
ChannelFactory
创建代理。我正在为如何为ASP.NET客户端正确创建
ChannelFactory
而苦苦挣扎

出于性能原因,我希望缓存
ChannelFactory
对象,并在每次调用服务时创建通道。在.NET远程处理时代,曾经有
RemotingConfiguration.getRegistereAllowKnownClientTypes()
方法来获取客户端对象的集合,然后我可以缓存这些对象。看起来,在WCF世界中并没有这样的东西,尽管我能够从配置文件中获得端点的集合

下面是我认为有效的方法。我可以创建如下内容:

public static ProxyHelper
{
    static Dictionary<Type, object> lookup = new Dictionary<string, object>();  

    static public T GetChannel<T>()
    {
        Type type = typeof(T);
        ChannelFactory<T> factory;

        if (!lookup.ContainsKey(type))
        {
            factory = new ChannelFactory<T>();
            lookup.Add(type, factory);
        }
        else
        {
            factory = (ChannelFactory<T>)lookup[type];
        }

        T proxy = factory.CreateChannel();   
        ((IClientChannel)proxy).Open();

        return proxy;
    }    
}
publicstaticproxyhelper
{
静态字典查找=新建字典();
静态公共T GetChannel()
{
类型=类型(T);
渠道工厂;
如果(!lookup.ContainsKey(类型))
{
工厂=新的渠道工厂();
查找.添加(类型,工厂);
}
其他的
{
工厂=(ChannelFactory)查找[类型];
}
T proxy=factory.CreateChannel();
((IClientChannel)代理);
返回代理;
}    
}
我认为上面的代码会起作用,但我有点担心多个线程试图添加新的
ChannelFactory
对象,如果它不在查找中。由于我使用的是.NET 4.0,我考虑使用
ConcurrentDictionary
并使用
GetOrAdd()
方法或使用
TryGetValue()
方法首先检查
ChannelFactory
是否存在,然后使用
GetOrAdd()
方法。不确定ConcurrentDictionary.TryGetValue()和ConcurrentDictionary.GetOrAdd()方法的性能


另一个小问题是,在ASP.NET应用程序结束后,我是否需要对通道工厂对象调用
ChannelFactory.Close()
方法,还是可以让.NET framework自行处理通道工厂对象。使用
((IChannel)proxy.)调用服务方法后,代理通道将始终关闭。Close()
方法。

是,如果您想创建这样的类—一个静态类来保存所有那些
ChannelFactory
实例—您必须确保该类是100%线程安全的,并且在并发访问时不会出错。我还没有太多地使用.NET4的功能,所以我无法具体评论这些功能,但我绝对建议尽可能安全地使用


至于你的第二个(次要)问题:ChannelFactory本身是一个静态类,因此你不能真正调用它的
.Close()
方法。如果您想询问是否在实际的
IChannel
上调用
.Close()
方法,请再说一遍:是的,尽最大努力做一个好公民,如果可能,关闭这些频道。如果你错过了一个,.NET会处理它-但不要只是把你没用的频道扔到地上继续-自己清理一下!-)

这里有一个我用来处理渠道工厂的助手类:

public class ChannelFactoryManager : IDisposable
{
    private static Dictionary<Type, ChannelFactory> _factories = new Dictionary<Type,ChannelFactory>();
    private static readonly object _syncRoot = new object();

    public virtual T CreateChannel<T>() where T : class
    {
        return CreateChannel<T>("*", null);
    }

    public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class
    {
        return CreateChannel<T>(endpointConfigurationName, null);
    }

    public virtual T CreateChannel<T>(string endpointConfigurationName, string endpointAddress) where T : class
    {
        T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel();
        ((IClientChannel)local).Faulted += ChannelFaulted;
        return local;
    }

    protected virtual ChannelFactory<T> GetFactory<T>(string endpointConfigurationName, string endpointAddress) where T : class
    {
        lock (_syncRoot)
        {
            ChannelFactory factory;
            if (!_factories.TryGetValue(typeof(T), out factory))
            {
                factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress);
                _factories.Add(typeof(T), factory);
            }
            return (factory as ChannelFactory<T>);
        }
    }

    private ChannelFactory CreateFactoryInstance<T>(string endpointConfigurationName, string endpointAddress)
    {
        ChannelFactory factory = null;
        if (!string.IsNullOrEmpty(endpointAddress))
        {
            factory = new ChannelFactory<T>(endpointConfigurationName, new EndpointAddress(endpointAddress));
        }
        else
        {
            factory = new ChannelFactory<T>(endpointConfigurationName);
        }
        factory.Faulted += FactoryFaulted;
        factory.Open();
        return factory;
    }

    private void ChannelFaulted(object sender, EventArgs e)
    {
        IClientChannel channel = (IClientChannel)sender;
        try
        {
            channel.Close();
        }
        catch
        {
            channel.Abort();
        }
        throw new ApplicationException("Exc_ChannelFailure");
    }

    private void FactoryFaulted(object sender, EventArgs args)
    {
        ChannelFactory factory = (ChannelFactory)sender;
        try
        {
            factory.Close();
        }
        catch
        {
            factory.Abort();
        }
        Type[] genericArguments = factory.GetType().GetGenericArguments();
        if ((genericArguments != null) && (genericArguments.Length == 1))
        {
            Type key = genericArguments[0];
            if (_factories.ContainsKey(key))
            {
                _factories.Remove(key);
            }
        }
        throw new ApplicationException("Exc_ChannelFactoryFailure");
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (_syncRoot)
            {
                foreach (Type type in _factories.Keys)
                {
                    ChannelFactory factory = _factories[type];
                    try
                    {
                        factory.Close();
                        continue;
                    }
                    catch
                    {
                        factory.Abort();
                        continue;
                    }
                }
                _factories.Clear();
            }
        }
    }
}

我不喜欢呼叫结构:

WCFServiceInvoker invoker = new WCFServiceInvoker();
var result = invoker.InvokeService<IClaimsService, ICollection<string>>(proxy => proxy.GetStringClaims());
WCFServiceInvoker invoker=new WCFServiceInvoker();
var result=invoker.InvokeService(proxy=>proxy.GetStringClaims());
同样,您不能使用同一频道两次

我创建了以下解决方案:

using(var i = Connection<IClaimsService>.Instance)
{           
   var result = i.Channel.GetStringClaims();
}
使用(var i=Connection.Instance)
{           
var result=i.Channel.GetStringClaims();
}
现在可以重用同一个通道,直到using语句调用dispose

GetChannel方法基本上是一个ChannelFactory.CreateChannel(),我正在使用一些额外的配置

您可以像其他解决方案一样为ChannelFactory构建一些缓存

连接类的代码:

public static class Connection<T>
   {
      public static ChannelHolder Instance
      {
         get
         {
            return new ChannelHolder();
         }
      }

      public class ChannelHolder : IDisposable
      {
         public T Channel { get; set; }

         public ChannelHolder()
         {
            this.Channel = GetChannel();
         }

         public void Dispose()
         {
            IChannel connection = null;
            try
            {
               connection = (IChannel)Channel;
               connection.Close();
            }
            catch (Exception)
            {
               if (connection != null)
               {
                  connection.Abort();
               }
            }
         }
      }
}
公共静态类连接
{
公共静态ChannelHolder实例
{
得到
{
返回新的ChannelHolder();
}
}
公共类频道持有者:IDisposable
{
公共T通道{get;set;}
公共频道持有人()
{
this.Channel=GetChannel();
}
公共空间处置()
{
i通道连接=空;
尝试
{
连接=(IChannel)通道;
connection.Close();
}
捕获(例外)
{
if(连接!=null)
{
connection.Abort();
}
}
}
}
}

@Nelson Rothermel,是的,我沿着 未在ChannelFactoryManager ChannelFaulted事件处理程序中使用try-catch。 因此,我们将成为

 private void ChannelFaulted(object sender, EventArgs e)
    {
        IClientChannel channel = (IClientChannel)sender;            
        channel.Abort();
    }
似乎允许最初的例外出现。 还选择不使用channel.close,因为它似乎会引发异常 因为通道已处于故障状态。 FactoryFaulted事件处理程序可能存在类似问题。
顺便说一句,Darin,代码不错…

对不起,我的意思是关闭ChannelFactory类的实例。字典将包含ChannelFactory的所有实例,但我希望在应用程序不再需要它们时关闭每个工厂。我使用了AppDomain.CurrentDomain.DomainUnload事件,这似乎对我的案例有效。我能够订阅DomainUnload事件,并在ASP.NET应用程序域重新启动时关闭每个通道工厂。不过,我不确定是在.NET4中使用ConcurrentDictionary,还是在同步访问字典的地方使用类似Darrin的工具。谢谢!这很有趣,我需要进一步分析。我有几个问题:你呢
WCFServiceInvoker invoker = new WCFServiceInvoker();
var result = invoker.InvokeService<IClaimsService, ICollection<string>>(proxy => proxy.GetStringClaims());
using(var i = Connection<IClaimsService>.Instance)
{           
   var result = i.Channel.GetStringClaims();
}
public static class Connection<T>
   {
      public static ChannelHolder Instance
      {
         get
         {
            return new ChannelHolder();
         }
      }

      public class ChannelHolder : IDisposable
      {
         public T Channel { get; set; }

         public ChannelHolder()
         {
            this.Channel = GetChannel();
         }

         public void Dispose()
         {
            IChannel connection = null;
            try
            {
               connection = (IChannel)Channel;
               connection.Close();
            }
            catch (Exception)
            {
               if (connection != null)
               {
                  connection.Abort();
               }
            }
         }
      }
}
 private void ChannelFaulted(object sender, EventArgs e)
    {
        IClientChannel channel = (IClientChannel)sender;            
        channel.Abort();
    }