wcf icon indicating copy to clipboard operation
wcf copied to clipboard

ServiceContractAttribute.ConfigurationName differs from what Visual Studio generated

Open KalleOlaviNiemitalo opened this issue 2 years ago • 5 comments

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 generates ConfigurationName="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/.

KalleOlaviNiemitalo avatar Nov 16 '23 08:11 KalleOlaviNiemitalo

I attached a solution with demo projects, and edited the "To Reproduce" and "Expected behavior" sections to match those projects.

KalleOlaviNiemitalo avatar Nov 17 '23 13:11 KalleOlaviNiemitalo

@imcarolwang can you please take a look at this?

HongGit avatar Dec 06 '23 22:12 HongGit

Comparing with svcutil tool in .NET Framework project, lots of code of the dotnet-svcutil tool in wcf.git is rewritten for .NET Core platform. .NET Core is originally missing support of config file and the ConfigurationName attribute generated is actually unused by .NET Core client project and also being ignored in the SDK style .NET Framework project, that's why you have to manually add code (in the demo project) for the ConfigurationName attribute to work with app.config for the SDK style .NET Framework project.

The value of ConfigurationName attribute is computed by combining Namespace specified by user and the service type name, I've verified that this is the same rule used by both svcutil and dotnet-svcutil tools.

The difference is that in the old-style .NET Framework C# project, the actual namespace of the generated code is project name plus the user-specified namespace, but in SDK style project which uses dotnet-svcutil, code namespace is the exact namespace specified by user, without the project name prefix. I think the change in dotnet-svcutil is by design because option "Namespace" indicates that what user inputs will be the namespace of the generated code.

So, I suggest updating the configuration manually as a workaround for now. After .NET Core updates support for configuration APIs, we may consider adding control specifying the ServiceContract's ConfigurationName attribute.

imcarolwang avatar Dec 08 '23 10:12 imcarolwang

So, I suggest updating the configuration manually as a workaround for now.

The software that I'm migrating to dotnet-svcutil is deployed to many environments with customized configuration files. It would be difficult to update all of them. It is easier to postprocess the C# code generated by dotnet-svcutil, so that the binary becomes drop-in compatible.

After .NET Core updates support for configuration APIs, we may consider adding control specifying the ServiceContract's ConfigurationName attribute.

What kind of update do you mean? AFAIK, System.Configuration.ConfigurationManager has existed since .NET Core 2.1 and reads the config file; see Breaking change: System.diagnostics entry in app.config, for example. Are you hoping for an API that would let WCF programmatically register the system.serviceModel configuration section group as if it were listed in the built-in machine.config, so that it wouldn't need to be declared in each configuration file?

KalleOlaviNiemitalo avatar Dec 14 '23 17:12 KalleOlaviNiemitalo

What kind of update do you mean?

Sorry for the confusion, it's my mistake. It was that we may introduce new wcf configuration api base on the new .NET IConfiguration api.

imcarolwang avatar Dec 15 '23 05:12 imcarolwang