[WCF] NetFx client bytecode instrumentation (#2541)
This commit is contained in:
parent
22139a12a4
commit
38f438e5f9
|
|
@ -48,3 +48,4 @@ GRPCNETCLIENT
|
|||
ENTITYFRAMEWORKCORE
|
||||
appcmd
|
||||
inetsrv
|
||||
WCFCLIENT
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ due to lack of stable semantic convention.
|
|||
| `SQLCLIENT` | [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient) and [System.Data.SqlClient](https://www.nuget.org/packages/System.Data.SqlClient) | * \[3\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `STACKEXCHANGEREDIS` | [StackExchange.Redis](https://www.nuget.org/packages/StackExchange.Redis) **Not supported on .NET Framework** | ≥2.0.405 < 3.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `WCF` | [System.ServiceModel](https://www.nuget.org/packages/System.ServiceModel) **No support for server side on .NET**. For configuration see [WCF Instrumentation Configuration](wcf-config.md) | * \[4\] | source | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
| `WCFCLIENT` | [System.ServiceModel](https://www.nuget.org/packages/System.ServiceModel) **Supported on .NET Framework**. | ≥4.0.0.0 < 5.0.0.0 | source & bytecode | [Experimental](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/versioning-and-stability.md) |
|
||||
|
||||
\[1\]: Only integrated pipeline mode is supported.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,44 +4,6 @@
|
|||
|
||||
## WCF Client Configuration (.NET Framework)
|
||||
|
||||
Add the `IClientMessageInspector` instrumentation via a behavior extension on
|
||||
the clients you want to instrument:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<system.serviceModel>
|
||||
<extensions>
|
||||
<behaviorExtensions>
|
||||
<add name="telemetryExtension" type="OpenTelemetry.Instrumentation.Wcf.TelemetryEndpointBehaviorExtensionElement, OpenTelemetry.Instrumentation.Wcf" />
|
||||
</behaviorExtensions>
|
||||
</extensions>
|
||||
<behaviors>
|
||||
<endpointBehaviors>
|
||||
<behavior name="telemetry">
|
||||
<telemetryExtension />
|
||||
</behavior>
|
||||
</endpointBehaviors>
|
||||
</behaviors>
|
||||
<bindings>
|
||||
<basicHttpBinding>
|
||||
<binding name="basicHttpConfig">
|
||||
<security mode="None" />
|
||||
</binding>
|
||||
</basicHttpBinding>
|
||||
<netTcpBinding>
|
||||
<binding name="netTCPConfig">
|
||||
<security mode="None" />
|
||||
</binding>
|
||||
</netTcpBinding>
|
||||
</bindings>
|
||||
<client>
|
||||
<endpoint address="http://localhost:9009/Telemetry" binding="basicHttpBinding" bindingConfiguration="basicHttpConfig" behaviorConfiguration="telemetry" contract="TestApplication.Wcf.Client.NetFramework.IStatusServiceContract" name="StatusService_Http" />
|
||||
</client>
|
||||
</system.serviceModel>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
Example project available in
|
||||
[test/test-applications/integrations/TestApplication.Wcf.Client.NetFramework](../test/test-applications/integrations/TestApplication.Wcf.Client.NetFramework/)
|
||||
folder.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Auto-generated file, do not change it - generated by the IntegrationsJsonGenerator
|
||||
#ifndef BYTECODE_INSTRUMENTATIONS_H
|
||||
#define BYTECODE_INSTRUMENTATIONS_H
|
||||
|
||||
|
|
@ -11,7 +10,16 @@
|
|||
|
||||
namespace trace
|
||||
{
|
||||
inline std::unordered_map<WSTRING, WSTRING> trace_integration_names = {{WStr("StrongNamedValidation"), WStr("OTEL_DOTNET_AUTO_TRACES_STRONGNAMEDVALIDATION_INSTRUMENTATION_ENABLED")}, {WStr("StackExchangeRedis"), WStr("OTEL_DOTNET_AUTO_TRACES_STACKEXCHANGEREDIS_INSTRUMENTATION_ENABLED")}, {WStr("NServiceBus"), WStr("OTEL_DOTNET_AUTO_TRACES_NSERVICEBUS_INSTRUMENTATION_ENABLED")}, {WStr("MySqlData"), WStr("OTEL_DOTNET_AUTO_TRACES_MYSQLDATA_INSTRUMENTATION_ENABLED")}, {WStr("MongoDB"), WStr("OTEL_DOTNET_AUTO_TRACES_MONGODB_INSTRUMENTATION_ENABLED")}, {WStr("GraphQL"), WStr("OTEL_DOTNET_AUTO_TRACES_GRAPHQL_INSTRUMENTATION_ENABLED")}};
|
||||
inline std::unordered_map<WSTRING, WSTRING> trace_integration_names =
|
||||
{
|
||||
{WStr("StrongNamedValidation"), WStr("OTEL_DOTNET_AUTO_TRACES_STRONGNAMEDVALIDATION_INSTRUMENTATION_ENABLED")},
|
||||
{WStr("StackExchangeRedis"), WStr("OTEL_DOTNET_AUTO_TRACES_STACKEXCHANGEREDIS_INSTRUMENTATION_ENABLED")},
|
||||
{WStr("NServiceBus"), WStr("OTEL_DOTNET_AUTO_TRACES_NSERVICEBUS_INSTRUMENTATION_ENABLED")},
|
||||
{WStr("MySqlData"), WStr("OTEL_DOTNET_AUTO_TRACES_MYSQLDATA_INSTRUMENTATION_ENABLED")},
|
||||
{WStr("MongoDB"), WStr("OTEL_DOTNET_AUTO_TRACES_MONGODB_INSTRUMENTATION_ENABLED")},
|
||||
{WStr("GraphQL"), WStr("OTEL_DOTNET_AUTO_TRACES_GRAPHQL_INSTRUMENTATION_ENABLED")},
|
||||
{WStr("WcfClient"), WStr("OTEL_DOTNET_AUTO_TRACES_WCFCLIENT_INSTRUMENTATION_ENABLED")}
|
||||
};
|
||||
inline std::unordered_map<WSTRING, WSTRING> metric_integration_names = {{WStr("NServiceBus"), WStr("OTEL_DOTNET_AUTO_METRICS_NSERVICEBUS_INSTRUMENTATION_ENABLED")}};
|
||||
inline std::unordered_map<WSTRING, WSTRING> log_integration_names = {{WStr("ILogger"), WStr("OTEL_DOTNET_AUTO_LOGS_ILOGGER_INSTRUMENTATION_ENABLED")}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ OpenTelemetry.AutoInstrumentation.Instrumentations.NServiceBus.EndpointConfigura
|
|||
OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegration
|
||||
OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync
|
||||
OpenTelemetry.AutoInstrumentation.Instrumentations.Validations.StrongNamedValidation
|
||||
OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.WcfClientIntegration
|
||||
override OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetReturn<T>.ToString() -> string!
|
||||
override OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetState.ToString() -> string!
|
||||
static OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetInvoker.BeginMethod<TIntegration, TTarget, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8>(TTarget instance, ref TArg1 arg1, ref TArg2 arg2, ref TArg3 arg3, ref TArg4 arg4, ref TArg5 arg5, ref TArg6 arg6, ref TArg7 arg7, ref TArg8 arg8) -> OpenTelemetry.AutoInstrumentation.CallTarget.CallTargetState
|
||||
|
|
|
|||
|
|
@ -106,6 +106,12 @@ internal enum TracerInstrumentation
|
|||
/// <summary>
|
||||
/// ASP.NET Core instrumentation.
|
||||
/// </summary>
|
||||
AspNetCore = 15
|
||||
AspNetCore = 15,
|
||||
#endif
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// WcfClient instrumentation.
|
||||
/// </summary>
|
||||
WcfClient = 16,
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,12 @@ internal static partial class InstrumentationDefinitions
|
|||
new("StackExchangeRedis", "Trace", "StackExchange.Redis", "StackExchange.Redis.ConnectionMultiplexer", "ConnectImplAsync", new[] { "System.Threading.Tasks.Task`1<StackExchange.Redis.ConnectionMultiplexer>", "System.Object", "System.IO.TextWriter" }, 2, 0, 0, 2, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync"),
|
||||
new("StackExchangeRedis", "Trace", "StackExchange.Redis", "StackExchange.Redis.ConnectionMultiplexer", "ConnectImplAsync", new[] { "System.Threading.Tasks.Task`1<StackExchange.Redis.ConnectionMultiplexer>", "StackExchange.Redis.ConfigurationOptions", "System.IO.TextWriter" }, 2, 0, 0, 2, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync"),
|
||||
new("StackExchangeRedis", "Trace", "StackExchange.Redis", "StackExchange.Redis.ConnectionMultiplexer", "ConnectImplAsync", new[] { "System.Threading.Tasks.Task`1<StackExchange.Redis.ConnectionMultiplexer>", "StackExchange.Redis.ConfigurationOptions", "System.IO.TextWriter", "System.Nullable`1[StackExchange.Redis.ServerType]" }, 2, 0, 0, 2, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.StackExchangeRedis.StackExchangeRedisIntegrationAsync"),
|
||||
|
||||
// WcfClient
|
||||
new("WcfClient", "Trace", "System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] { "System.Void", "System.String", "System.ServiceModel.EndpointAddress" }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.WcfClientIntegration"),
|
||||
new("WcfClient", "Trace", "System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] { "System.Void", "System.String", "System.ServiceModel.EndpointAddress", "System.Configuration.Configuration" }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.WcfClientIntegration"),
|
||||
new("WcfClient", "Trace", "System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] { "System.Void", "System.ServiceModel.Description.ServiceEndpoint" }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.WcfClientIntegration"),
|
||||
new("WcfClient", "Trace", "System.ServiceModel", "System.ServiceModel.ChannelFactory", "InitializeEndpoint", new[] { "System.Void", "System.ServiceModel.Channels.Binding", "System.ServiceModel.EndpointAddress" }, 4, 0, 0, 4, 65535, 65535, assemblyFullName, "OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf.WcfClientIntegration"),
|
||||
};
|
||||
|
||||
// TODO: Generate this list using source generators
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// <copyright file="WcfClientConstants.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
#if NETFRAMEWORK
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf;
|
||||
|
||||
internal static class WcfClientConstants
|
||||
{
|
||||
public const string ServiceModelAssemblyName = "System.ServiceModel";
|
||||
public const string ChannelFactoryTypeName = "System.ServiceModel.ChannelFactory";
|
||||
public const string InitializeEndpointMethodName = "InitializeEndpoint";
|
||||
public const string MinVersion = "4.0.0";
|
||||
public const string MaxVersion = "4.*.*";
|
||||
public const string IntegrationName = nameof(TracerInstrumentation.WcfClient);
|
||||
public const string EndpointAddressTypeName = "System.ServiceModel.EndpointAddress";
|
||||
public const string ServiceEndpointTypeName = "System.ServiceModel.Description.ServiceEndpoint";
|
||||
public const string BindingTypeName = "System.ServiceModel.Channels.Binding";
|
||||
public const string ConfigurationTypeName = "System.Configuration.Configuration";
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// <copyright file="WcfClientInitializer.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
#if NETFRAMEWORK
|
||||
using OpenTelemetry.AutoInstrumentation.DuckTyping;
|
||||
using OpenTelemetry.Instrumentation.Wcf;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf;
|
||||
|
||||
internal static class WcfClientInitializer
|
||||
{
|
||||
private interface IChannelFactory
|
||||
{
|
||||
IEndpoint Endpoint { get; }
|
||||
}
|
||||
|
||||
private interface IEndpoint
|
||||
{
|
||||
IKeyedCollection Behaviors { get; }
|
||||
}
|
||||
|
||||
private interface IKeyedCollection
|
||||
{
|
||||
void Add(object o);
|
||||
|
||||
bool Contains(Type t);
|
||||
}
|
||||
|
||||
public static void Initialize(object instance)
|
||||
{
|
||||
// WcfInstrumentationActivitySource.Options is initialized by WcfInitializer
|
||||
// when targeted assembly loads. Remaining work to initialize instrumentation
|
||||
// is to add telemetry behavior to the endpoint's collection.
|
||||
if (!instance.TryDuckCast<IChannelFactory>(out var channelFactory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var behaviors = channelFactory.Endpoint.Behaviors;
|
||||
if (!behaviors.Contains(typeof(TelemetryEndpointBehavior)))
|
||||
{
|
||||
behaviors.Add(new TelemetryEndpointBehavior());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// <copyright file="WcfClientIntegration.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
#if NETFRAMEWORK
|
||||
|
||||
using OpenTelemetry.AutoInstrumentation.CallTarget;
|
||||
using OpenTelemetry.AutoInstrumentation.Configurations;
|
||||
|
||||
namespace OpenTelemetry.AutoInstrumentation.Instrumentations.Wcf;
|
||||
|
||||
/// <summary>
|
||||
/// ChannelFactory instrumentation.
|
||||
/// </summary>
|
||||
[InstrumentMethod(
|
||||
assemblyName: WcfClientConstants.ServiceModelAssemblyName,
|
||||
typeName: WcfClientConstants.ChannelFactoryTypeName,
|
||||
methodName: WcfClientConstants.InitializeEndpointMethodName,
|
||||
returnTypeName: ClrNames.Void,
|
||||
parameterTypeNames: new[] { ClrNames.String, WcfClientConstants.EndpointAddressTypeName },
|
||||
minimumVersion: WcfClientConstants.MinVersion,
|
||||
maximumVersion: WcfClientConstants.MaxVersion,
|
||||
integrationName: WcfClientConstants.IntegrationName,
|
||||
type: InstrumentationType.Trace)]
|
||||
[InstrumentMethod(
|
||||
assemblyName: WcfClientConstants.ServiceModelAssemblyName,
|
||||
typeName: WcfClientConstants.ChannelFactoryTypeName,
|
||||
methodName: WcfClientConstants.InitializeEndpointMethodName,
|
||||
returnTypeName: ClrNames.Void,
|
||||
parameterTypeNames: new[] { ClrNames.String, WcfClientConstants.EndpointAddressTypeName, WcfClientConstants.ConfigurationTypeName },
|
||||
minimumVersion: WcfClientConstants.MinVersion,
|
||||
maximumVersion: WcfClientConstants.MaxVersion,
|
||||
integrationName: WcfClientConstants.IntegrationName,
|
||||
type: InstrumentationType.Trace)]
|
||||
[InstrumentMethod(
|
||||
assemblyName: WcfClientConstants.ServiceModelAssemblyName,
|
||||
typeName: WcfClientConstants.ChannelFactoryTypeName,
|
||||
methodName: WcfClientConstants.InitializeEndpointMethodName,
|
||||
returnTypeName: ClrNames.Void,
|
||||
parameterTypeNames: new[] { WcfClientConstants.ServiceEndpointTypeName },
|
||||
minimumVersion: WcfClientConstants.MinVersion,
|
||||
maximumVersion: WcfClientConstants.MaxVersion,
|
||||
integrationName: WcfClientConstants.IntegrationName,
|
||||
type: InstrumentationType.Trace)]
|
||||
[InstrumentMethod(
|
||||
assemblyName: WcfClientConstants.ServiceModelAssemblyName,
|
||||
typeName: WcfClientConstants.ChannelFactoryTypeName,
|
||||
methodName: WcfClientConstants.InitializeEndpointMethodName,
|
||||
returnTypeName: ClrNames.Void,
|
||||
parameterTypeNames: new[] { WcfClientConstants.BindingTypeName, WcfClientConstants.EndpointAddressTypeName },
|
||||
minimumVersion: WcfClientConstants.MinVersion,
|
||||
maximumVersion: WcfClientConstants.MaxVersion,
|
||||
integrationName: WcfClientConstants.IntegrationName,
|
||||
type: InstrumentationType.Trace)]
|
||||
public static class WcfClientIntegration
|
||||
{
|
||||
/// <summary>
|
||||
/// OnMethodEnd callback
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance value, aka `this` of the instrumented method.</param>
|
||||
/// <param name="exception">Exception value</param>
|
||||
/// <param name="state">CallTarget state</param>
|
||||
/// <typeparam name="TTarget">Type of the target</typeparam>
|
||||
/// <returns>A response value, in an async scenario will be T of Task of T</returns>
|
||||
internal static CallTargetReturn OnMethodEnd<TTarget>(TTarget instance, Exception exception, in CallTargetState state)
|
||||
{
|
||||
if (Instrumentation.TracerSettings.Value.EnabledInstrumentations.Contains(TracerInstrumentation.WcfClient))
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
WcfClientInitializer.Initialize(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return CallTargetReturn.GetDefault();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -2,16 +2,8 @@
|
|||
<configuration>
|
||||
<system.serviceModel>
|
||||
<extensions>
|
||||
<behaviorExtensions>
|
||||
<add name="telemetryExtension" type="OpenTelemetry.Instrumentation.Wcf.TelemetryEndpointBehaviorExtensionElement, OpenTelemetry.Instrumentation.Wcf" />
|
||||
</behaviorExtensions>
|
||||
</extensions>
|
||||
<behaviors>
|
||||
<endpointBehaviors>
|
||||
<behavior name="telemetry">
|
||||
<telemetryExtension />
|
||||
</behavior>
|
||||
</endpointBehaviors>
|
||||
</behaviors>
|
||||
<bindings>
|
||||
<basicHttpBinding>
|
||||
|
|
@ -26,8 +18,8 @@
|
|||
</netTcpBinding>
|
||||
</bindings>
|
||||
<client>
|
||||
<endpoint address="http://127.0.0.1:9009/Telemetry" binding="basicHttpBinding" bindingConfiguration="basicHttpConfig" behaviorConfiguration="telemetry" contract="TestApplication.Wcf.Client.NetFramework.IStatusServiceContract" name="StatusService_Http" />
|
||||
<endpoint address="net.tcp://127.0.0.1:9090/Telemetry" binding="netTcpBinding" bindingConfiguration="netTCPConfig" behaviorConfiguration="telemetry" contract="TestApplication.Wcf.Client.NetFramework.IStatusServiceContract" name="StatusService_Tcp" />
|
||||
<endpoint address="http://127.0.0.1:9009/Telemetry" binding="basicHttpBinding" bindingConfiguration="basicHttpConfig" contract="TestApplication.Wcf.Client.NetFramework.IStatusServiceContract" name="StatusService_Http" />
|
||||
<endpoint address="net.tcp://127.0.0.1:9090/Telemetry" binding="netTcpBinding" bindingConfiguration="netTCPConfig" contract="TestApplication.Wcf.Client.NetFramework.IStatusServiceContract" name="StatusService_Tcp" />
|
||||
</client>
|
||||
</system.serviceModel>
|
||||
</configuration>
|
||||
|
|
|
|||
Loading…
Reference in New Issue