Consuming NetSuite WebServices in .NET Core / .NET Standard 2.0+

Full Disclosure: I’m one of the authors of the Celigo ServiceManager for NetSuite library mentioned in this post.

A lot of my team’s development tasks revolve around SuiteTalk WebServices API. Because of this we’ve always maintained wrapper library around SuiteTalk, called the Celigo Service Manager. And we’ve been quite active in keep it open source and (reasonably) up to date. Historically, this library was meant primarily for monolithic web or desktop applications built on the full .NET Framework. In such applications, the library was useful for it’s built in retry logic, connection pooling capabilities and for the overall unified API it presented.

Recently, we’ve ported this concept over to .NET Standard and have been quite actively releasing updates via the repository as well as NuGet. The library is not “officially” supported by Celigo, Inc. but follows more of an open development process. This blog post should serve as a primer in to why and how to use the Celigo ServiceManager for NetSuite.

Using VisualStudio Generated Stubs and Proxies

First off, here’s why you should consider using the library as opposed to just using the stubs generated by VisualStudio.

SuiteTalk WSDL doesn’t play nice with WCF Connected Services

There are few running issues with SuiteTalk and WCF Connected Service code generator. Few of them I’m aware of are:

  1. Web Service Reference Provider error: There was a problem reading the MetadataSet argument
  2. Unable to Import WSDL – Metadata contains a reference that cannot be resolved
  3. “Operation not Supported on this Platform” error attempting to make a request to a Web Service
  4. WCF Connected Service – Base class properties are not deserialized

The new Request/Response Wrappers

If you do manage to get the code to generate (using dotnet-svcutil for example), you’ll find that the generated client (NetSuitePortTypeClient), have method signatures that look different from that of the concise ASMX style proxies that VisualStudio used to generate.

For example, it used to be that you could pre-configure your client with the user credentials and preferences, and simply call add(Record record) to add a single record to NetSuite. The response would then contain a single WriteResponse with the status and a BaseRef holding the Internal ID that was assigned to the record that was created.

In the new Connected Service proxy will instead hold a method like this:

Task<addResponse> addAsync(addRequest request)

Inspecting the addRequest class you’d find that it expects the SOAP Header fields to be set in this request object along with the actual data you want to upload. Similarly, addResponse wraps the WriteResponse along with a documentInfo property that would hold the JSESSIONID that you don’t really need when using request-level authentication.

Using the Celigo ServiceManager for NetSuite

The first obvious advantage of using the library is that you don’t need to go through the process of generating the stubs and proxies: Install the nuget package and you’ll find all the WSDL types under the SuiteTalk namespace. Note that, at the time of this writing we are supporting SuiteTalk 2017.2. Our next major update will, likely be to target 2018.2.

Install-Package Celigo.ServiceManager.NetSuite

Note that if you don’t really care for the ServieManager functionality, and only need the stubs, you can just install our base package for SuiteTalk 2017.2:

​​​​​Install-Package Celigo.SuiteTalk.2017_2

The INetSuiteClient Interface

When making WebService requests, you’d be working with the INetSuiteClient interface. It contains “wrapper”​ methods around the SuiteTalk API methods, exposing to you similar method contracts you had with the old ASMX style proxies. You’ll find the the full definition of this interface on GitHub.

Utility Interfaces on SuiteTalk WSDL types

Even with our legacy ServiceManager, something that we’ve been doing is to augment the VisualStudio generated WSDL types with custom interfaces. This of course makes a lot of sense to us, because of the type of dynamic applications we build, we require as much metadata as possible about the types that we are working on and adding these augmentations at compile time allows you to write more reusable code and not have to resort to runtime checks, especially those involving reflection. We’ve kept these augmentations open sourced hoping that they would prove useful to anyone else as well.

The custom interfaces we’ve added include:

  1. ISearch and ISearch<T> types that are applied to SearchRecord types. Similarly, there are ISearchAdvanced<>, ISearchAdvancedRow, ISearchColumnField etc. With these types, you get write more concise reusable code. E.g.
    // Old Code
    var oldStyle = new TaskSearchAdvanced {
        columns = new TaskSearchRow {
            basic = new TaskSearchRowBasic {
                startDate = new[] {
                    new SearchColumnDateField { customLabel = "startDate" }
                },
                status = new[] {
                    new SearchColumnEnumSelectField { customLabel = "status" }
                }
            }
        }
    };
    
    // Becomes...
    var search = new TaskSearchAdvanced().CreateColumns(
        columns => columns.CreateBasic(
                            b => b.SetColumns(new[] {
                                nameof(b.startDate),
                                nameof(b.status)
                            })
                        ));
  2. IReference that is applied to all BaseRef derived types such as RecordRef, CustomRecordRef, CustomTransactionRef etc. The interface enable you to set or retrieve the Internal ID or External ID even if all you have is a reference to a BaseRef without then doing an upcast or even a type check.

