Description
Describe the bug
When Visual Studio 2017 generates code for a Service Reference, it sets ServiceContractAttribute.ConfigurationName = the name of the Service Reference + "." + the name of the interface type. However, dotnet-svcutil 2.1.0 instead sets ServiceContractAttribute.ConfigurationName = the full name of the interface type. This difference makes the generated code incompatible with existing configuration files, hindering our migration from .NET Framework to .NET.
(We're first switching to SDK-style csproj files and dotnet-svcutil before we start multitargeting.)
To Reproduce
The attached ConfigurationNameDemo.zip demonstrates the problem. It contains three projects:
-
DemoService is a WCF web service. It has an old-style C# project file and targets .NET Framework 4.8.
-
DemoProjectVS is a console application that uses DemoService via a "Connected Service" (
WCFMetadata
item) in Visual Studio. It has an old-style C# project file and targets .NET Framework 4.8.Visual Studio generates
ConfigurationName="DemoService.IService1"
:namespace DemoProjectVS.DemoService { [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] [System.ServiceModel.ServiceContractAttribute(ConfigurationName="DemoService.IService1")] public interface IService1 {
The App.config file then has:
contract="DemoService.IService1"
:<endpoint address="http://localhost:59055/Service1.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1" contract="DemoService.IService1" name="BasicHttpBinding_IService1" />
-
DemoProjectSvcUtil is a console application that uses DemoService via code generated by dotnet-svcutil 2.1.0, augmented with a
partial class
that lets the application use the ClientBase<TChannel>(string endpointConfigurationName) constructor. It has an SDK-style C# project file and targets .NET Framework 4.8.dotnet-svcutil
generatesConfigurationName="DemoProjectSvcUtil.DemoService.IService1"
:namespace DemoProjectSvcUtil.DemoService { [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")] [System.ServiceModel.ServiceContractAttribute(ConfigurationName="DemoProjectSvcUtil.DemoService.IService1")] internal interface IService1 {
The App.config file then has:
contract="DemoProjectSvcUtil.DemoService.IService1"
:<endpoint address="http://localhost:59055/Service1.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1" contract="DemoProjectSvcUtil.DemoService.IService1" name="BasicHttpBinding_IService1" />
Expected behavior
Have some way to generate the same ServiceContractAttribute.ConfigurationName as in Service Reference in Visual Studio. For example, the parameters JSON file could support a "configurationNamespaceMappings" akin to "namespaceMappings":
{
"providerId": "Microsoft.Tools.ServiceModel.Svcutil",
"version": "2.1.0",
"options": {
"configurationNamespaceMappings": [
"*, DemoService"
]
"inputs": [
"Service1.wsdl",
"Service11.xsd",
"Service12.xsd"
],
"internal": true,
"namespaceMappings": [
"*, DemoProjectSvcUtil.DemoService"
],
"outputFile": "Reference.cs",
"sync": true,
"targetFramework": "net48",
"typeReuseMode": "All"
}
}
Alternatively, the parameters JSON file could support a mapping from Type.FullName to the desired ServiceContractAttribute.ConfigurationName. This would be more tedious to configure if there are multiple services in the same WSDL:
"configurationNameMappings": [
"DemoProjectSvcUtil.DemoService.IService1, DemoService.IService1"
]
Either way, the parameters would then cause the generated code to change:
--- a/DemoProjectSvcUtil/ServiceReferences/DemoService/Reference.cs
+++ b/DemoProjectSvcUtil/ServiceReferences/DemoService/Reference.cs
@@ -10,11 +10,11 @@
namespace DemoProjectSvcUtil.DemoService
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
- [System.ServiceModel.ServiceContractAttribute(ConfigurationName="DemoProjectSvcUtil.DemoService.IService1")]
+ [System.ServiceModel.ServiceContractAttribute(ConfigurationName="DemoService.IService1")]
internal interface IService1
{
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/GetData", ReplyAction="http://tempuri.org/IService1/GetDataResponse")]
string GetData();
Screenshots
None.
Additional context
It seems Visual Studio uses VSWCFServiceContractGenerator.PatchConfigurationName to change ConfigurationName after ServiceContractGenerator has generated it. The VSWCFServiceContractGenerator class does not exist in https://github.com/dotnet/wcf/.