WCF服务自定义配置

时间:2008-10-17 04:59:11

标签: .net wcf .net-3.5

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

我猜这可能是通过实现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

迈克尔

4 个答案:

答案 0 :(得分:8)

我知道这是旧的,但它从来没有标记过,所以我想我会开枪。如果我理解你所追求的是什么,你可以使用自定义的ServiceHostFactory来实现 关于这个here的好帖子。

您可以设置yuour自定义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");
}

答案 1 :(得分:3)

我遇到了类似的问题,但我使用的是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 :(得分:0)

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

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

答案 3 :(得分:0)

好吧,我认为我给出的例子非常精细。我会尝试进一步阐述......

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

那么这意味着在应用程序中运行的服务实例中我想要访问专门为该服务(而不是其他服务)配置的数据。我想我可以通过使用应用程序设置并使用服务类型作为密钥来完成此操作。我希望WCF可能有更好的构造。