Swagger-codegen: [C#] Declaring ApiClient:InterceptRequest as virtual instead of partial

Created on 11 Oct 2017  路  12Comments  路  Source: swagger-api/swagger-codegen

Description

I use a C# client generated with swagger-codegen. In order to log some diagnostic Information I would like to make use of InterceptRequest and InterceptResponse. Unfortunately both methods are declared as partial instead of virtual. Thus I cannot override these methods and I would like to avoid to manually manipulate the generated code afterwards. Maybe someone can give me a hint whether I oversee something or if I have another option to get achieved what I want.

Swagger-codegen version

2.2.1

Suggest a fix/enhancement

Maybe it's an option to declare both methods as virtual instead of partial.

C-Sharp Bug help wanted

Most helpful comment

I know this is way late, but hopefully, this might help someone else.

TL; DR: Partial is the way to go here - there shouldn't be a bug. It should be by design. Partial Classes and Methods

It is common practice for data/model generation tools to generate classes/methods as partial. The intent is that rather than creating a derived class and overriding the member, you can extend the class directly - without the concern of your extension being overwritten if the generated code is ever regenerated.

The to achieve your goal in a manner consistent with common conventions (such as in EntityFramework), add a new class in the root of the project named ApiClient and change the filename from ApiClient.cs to ApiClient.partial.cs. The ".partial" part is a common naming convention for files containing classes declared as "partial". Then, replace the content of the file with this:

namespace IO.Swagger.Client
{
    public partial class ApiClient
    {
        partial void InterceptRequest(IRestRequest request)
        {
            // Your code here.
        }

        partial void InterceptResponse(IRestRequest request, IRestResponse response)
        {
            // Your code here.
        }
    }
}

It's important that 1) the class is declared partial, 2) the methods are declared partial, and 3) the namespace matches that of the main ApiClient class definition. Partial classes allow you to define part of the class in one file (i.e. the generated one) and part of it in another (i.e. your extension file). When the library is compiled, it's all compiled into a single class - with your functionality included. If you have generated a client for an API with a large surface area (i.e. a lot of endpoints), this will likely save you a lot of work going through each one of the generated files and replacing references to ApiClient with your custom-derived class (which would also be wiped out if you have to regenerate the API client library).

If you are trying to do this so that you can define your logic outside the API library, I'd recommend defining an interface in the API library that you can implement somewhere else and pass in to be called from the partial class.

If for some reason you still need to create a derived class, here is one way to do exactly what you wanted to originally. Start with the same partial definition above, then:

namespace IO.Swagger.Client
{
    public partial class ApiClient
    {
        partial void InterceptRequest(IRestRequest request)
        {
            InterceptRequestOverride(request);
        }

        partial void InterceptResponse(IRestRequest request, IRestResponse response)
        {
            InterceptResponseOverride(request, response);
        }

        protected virtual void InterceptRequestOverride(IRestRequest request) { }
        protected virtual void InterceptResponseOverride(IRestRequest request, IRestResponse response) { }
    }

    // Elsewhere.......
    class MyApiClient : ApiClient
    {
        protected override void InterceptRequestOverride(IRestRequest request)
        {
            // Your code here.
        }

        protected override void InterceptResponseOverride(IRestRequest request, IRestResponse response)
        {
            // Your code here.
        }
    }
}

I hope this helps someone!

All 12 comments

I would suggest you to try the latest master to generate the C# client, which has been refactored. The SNAPSHOT version of the latest master can be found in the project's README.

You may also want to review and try the following PR, which adds capabilities to have better logging:

https://github.com/swagger-api/swagger-codegen/pull/5689

Sry for my late response. I downloaded the latest version (2.2.3) and generated the csharp client. In addition I had a look at the PR and it looks quite good to me. But I do not find any hint of that in the generated code. Do you have an example how I could make use of both intercept methods after I added the generated client as reference to another project?

Please try the latest master (2.3.0) SNAPSHOT version instead of 2.2.3: https://github.com/swagger-api/swagger-codegen#compatibility

After some tries I got it managed to generate a csharp client (incl. some warning in regard to JsonSubTypes class). But I still see InterceptRequest and InterceptResponse being declared as partial and could find a Logger class as mentioned in #5689
Did I oversee something?

Update Unfortunately I currently have to stay with MsVs2010. Thus I only can use NuGet 2.8.x. Thus I cannot add the reference to Json.Net 10.x as it requires NuGet 2.12+ and thus MsVs2013+
If I go backward and try to downgrade to Json.Net 4.5.11 as it worked in beforehand I coulnd't build the dll because of the following errors:

src\IO.Swagger\Client\JsonSubTypes.cs(174,27): error CS1061: 'Newtonsoft.Json.JsonReader' enth盲lt keine Definition f眉r 'SupportMultipleContent', und es konnte
keine Erweiterungsmethode 'SupportMultipleContent' gefunden werden, die ein erstes Argument vom Typ 'Newtonsoft.Json.JsonReader' akzeptiert (Fehlt eine
Using-Direktive oder ein Assemblyverweis?).

Seems like the new class JsonSubTypes crashed it => 2.3.0 SNAPSHOT isn't currently an option for me :-(

Just a short push for my remark => 2.3.0 SNAPSHOT isn't currently an option for me :-(
Did I oversee something?

Any update on this?

@Faerylnahr we released stable version 2.3.0 before X'mas. Please give it a try and let us know if it's still an issue.

(we'll reopen this issue if needed)

I'm sorry that I don't have better news but 2.3.0 is still not an option as I have to stick with MsVs2010 :(
As I stated in my Feedback JsonSubTypes crashed it.

I know this is way late, but hopefully, this might help someone else.

TL; DR: Partial is the way to go here - there shouldn't be a bug. It should be by design. Partial Classes and Methods

It is common practice for data/model generation tools to generate classes/methods as partial. The intent is that rather than creating a derived class and overriding the member, you can extend the class directly - without the concern of your extension being overwritten if the generated code is ever regenerated.

The to achieve your goal in a manner consistent with common conventions (such as in EntityFramework), add a new class in the root of the project named ApiClient and change the filename from ApiClient.cs to ApiClient.partial.cs. The ".partial" part is a common naming convention for files containing classes declared as "partial". Then, replace the content of the file with this:

namespace IO.Swagger.Client
{
    public partial class ApiClient
    {
        partial void InterceptRequest(IRestRequest request)
        {
            // Your code here.
        }

        partial void InterceptResponse(IRestRequest request, IRestResponse response)
        {
            // Your code here.
        }
    }
}

It's important that 1) the class is declared partial, 2) the methods are declared partial, and 3) the namespace matches that of the main ApiClient class definition. Partial classes allow you to define part of the class in one file (i.e. the generated one) and part of it in another (i.e. your extension file). When the library is compiled, it's all compiled into a single class - with your functionality included. If you have generated a client for an API with a large surface area (i.e. a lot of endpoints), this will likely save you a lot of work going through each one of the generated files and replacing references to ApiClient with your custom-derived class (which would also be wiped out if you have to regenerate the API client library).

If you are trying to do this so that you can define your logic outside the API library, I'd recommend defining an interface in the API library that you can implement somewhere else and pass in to be called from the partial class.

If for some reason you still need to create a derived class, here is one way to do exactly what you wanted to originally. Start with the same partial definition above, then:

namespace IO.Swagger.Client
{
    public partial class ApiClient
    {
        partial void InterceptRequest(IRestRequest request)
        {
            InterceptRequestOverride(request);
        }

        partial void InterceptResponse(IRestRequest request, IRestResponse response)
        {
            InterceptResponseOverride(request, response);
        }

        protected virtual void InterceptRequestOverride(IRestRequest request) { }
        protected virtual void InterceptResponseOverride(IRestRequest request, IRestResponse response) { }
    }

    // Elsewhere.......
    class MyApiClient : ApiClient
    {
        protected override void InterceptRequestOverride(IRestRequest request)
        {
            // Your code here.
        }

        protected override void InterceptResponseOverride(IRestRequest request, IRestResponse response)
        {
            // Your code here.
        }
    }
}

I hope this helps someone!

I agree that Partial make sense here. BUT I question the ByVal parameter passing. There's no way to, say, manage a 429 Too Many Requests situation where what you (I!) would do is (without the usual retry loop or checking of response header for delay timings):

    partial void InterceptResponse(**ref** IRestRequest request, **ref** IRestResponse response)
    {
        if (response != null && (int)response.StatusCode == 429)
        {
            System.Threading.Thread.Sleep(1000);
            response = RestClient.Execute(request);
        }
    }

ByRef parameters here make sense, especially for the response.

Does anyone have a solution to this that doesn't require modifying the generated code? (The code above is in a custom Helper Class.)

@protegesolutions I think you're misunderstanding what ByVal does for reference types in C#. Passing the object ByVal simply prevents you from completely breaking the variable. You can still update the passed objects.

@dcook34 It's possible, but when I run the following code it behaves as I expect:

class Program
{
    static void Main()
    {
        Object param = "Unchanged";
        Console.WriteLine(param);
        MethodVal(param);
        Console.WriteLine(param);
        MethodRef(ref param);
        Console.WriteLine(param);
    }

    static void MethodVal(Object param1)
    {
        param1 = "Changed";
    }

    static void MethodRef(ref Object param2)
    {
        param2 = "Changed";
    }
}

Output:

Unchanged
Unchanged
Changed

Was this page helpful?
0 / 5 - 0 ratings