代码之家  ›  专栏  ›  技术社区  ›  Eric

创建wcf channelfactory<t>

  •  44
  • Eric  · 技术社区  · 15 年前

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

    在WCF世界中,这类似于创建每个呼叫服务并使用 ChannelFactory<T> 创建代理。我正在为如何正确地创建 渠道工厂 对于ASP.NET客户端。

    出于性能原因,我要缓存 渠道工厂 对象,每次调用服务时只创建通道。在.NET远程处理时代,以前 RemotingConfiguration.GetRegisteredWellknownClientTypes() 方法获取一组客户端对象,然后我可以缓存这些对象。看起来,在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;
        }    
    }
    

    我认为上面的代码可以工作,但是我有点担心多个线程试图添加新的 渠道工厂 对象(如果它不在查找中)。因为我在使用.NET 4.0,所以我考虑使用 ConcurrentDictionary 使用 GetOrAdd() 方法或用途 TryGetValue() 方法首先检查 渠道工厂 存在而不存在,则使用 GATORADD() 方法。但对性能不确定 ConcurrentDictionary.TryGetValue() ConcurrentDictionary.GetOrAdd() 方法。

    另一个小问题是我是否需要打电话 ChannelFactory.Close() 方法在ASP.NET应用程序结束后处理通道工厂对象,或者我可以让.NET框架自行处理通道工厂对象。在使用 ((IChannel)proxy).Close() 方法。

    4 回复  |  直到 9 年前
        1
  •  13
  •   marc_s    15 年前

    是的,如果你想创建这样的东西-一个静态类来保存所有这些 ChannelFactory<T> 实例-您一定要确保这个类是100%线程安全的,并且在并发访问时不会出错。我还没有使用过.NET 4的很多功能,所以我不能具体评论这些功能——但我绝对建议尽可能安全地使用这些功能。

    至于第二个(次要)问题:channelFactory本身是一个静态类,因此不能真正调用 .Close() 方法。如果你想问是否打电话给 关闭() 实际的方法 IChannel 再次强调:是的,尽你最大的努力成为一个好公民,如果你能的话,关闭那些渠道。如果你错过了一个,.net会处理它-但不要只是把你未使用的频道扔到地板上继续-自己清理!-)

        2
  •  64
  •   Darin Dimitrov    14 年前

    下面是一个我用来处理通道工厂的助手类:

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

    然后我定义一个服务调用程序:

    public interface IServiceInvoker
    {
        R InvokeService<T, R>(Func<T, R> invokeHandler) where T: class;
    }
    

    以及一个实现:

    public class WCFServiceInvoker : IServiceInvoker
    {
        private static ChannelFactoryManager _factoryManager = new ChannelFactoryManager();
        private static ClientSection _clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
    
        public R InvokeService<T, R>(Func<T, R> invokeHandler) where T : class
        {
            var endpointNameAddressPair = GetEndpointNameAddressPair(typeof(T));
            T arg = _factoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);
            ICommunicationObject obj2 = (ICommunicationObject)arg;
            try
            {
                return invokeHandler(arg);
            }
            finally
            {
                try
                {
                    if (obj2.State != CommunicationState.Faulted)
                    {
                        obj2.Close();
                    }
                }
                catch
                {
                    obj2.Abort();
                }
            }
        }
    
        private KeyValuePair<string, string> GetEndpointNameAddressPair(Type serviceContractType)
        {
            var configException = new ConfigurationErrorsException(string.Format("No client endpoint found for type {0}. Please add the section <client><endpoint name=\"myservice\" address=\"http://address/\" binding=\"basicHttpBinding\" contract=\"{0}\"/></client> in the config file.", serviceContractType));
            if (((_clientSection == null) || (_clientSection.Endpoints == null)) || (_clientSection.Endpoints.Count < 1))
            {
                throw configException;
            }
            foreach (ChannelEndpointElement element in _clientSection.Endpoints)
            {
                if (element.Contract == serviceContractType.ToString())
                {
                    return new KeyValuePair<string, string>(element.Name, element.Address.AbsoluteUri);
                }
            }
            throw configException;
        }
    
    }
    

    现在,每次需要调用WCF服务时,您都可以使用此功能:

    WCFServiceInvoker invoker = new WCFServiceInvoker();
    SomeReturnType result = invoker.InvokeService<IMyServiceContract, SomeReturnType>(
        proxy => proxy.SomeMethod()
    );
    

    这假定您已经为 IMyServiceContract 配置文件中的服务约定:

    <client>
        <endpoint 
            name="myservice" 
            address="http://example.com/" 
            binding="basicHttpBinding" 
            contract="IMyServiceContract" />
    </client>
    
        3
  •  2
  •   Max    14 年前

    我不喜欢呼叫结构:

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

    现在可以重用同一个通道,直到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();
                   }
                }
             }
          }
    }
    
        4
  •  0
  •   PhilMc    11 年前

    @Nelsonrothermel,是的,我沿着 未在ChannelFactoryManager ChannelFaulted事件处理程序中使用Try Catch。 所以通道故障会变成

     private void ChannelFaulted(object sender, EventArgs e)
        {
            IClientChannel channel = (IClientChannel)sender;            
            channel.Abort();
        }
    

    似乎允许最初的异常冒泡。 还选择不使用channel.close,因为它似乎引发了异常 因为通道已经处于故障状态。 FactoryFaulted事件处理程序可能有类似的问题。 btw@darin,代码不错…