以声明方式在以编程方式构建的终结点上配置WCF行为扩展

8
我有一个WCF行为扩展,我想将其添加到WCF客户端。但是,客户端是通过编程构建的。端点地址可能会变化,但我知道类型。我可以通过程序或配置文件(首选)添加行为,但我需要仅在配置文件中传递一些配置。
我不希望将其放在通用行为(machine.config)中。
我可以通过编程方式添加行为。
endpoint.Behaviors.Add(new MyCustomBehavior())

但我宁愿在配置文件中进行,这样我也可以在那里配置扩展。

是否可能通过声明方式向程序化构建的端点添加和配置端点行为扩展,仅知道类型或接口而将客户端端点留给以编程方式构建?

<system.serviceModel>
  <client>
    <!-- Created programmatically -->
  </client>
<extensions>
  <behaviorExtensions>
    <add name="MyCustomBehavior" type="namespace.CustomBehaviors", MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </behaviorExtensions>   
</extensions>
  <behaviors>
    <endpointBehaviors>
      <behavior name="MyCustomBehavior">
        <MyCustomBehavior MyImportantBehaviorParam1="foo"  />
      </behavior>
    </endpointBehaviors>   
  </behaviors>
</system.serviceModel>

当然我可以把配置放在另一个部分,并让我的行为在那里读取它,但如果可能的话,我宁愿使用WCF工具。

1个回答

12

要实现这一点,您需要为端点创建一个行为配置扩展。有关如何执行此操作的更多信息,请查看https://learn.microsoft.com/en-us/archive/blogs/carlosfigueira/wcf-extensibility-behavior-configuration-extensions

更新:我现在看到了您的问题。没有直接的方法来向通过代码创建的端点中添加在配置中声明的行为。不过,您仍然可以这样做,但是需要使用一些反射方法来访问行为配置扩展的CreateBehavior方法(该方法是受保护的),以实际创建端点行为并将其添加到通过代码创建的端点中。下面的代码展示了如何实现此操作。

public class StackOverflow_10232385
{
    public class MyCustomBehavior : IEndpointBehavior
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
            Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
            Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
            Console.WriteLine("In {0}.{1}", this.GetType().Name, MethodBase.GetCurrentMethod().Name);
        }
    }

    public class MyCustomBehaviorExtension : BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(MyCustomBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new MyCustomBehavior();
        }
    }

    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        string Echo(string text);
    }
    public class Service : ITest
    {
        public string Echo(string text)
        {
            return text;
        }
    }

    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");

        var configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        ServiceModelSectionGroup smsg = configuration.GetSectionGroup("system.serviceModel") as ServiceModelSectionGroup;
        EndpointBehaviorElement endpointBehaviorElement = smsg.Behaviors.EndpointBehaviors["MyCustomBehavior_10232385"];
        foreach (BehaviorExtensionElement behaviorElement in endpointBehaviorElement)
        {
            MethodInfo createBehaviorMethod = behaviorElement.GetType().GetMethod("CreateBehavior", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Type.EmptyTypes, null);
            IEndpointBehavior behavior = createBehaviorMethod.Invoke(behaviorElement, new object[0]) as IEndpointBehavior;
            endpoint.Behaviors.Add(behavior);
        }

        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
        ITest proxy = factory.CreateChannel();
        Console.WriteLine(proxy.Echo("Hello"));

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

而这段代码的配置如下:

<system.serviceModel>
    <extensions>
        <behaviorExtensions>
            <add name="myCustomBehavior_10232385" type="QuickCode1.StackOverflow_10232385+MyCustomBehaviorExtension, QuickCode1"/>
        </behaviorExtensions>
    </extensions>
    <behaviors>
        <endpointBehaviors>
            <behavior name="MyCustomBehavior_10232385">
                <myCustomBehavior_10232385/>
            </behavior>
        </endpointBehaviors>
    </behaviors>
</system.serviceModel>

我的行为已经实现了行为扩展,注意<endpointBehaviors>中的参数...如果我的问题不够清晰,那我很抱歉。在我能找到的所有示例中,behaviorConfiguration都被添加到声明式构建的端点(服务或客户端)中。我需要知道如何将其添加到以编程方式创建的具有给定契约的端点中。 - DanO
明白了,之前我没理解。我已经更新了答案,并且你可以在https://github.com/carlosfigueira/WCFQuickSamples/tree/master/WCFForums/QuickCode1找到完整的代码。 - carlosfigueira
感谢您花费精力提供解决方法!我已经放弃了自己想做的事情,选择了更简单的方式,但如果我重新开始这个项目,您的代码将非常有帮助! - DanO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接