简体   繁体   English

WCF服务自定义配置

[英]WCF Service Custom Configuration

In an application that is hosting several WCF services, what would be the best way to add custom configuration information for each service? 在托管多个WCF服务的应用程序中,为每个服务添加自定义配置信息的最佳方法是什么? For example you may want to pass or set a company name or specify the connectionString a service or some other parameter. 例如,您可能希望传递或设置公司名称或指定connectionString服务或其他一些参数。

I'm guessing this might be possible by implementing IServiceBehavior. 我猜这可能是通过实现IServiceBehavior实现的。

ie something like.... 即...像....

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

Would set ABC on MyService and DEF on MyOtherService (assuming they have some common interface with a company name). 在MyService上设置ABC,在MyOtherService上设置DEF(假设它们与公司名称有一些共同的接口)。

Can anyone elaborate on how you implement this? 任何人都可以详细说明你如何实现这个?

TIA TIA

Michael 迈克尔

I know this is old, but it was never marked answered, so I thought I'd take a shot. 我知道这是旧的,但它从未被标记为已回答,所以我想我会开枪。 If I understand what you're after, you can do it with a custom ServiceHostFactory. 如果我理解你所追求的是什么,你可以使用自定义的ServiceHostFactory来实现。
Good post on this here . 好后在这个位置

You set up yuour custom ServiceHostFactory like so: 您设置yuour自定义ServiceHostFactory,如下所示:

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

Then, in your ServiceHostFactory, you can override a method called ApplyConfiguration. 然后,在ServiceHostFactory中,您可以覆盖名为ApplyConfiguration的方法。 Normally for WCF apps hosted in IIS, WCF would automatically look for config in web.config. 通常,对于在IIS中托管的WCF应用程序,WCF会自动在web.config中查找配置。 In this example, we override that behavior to first look for a config file named after the WCF Service Description. 在此示例中,我们重写该行为以首先查找以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);
}

You could replace this with "anything" - for example, looking for config in a database table. 您可以用“任何东西”替换它 - 例如,在数据库表中查找配置。

A few more methods complete the puzzle. 还有一些方法可以完成拼图。

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

I was having a similar issue, but I was using DuplexChannel. 我遇到了类似的问题,但我使用的是DuplexChannel。 Based on a post i found I found I solved this way: 根据我发现的帖子,我发现我解决了这个问题:

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

I summarized this in my blog 我在博客中对此进行了总结

It depends a lot of where and how you expect to use said information. 这取决于您希望使用所述信息的地点和方式。 If it's not something that's going to do a lot with the infrastructure (ie getting the services to run and processing requests), I'd be tempted to say that trying to push that into the WCF behaviors might be adding more complexity than it's worth. 如果它不会对基础设施做很多事情(即让服务运行和处理请求),我很想说尝试将其推入WCF行为可能会增加更多的复杂性而不是它的价值。 It would probably be simpler to just use a custom configuration section of your own. 使用您自己的自定义配置部分可能更简单。

Could you clarify how you expect to use this information at runtime? 您能否澄清一下您希望如何在运行时使用此信息? Maybe that way we can provide more explicit advice... 也许这样我们可以提供更明确的建议......

Well, I thought the example I've given was pretty elaborate. 好吧,我认为我给出的例子非常精细。 I'll try to elaborate further... 我会尝试进一步阐述......

Basically I want to be able to pass custom configuration data to a WCF service in an application where there may be several WCF services running. 基本上我希望能够将自定义配置数据传递到可能运行多个WCF服务的应用程序中的WCF服务。

So what that means is in the an instance of a service running in the application I want to access data that has been configured specifically for that service (and not another service). 那么这意味着在应用程序中运行的服务的实例中,我想要访问专门为该服务(而不是其他服务)配置的数据。 I guess I could do this simply by using the application settings and using the service's type as the key. 我想我可以通过使用应用程序设置并使用服务类型作为密钥来完成此操作。 I was hoping WCF might have some better constructs for this. 我希望WCF可能有更好的构造。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM