Javascript error in plain HTML when using OData collections

ServiceStack is being used to provide web services that expose OData data, connecting to our Dynamics365 hosted environment. The services themselves are working, but I have a nagging error in our main HTML page that I’m looking for advice on.

The HTML code is relatively simple and was based on the information presented here: https://docs.servicestack.net/typescript-add-servicestack-reference#install. Here’s the relevant snippet:

...
<body>

<h2>Dynamics Web Services</h2>
<div id="result"></div>

<script src="https://unpkg.com/@servicestack/client/dist/servicestack-client.min.js"></script>
<script src="/js/require.js"></script>
<script src="/js/servicestack-client.js"></script>
<script src="/types/js"></script>
<script>
var { JsonServiceClient, GetVersion } = exports;

var client = new JsonServiceClient();
function callGetVersion() {
    client.api(new GetVersion())
        .then(function(r) {
            document.getElementById('result').innerHTML = r.status;
        });
}

callGetVersion();
</script>
...

The GetVersion service simply returns a string that includes the current assembly information and a link to the metadata/Swagger.

When the page is rendered, the generated JS contains an error that prevents the call to the web service from running correctly. I can see that the types/js file is getting regenerated each time as the header timestamp gets updated.

image

For reference, the Visual Studio Extension “OData Connected Service” was used to generate the Reference.cs that defines all the various classes that represent the OData endpoints (https://github.com/odata/ODataConnectedService).

I’ve tested this type of approach with a similar project that doesn’t utilize OData objects in the services and that project works as expected, the service is called and no JS error occur.

I’m unsure of how to proceed or what my options are to resolve this error.

Yeah we definitely don’t support returning OData collections from ServiceStack Services, you should use whatever they recommended for exposing them on the server and whatever OData client they recommend for calling them with.

Having said that the error is because of a missing generated type which you can try to force exporting using Metadata.ForceInclude, e.g:

public override void Configure(Container container)
{
    Metadata.ForceInclude = new() {
        typeof(ObservableCollection),
    };
}

Which may or may not fix this issue. Another solution is to prevent the problematic types from being generated by adding them to ExcludeTypes, e.g:

var nativeTypes = this.GetPlugin<NativeTypesFeature>();
nativeTypes.MetadataTypesConfig.ExcludeTypes.AddRange(new[] {
    nameof(DataServiceCollection),
    //...
});

There’s also an opportunity to modify what gets generated, e.g. if you’re only using the GetVersion API you can limit generation to just that API and its dependent types with:

<script src="/types/js?IncludeTypes=GetVersion.*"></script>

Or do the opposite and exclude the problematic types from being generated with:

<script src="/types/js?ExcludeTypes=DataServiceCollection"></script>

Otherwise instead of referencing the /types/js endpoint directly you can save it to a static file like dtos.js that way you can modify it to exclude the problematic OData types you’re not using.

Also simply defining the missing types before importing it may also resolve this issue, e.g:

<script>
window.ObservableCollection = /** @class */ (function () {
    function ObservableCollection(init) {
        Object.assign(this, init);
    }
    return ObservableCollection;
}());
</script>
<script src="/types/js"></script>

Note if you don’t have to support ES3 (e.g. old IE’s), you can get better development experience using the newer ES6 classes in JavasScript Add ServiceStack Reference.

You must get tired of hearing how awesome you are, but it won’t stop me from saying it: you are truly a legend and I want to be like you when I grow up.

I ran through the various options you provided, most of which didn’t work as you correctly anticipated due to the OData classes or conflicts between the OData classes and our internal AutoMapper classes that get presented to the public side of the services. It was your final note that really caught my attention, however. Since this project is NET6, I was able to use the newer ES6 classes and I have a working result now. I’m documenting it here just for anyone else who might come across this issue, no need for any further follow up.

<script async src="https://ga.jspm.io/npm:es-module-shims@1.6.3/dist/es-module-shims.js"></script><!--safari-->
<script type="importmap">
        {
            "imports": {
                "@servicestack/client":"https://unpkg.com/@servicestack/client@2/dist/servicestack-client.min.mjs"
            }
        }
</script>
<script type="module">
    import { JsonApiClient } from '@servicestack/client'
    import { GetVersion } from '/types/mjs?IncludeTypes=GetVersion.*'

    const client = JsonApiClient.create()

    client.api(new GetVersion()).then(function (r) {
        //console.log(r.succeeded);
        document.getElementById('result').innerHTML = r.response.status;
    });
</script>

Thank you for your prompt reply and pointer to a working solution, it’s an amazing level of support that you’d be answering posts late on a Friday night.

2 Likes