fix(wcf): set Via via CreateChannel(address, via) — ClientViaBehavior absent in .NET WCF

ClientViaBehavior is a .NET Framework type not present in the System.ServiceModel
client libraries. Use the portable ChannelFactory.CreateChannel(EndpointAddress, Uri)
overload instead, via a CreateChannel helper applied at the history-open and
retrieval-query sites (the critical event path). Fixes the build break in 954b9cc.

Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
Joseph Doherty
2026-06-25 20:37:38 -04:00
parent 954b9cc9cc
commit 8777c0b816
3 changed files with 19 additions and 10 deletions
@@ -41,7 +41,7 @@ internal static class HistorianWcfAuthChainHelper
try try
{ {
IHistoryServiceContract2 historyChannel = historyFactory.CreateChannel(); IHistoryServiceContract2 historyChannel = HistorianWcfClientCredentialsHelper.CreateChannel(historyFactory, options);
ICommunicationObject historyChannelCo = (ICommunicationObject)historyChannel; ICommunicationObject historyChannelCo = (ICommunicationObject)historyChannel;
try try
{ {
@@ -2,7 +2,6 @@ using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens; using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.ServiceModel; using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security; using System.ServiceModel.Security;
namespace AVEVA.Historian.Client.Wcf; namespace AVEVA.Historian.Client.Wcf;
@@ -29,14 +28,24 @@ internal static class HistorianWcfClientCredentialsHelper
RevocationMode = X509RevocationMode.NoCheck, RevocationMode = X509RevocationMode.NoCheck,
}; };
} }
}
// Tunnel/proxy support: connect to the Via address while still addressing the message To the /// <summary>
// logical endpoint (Host/Port). Lets a port-forward whose local port differs from the server's /// Creates a channel from <paramref name="factory"/>, honoring
// real service port satisfy the server-side WCF AddressFilter (which checks the To header). /// <see cref="HistorianClientOptions.ConnectViaAddress"/> when set: the channel <b>connects</b> to
if (!string.IsNullOrWhiteSpace(options.ConnectViaAddress)) /// the Via address while still addressing the SOAP message <c>To</c> the factory's logical endpoint.
{ /// This lets a port-forward whose local port differs from the server's real service port satisfy the
factory.Endpoint.EndpointBehaviors.Add(new ClientViaBehavior(new Uri(options.ConnectViaAddress))); /// server-side WCF AddressFilter (which validates the To header). Use this in place of
} /// <c>factory.CreateChannel()</c> at every WCF event/read channel-creation site.
/// </summary>
public static TChannel CreateChannel<TChannel>(ChannelFactory<TChannel> factory, HistorianClientOptions options)
{
ArgumentNullException.ThrowIfNull(factory);
ArgumentNullException.ThrowIfNull(options);
return string.IsNullOrWhiteSpace(options.ConnectViaAddress)
? factory.CreateChannel()
: factory.CreateChannel(factory.Endpoint.Address, new Uri(options.ConnectViaAddress));
} }
private sealed class AcceptAnyCertificateValidator : X509CertificateValidator private sealed class AcceptAnyCertificateValidator : X509CertificateValidator
@@ -167,7 +167,7 @@ internal sealed class HistorianWcfEventOrchestrator
try try
{ {
IRetrievalServiceContract4 channel = factory.CreateChannel(); IRetrievalServiceContract4 channel = HistorianWcfClientCredentialsHelper.CreateChannel(factory, _options);
ICommunicationObject channelCo = (ICommunicationObject)channel; ICommunicationObject channelCo = (ICommunicationObject)channel;
try try
{ {