Making Requests to NetSuite

You can find the sample usage of ServiceManager in the unit test project. In short, you need 2 things:

  1. An Application ID, that you’d generate on NetSuite by going to
    Setup > Integration > Manage Integrations > New
  2. A class that implements the IPassportProvider interface or ITokenPassportProvider interface. You can find implementations of these interfaces using that read the required data from environment variables in the unit tests folder.

Everything else is pretty straightforward.

var factory = new ClientFactory(config.ApplicationId);
client = factory.CreateClient(config.PassportProvider);
var serverTimeResult = await client.getServerTimeAsync();
serverTimeResult.status.isSuccess.Should().BeTrue();
serverTimeResult.serverTime.Year.Should().Be(DateTime.Now.Year);

 

 

8 thoughts on “Consuming NetSuite WebServices in .NET Core / .NET Standard 2.0+

  1. Thanks Sameera,

    I have this running in an Azure Function (Azure Function 2 with .Net Core 2.1) and had to make the following changes to the .csproj file to get it to run on my local system. (I think this is because of bugs in Azure functions)

    Always

    However, when I publish this to Azure and run it there I get a very cryptic “Could not load the specified file.” error.

    I’m not sure if you have tried this or know a workaround but if you do let me know.

    Thanks so much for making this available!

    Many thanks, Stuart

    Like

  2. Sorry can’t post the changes to the ,csproj file for some reason.

    Had to set the Target Name ‘System.Http.WinHttpHandler.dll’ and ‘System.Private.ServiceModel.dll’ to copy to outputPath\bin for this to work.

    Stuart

    Like

  3. Hi Sameera,

    Thanks heaps for this, I now have this working with Azure Functions but I am now getting the following timeout message.

    System.TimeoutException: The HTTP request to ‘https://webservices.netsuite.com/services/NetSuitePort_2018_2’ has exceeded the allotted timeout of 00:01:00 while reading the response. The time allotted to this operation may have been a portion of a longer timeout.

    I am logging in using user credentials and can’t see how to extend the timeout.

    Can you let me know how to do this?

    Kind regards,
    Stuart Barnaby

    Like

    1. Stuart,
      Apologies for not seeing this in time. I hope you sorted this out.

      For others looking for the same answer, following should work to increase the time out:

      client.InnerChannel.OperationTimeout = TimeSpan.FromMinutes(10);
      

      Like

  4. Hi Sameera,

    I am trying to consume the soap web services of netsuite in .net core 2.2. While trying to call a method provided in the reference.cs, the method throws error : : ‘The remote server returned an unexpected response: (410) Gone.’

    Here’s what I have tried :

    public async Task<AdarzaService2.getListResponse> Get()
    {
    AdarzaService2.Passport passport = new Passport();
    AdarzaService2.TokenPassport tokenPassport = new TokenPassport();
    AdarzaService2.ApplicationInfo applicationInfo = new ApplicationInfo();
    AdarzaService2.PartnerInfo partnerInfo = new PartnerInfo();
    AdarzaService2.Preferences preferences = new Preferences();
    AdarzaService2.BaseRef[] baseRef = new BaseRef[1];
    NetSuitePortTypeClient netSuitePortTypeClient = new NetSuitePortTypeClient();
    passport.account = this.configuration[“AccountId”];
    passport.email = this.configuration[“Email”];
    passport.role = new RecordRef
    {
    name = this.configuration[“Role”]
    };
    tokenPassport.account = this.configuration[“AccountId”];
    tokenPassport.consumerKey = this.configuration[“ConsumerKey”];
    tokenPassport.token = this.configuration[“TokenId”];
    applicationInfo.applicationId = this.configuration[“ApplicationId”];
    partnerInfo.partnerId = “”;
    //baseref[0].name = “”;
    var data = await netSuitePortTypeClient.getListAsync(passport, tokenPassport, applicationInfo, partnerInfo, preferences, baseRef);
    return data;
    }

    Can you please tell me what is causing this issue?

    Like

    1. Hi Prasanna,
      I believe this has to do with the getListAsync overload method that you are using. Firstly, you should not use both passport and tokenPassport parameters. One of them should be null.

      Also, please note that we’ve stopped using the NetSuitePortTypeClient class directly because similar issues that we’ve run in to. Which is one the primary reasons why the ServiceManager library exists. If you look at the library, you’ll see that we’ve hidden away these passport, partnerInfo etc parameters and use alternative means of creating the SOAP header.

      Like

Leave a Reply to dvtnz Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.