Пользовательская конфигурация службы WCF - PullRequest
6 голосов
/ 17 октября 2008

В приложении, в котором размещены несколько служб 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>

Устанавливает ABC для MyService и DEF для MyOtherService (при условии, что у них есть некоторый общий интерфейс с названием компании).

Может кто-нибудь рассказать, как вы это реализовали?

1012 * ТИА *

Michael

Ответы [ 4 ]

8 голосов
/ 06 марта 2009

Я знаю, что это старое, но оно так и не было помечено как отвеченное, поэтому я решил сделать снимок. Если я понимаю, что вам нужно, вы можете сделать это с помощью пользовательского ServiceHostFactory.
Хороший пост по этому здесь .

Вы настроили свой собственный ServiceHostFactory следующим образом:

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

Затем в ServiceHostFactory вы можете переопределить метод ApplyConfiguration. Обычно для приложений WCF, размещенных в IIS, 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 голосов
/ 18 января 2010

У меня была похожая проблема, но я использовал DuplexChannel. Основываясь на сообщении , я обнаружил, что нашел, что решил так:

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

Я обобщил это в моем блоге

0 голосов
/ 20 октября 2008

Ну, я думал, что пример, который я привел, был довольно сложным. Я постараюсь уточнить дальше ...

По сути, я хочу иметь возможность передавать пользовательские данные конфигурации в службу WCF в приложении, где может быть запущено несколько служб WCF.

Так что это означает, что в экземпляре службы, запущенной в приложении, я хочу получить доступ к данным, которые были настроены специально для этой службы (а не другой службы). Я думаю, что я мог бы сделать это, просто используя настройки приложения и используя тип сервиса в качестве ключа. Я надеялся, что у WCF могут быть более удачные конструкции для этого.

0 голосов
/ 17 октября 2008

Это зависит от того, где и как вы ожидаете использовать упомянутую информацию. Если это не то, что будет много делать с инфраструктурой (то есть запускать службы и обрабатывать запросы), я бы хотел сказать, что попытка вставить это в поведение WCF может добавить больше сложности, чем оно того стоит. Вероятно, было бы проще использовать собственный раздел конфигурации.

Не могли бы вы уточнить, как вы собираетесь использовать эту информацию во время выполнения? Может быть, таким образом мы сможем дать более четкий совет ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...