【问题标题】:WCF Service Custom ConfigurationWCF 服务自定义配置
【发布时间】:2010-09-17 16:32:10
【问题描述】:

在托管多个 WCF 服务的应用程序中,为每个服务添加自定义配置信息的最佳方式是什么?例如,您可能想要传递或设置公司名称或指定连接字符串服务或其他一些参数。

我猜这可能通过实现 IServiceBehavior 来实现。

即类似....

<behaviors>
  <serviceBehaviors>
    <behavior name="MyBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug />
      <customBehavior myCompany="ABC" />
    </behavior>
    <behavior name="MyOtherBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug />
      <customBehavior myCompany="DEF" />
    </behavior>
  </serviceBehaviors>
</behaviors>

<services>
  <service behaviorConfiguration="MyBehavior" name="MyNameSpace.MyService">
    <endpoint address="" behaviorConfiguration="" binding="netTcpBinding" 
      name="TcpEndpoint" contract="MyNameSpace.IMyService" />
    <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
      name="TcpMexEndpoint" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="net.tcp://localhost:4000/MyService" />
      </baseAddresses>
    </host>
  </service>
  <service behaviorConfiguration="MyOtherBehavior" name="MyNameSpace.MyOtherService">
    <endpoint address="" behaviorConfiguration="" binding="netTcpBinding" 
      name="TcpEndpoint" contract="MyNameSpace.IMyOtherService" />
    <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
      name="TcpMexEndpoint" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="net.tcp://localhost:4000/MyOtherService" />
      </baseAddresses>
    </host>
  </service>
</services>

会在 MyService 上设置 ABC,在 MyOtherService 上设置 DEF(假设它们有一些带有公司名称的通用接口)。

谁能详细说明你是如何实现的?

TIA

迈克尔

【问题讨论】:

    标签: .net wcf .net-3.5


    【解决方案1】:

    我遇到了类似的问题,但我使用的是 DuplexChannel。 基于post,我发现我发现我是这样解决的:

    public class CustomDuplexChannelFactory<TChannel> : DuplexChannelFactory<TChannel>
    {
        public static string ConfigurationPath { get; set; }
    
        public CustomDuplexChannelFactory(InstanceContext callbackInstance)
            : base(callbackInstance)
        {
        }
    
        protected override ServiceEndpoint CreateDescription()
        {
            ServiceEndpoint serviceEndpoint = base.CreateDescription();
    
            if(ConfigurationPath == null || !File.Exists(ConfigurationPath))
                return base.CreateDescription();
    
            ExeConfigurationFileMap executionFileMap = new ExeConfigurationFileMap();
            executionFileMap.ExeConfigFilename = ConfigurationPath;
            System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(executionFileMap, ConfigurationUserLevel.None);
            ServiceModelSectionGroup serviceModeGroup = ServiceModelSectionGroup.GetSectionGroup(config);
            ChannelEndpointElement selectedEndpoint = null;
            foreach(ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints)
            {
                if(endpoint.Contract == serviceEndpoint.Contract.ConfigurationName)
                {
                    selectedEndpoint = endpoint; break;
                }
            }
            if(selectedEndpoint != null)
            {
                if(serviceEndpoint.Binding == null)
                {
                    serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, serviceModeGroup);
                } if(serviceEndpoint.Address == null)
                {
                    serviceEndpoint.Address = new EndpointAddress(selectedEndpoint.Address,
                    GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers);
                } if(serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration))
                {
                    AddBehaviors(selectedEndpoint.BehaviorConfiguration,
                        serviceEndpoint, serviceModeGroup);
                }
                serviceEndpoint.Name = selectedEndpoint.Contract;
            }
            return serviceEndpoint;
        }
    
        private Binding CreateBinding(string bindingName, ServiceModelSectionGroup group)
        {
            BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];
            if(bindingElementCollection.ConfiguredBindings.Count > 0)
            {
                IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0];
                Binding binding = GetBinding(be); if(be != null)
                {
                    be.ApplyConfiguration(binding);
                }
                return binding;
            }
            return null;
        }
    
        private void AddBehaviors(string behaviorConfiguration, ServiceEndpoint serviceEndpoint, ServiceModelSectionGroup group)
        {
            EndpointBehaviorElement behaviorElement = group.Behaviors.EndpointBehaviors[behaviorConfiguration];
            for(int i = 0; i < behaviorElement.Count; i++)
            {
                BehaviorExtensionElement behaviorExtension = behaviorElement[i];
                object extension = behaviorExtension.GetType().InvokeMember("CreateBehavior", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, behaviorExtension, null);
                if(extension != null)
                {
                    serviceEndpoint.Behaviors.Add((IEndpointBehavior)extension);
                }
            }
        }
    
        private EndpointIdentity GetIdentity(IdentityElement element)
        {
            EndpointIdentity identity = null;
            PropertyInformationCollection properties = element.ElementInformation.Properties;
            if(properties["userPrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value);
            }
            if(properties["servicePrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value);
            }
            if(properties["dns"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateDnsIdentity(element.Dns.Value);
            }
            if(properties["rsa"].ValueOrigin != PropertyValueOrigin.Default)
            {
                return EndpointIdentity.CreateRsaIdentity(element.Rsa.Value);
            }
            if(properties["certificate"].ValueOrigin != PropertyValueOrigin.Default)
            {
                X509Certificate2Collection supportingCertificates = new X509Certificate2Collection();
                supportingCertificates.Import(Convert.FromBase64String(element.Certificate.EncodedValue));
    
                if(supportingCertificates.Count == 0)
                {
                    throw new InvalidOperationException("UnableToLoadCertificateIdentity");
                }
    
                X509Certificate2 primaryCertificate = supportingCertificates[0]; supportingCertificates.RemoveAt(0);
                return EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportingCertificates);
            }
            return identity;
        }
    
        private Binding GetBinding(IBindingConfigurationElement configurationElement)
        {
            if(configurationElement is CustomBindingElement)
                return new CustomBinding();
            else if(configurationElement is BasicHttpBindingElement)
                return new BasicHttpBinding();
            else if(configurationElement is NetMsmqBindingElement)
                return new NetMsmqBinding();
            else if(configurationElement is NetNamedPipeBindingElement)
                return new NetNamedPipeBinding();
            else if(configurationElement is NetPeerTcpBindingElement)
                return new NetPeerTcpBinding();
            else if(configurationElement is NetTcpBindingElement)
                return new NetTcpBinding();
            else if(configurationElement is WSDualHttpBindingElement)
                return new WSDualHttpBinding();
            else if(configurationElement is WSHttpBindingElement)
                return new WSHttpBinding();
            else if(configurationElement is WSFederationHttpBindingElement)
                return new WSFederationHttpBinding();
            return null;
        }
    }
    

    我在blog中总结了这一点

    【讨论】:

      【解决方案2】:

      我知道这是旧的,但它从未被标记为已回答,所以我想我会试一试。如果我了解您的需求,您可以使用自定义 ServiceHostFactory。
      关于这个here 的好帖子。

      您像这样设置您的自定义 ServiceHostFactory:

      <%@ ServiceHost
       Language="C#"
       Debug="true"
       Service="Ionic.Samples.Webservices.Sep20.CustomConfigService"
       Factory="Ionic.ServiceModel.ServiceHostFactory"%>
      

      然后,在您的 ServiceHostFactory 中,您可以覆盖一个名为 ApplyConfiguration 的方法。通常对于托管在 IIS 中的 WCF 应用程序,WCF 会自动在 web.config 中查找配置。在此示例中,我们覆盖该行为以首先查找以 WCF 服务描述命名的配置文件。

      protected override void ApplyConfiguration()
      {
          // generate the name of the custom configFile, from the service name:
          string configFilename = System.IO.Path.Combine ( physicalPath,
              String.Format("{0}.config", this.Description.Name));
      
          if (string.IsNullOrEmpty(configFilename) || !System.IO.File.Exists(configFilename))
              base.ApplyConfiguration();
          else
              LoadConfigFromCustomLocation(configFilename);
      }
      

      您可以将其替换为“任何内容” - 例如,在数据库表中查找配置。

      还有一些方法可以完成这个难题。

      private string _physicalPath = null;
      private string physicalPath
      {
          get
          {
              if (_physicalPath == null)
              {
                  // if hosted in IIS
                  _physicalPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
      
                  if (String.IsNullOrEmpty(_physicalPath))
                  {
                      // for hosting outside of IIS
                      _physicalPath= System.IO.Directory.GetCurrentDirectory();
                  }
              }
              return _physicalPath;
          }
      }
      
      
      private void LoadConfigFromCustomLocation(string configFilename)
      {
          var filemap = new System.Configuration.ExeConfigurationFileMap();
          filemap.ExeConfigFilename = configFilename;
          System.Configuration.Configuration config =
              System.Configuration.ConfigurationManager.OpenMappedExeConfiguration
              (filemap,
               System.Configuration.ConfigurationUserLevel.None);
          var serviceModel = System.ServiceModel.Configuration.ServiceModelSectionGroup.GetSectionGroup(config);
          bool loaded= false;
          foreach (System.ServiceModel.Configuration.ServiceElement se in serviceModel.Services.Services)
          {
              if(!loaded)
                  if (se.Name == this.Description.ConfigurationName)
                  {
                      base.LoadConfigurationSection(se);
                      loaded= true;
                  }
          }
      
          if (!loaded)
              throw new ArgumentException("ServiceElement doesn't exist");
      }
      

      【讨论】:

        【解决方案3】:

        嗯,我认为我给出的示例非常详尽。我会尝试进一步详细说明...

        基本上,我希望能够将自定义配置数据传递给可能有多个 WCF 服务正在运行的应用程序中的 WCF 服务。

        所以这意味着在应用程序中运行的服务实例中,我想要访问专门为该服务(而不是其他服务)配置的数据。我想我可以简单地通过使用应用程序设置并使用服务的类型作为键来做到这一点。我希望 WCF 可以为此提供一些更好的构造。

        【讨论】:

          【解决方案4】:

          这在很大程度上取决于您希望在何处以及如何使用所述信息。如果它不会对基础设施做很多事情(即让服务运行和处理请求),我很想说尝试将其推入 WCF 行为可能会增加比其价值更多的复杂性。只使用您自己的自定义配置部分可能会更简单。

          您能否说明您希望在运行时如何使用这些信息?也许这样我们可以提供更明确的建议......

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-05
            相关资源
            最近更新 更多