Saturday, 28 May 2022

Get properties of a given type in C#

This article shows how we can find all properties with a given property type. The provided code also can find private properties or look for nullable of the property type. E.g. find all DateTime properties and also include all properties which are Nullable of DateTime, Nullable. An extension method for this looks like the following (put the method into a static class as it is an extension method) :
  

  
          /// <summary>
        /// Retrieves a list of properties (property info) with given type <paramref name="propertyType"/> in a nested object
        /// </summary>
        /// <param name="rootObject"></param>
        /// <param name="propertyType"></param>
        /// <param name="includePrivateProperties">If set to true, includes private properties</param>
        /// <param name="includeNullableVariant">If set to true, return also propertie which are the nullable variant of the <paramref name="propertyType"/>.</param>
        /// <returns>A list of properties with given <paramref name="propertyType"/>, possibly including also non-nullable variant of the type and both public and private properties set with the parameters <paramref name="includePrivateProperties"/> and <paramref name="includeNullableVariant"/></returns>
        public static IEnumerable<PropertyInfo> GetPropertiesOfType(this object rootObject, Type propertyType,
            bool includePrivateProperties = false, bool includeNullableVariant = false)
        {
            if (rootObject == null)
            {
                yield return null;
            }
            var bindingFlagsFilter = !includePrivateProperties ? BindingFlags.Public | BindingFlags.Instance : BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var propertiesOfType = rootObject.GetType().GetProperties(bindingFlagsFilter)
                .Where(p => p.PropertyType == propertyType || (includeNullableVariant && propertyType == Nullable.GetUnderlyingType(p.PropertyType)))
                .ToList();
            foreach (var prop in propertiesOfType)
            {
                yield return prop;
            }
            var nestableProperties = rootObject.GetType().GetProperties(bindingFlagsFilter)
              .Where(p => p.PropertyType.IsClass && p.PropertyType != typeof(string))
              .ToList(); //ignoring properties of type strings as they are not nested, though a class
            foreach (var prop in nestableProperties)
            {
                if (prop.GetIndexParameters().Length > 0)
                {
                    continue; //skip indexer properties 
                }
                var rootObjectLevel = prop.GetValue(rootObject, null);
                if (rootObjectLevel == null)
                {
                    continue;
                }
                foreach (var propertyAtLevel in GetPropertiesOfType(rootObjectLevel, propertyType, includePrivateProperties, includeNullableVariant))
                {
                    yield return propertyAtLevel;
                }
            }
        }

We find the properties matching the property type and then recursively fetch such properties at nested levels too if the property is a class and therefore can contain sub properties. We end up with all the properties of a given type. As we see, we adjust the binding flags to include private properties too or not. And we use the Nullable.GetUnderlyingType method to match the underlying type in case we want to look for DateTime and DateTime? properties. This method is fairly fast, in the order of a few milliseconds (1-5 when I tested for an ordinary two level nested object.) But we are using reflection here and the method could be faster if we made use of some other techniques, perhaps with
IL 'magic'. I have not found a way to do this yet though.. Here is another utility method (extension method) for finding 'property paths'. This is handly if you want to craft an SQL select statement for example as we need the fully qualified path perhaps if our tooling creates fields in the database similar to POCO object
and the nested object is similar to table structure. Maybe your nested properties are mapped to SomeInnerTable1_SomeField1 and so on. Anyways, it is handly to have 'property paths' to the properties to get a fast overview of where the properties are located in the nested structure of your (possibly complex) object.



         /// <summary>
        /// This method looks for properties of given type in a nested object (e.g. a form data contract) 
        /// </summary>
        /// <param name="rootObject"></param>
        /// <param name="propertyType"></param>
        /// <returns></returns>
        private IEnumerable<string> GetPropertyPathsForType(object rootObject, Type propertyType, string prefixAtLevel = "")
        {
            if (rootObject == null)
            {
                yield return string.Empty;
            }
            var propertiesOfType = rootObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.PropertyType == propertyType)
                .ToList();

            foreach (var prop in propertiesOfType)
            {
                if (string.IsNullOrWhiteSpace(prefixAtLevel))
                {
                    yield return prop.Name; //root properties have no prefix 
                }
                else
                {
                    yield return prefixAtLevel.TrimStart('.') + "." + prop.Name;
                }
            }

            var nestableProperties = rootObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
              .Where(p => p.PropertyType.IsClass && p.PropertyType != typeof(string))
              .ToList(); //ignoring properties of type strings as they are not nested, though a class

            foreach (var prop in nestableProperties)
            {
                if (prop.GetIndexParameters().Length > 0)
                {
                    continue; //skip indexer properties - this is identified as required 
                }
                var rootObjectLevel = prop.GetValue(rootObject, null);
                if (rootObjectLevel == null)
                {
                    continue;
                }
                foreach (var propertyAtLevel in GetPropertyPathsForType(rootObjectLevel, propertyType, prefixAtLevel + "." + prop.Name))
                {
                    yield return propertyAtLevel.TrimStart('.').TrimEnd('.');
                }
            }
        }


The code above could use the first method more to support including public and private properties, but I leave it out 'as an exercise to the reader' as text books often states. So this code is very handy if you for example at work need to find 'all the datetime properties in a domain object' and similar cases. Maybe you want to deny all datetimes have a future datetime in case it is a report for a patient treatment report being performed yesterday and so on, and for
that particular model, there will be no future date time values.

Wednesday, 6 April 2022

Abstract factory - registering factory methods

Today, instead of relying on factories we often resort to dependency injection. However, you can 'blueprint' objects by offering a helper class to register both interfaces with concrete classes and default parameters if you want to instantiate a type of object with given parameters using an abstract factory class The following code shows how we can start building such a registration approach, supporting also unregistering factories and creating objects which are registered. You will have to add thread safety and better checking if a given type T and parameters P1..PN are registered, here is though a simple Abstract factory. Paste the code into Linqpad 6 or Linqpad 7 to test it out !
 
 
 void Main()
{

 var factory = new AbstractFactory(); 
 
 factory.Register<IDrive>(() => new FoulwheeledDrive());
 factory.Register<Car, string, string, IDrive>((string make, string model, IDrive drive) => new Car(make, model, drive)); 
 
 var car = factory.Create<Car, string, string, IDrive>("BMW", "M5", factory.Create<IDrive>());
 
 car.ToString().Dump();
 
 //can also unregister a factory method
 //factory.Unregister<Car, string, string, IDrive>();
 //var car2 = factory.Create<Car, string, string, IDrive>("BMW", "M5", factory.Create<IDrive>());
 
	
}

public interface IVehicle {
 string Model { get; set; }
 string Make { get; set; }
}

public interface IDrive {
 string Drive();
}

public class FoulwheeledDrive : IDrive {
 public string Drive() => "Car driving with four wheeled motion";
}

public class Car  {

  public string Make { get; set; }  
  public string Model { get; set; }
  
  public IDrive Drive { get; set; }
 
  public Car(string model, string make, IDrive drive){
    Model = model; 
 	Make = make;
	Drive = drive;
  }
  
  public override string ToString() => $"{Model} {Make} : {Drive.Drive()}";
 
}

public class AbstractFactory {

 private readonly Dictionary<Type, Delegate> _factories = new (); 
 
 
 public void Register<T>(Func<T> factory) => _factories[typeof(Func<T>)] = factory;
 public void Register<T,P>(Func<P,T> factory) => _factories[typeof(Func<P,T>)] = factory;
 public void Register<T,P1, P2>(Func<P1, P2, T> factory) => _factories[typeof(Func<P1, P2, T>)] = factory;
 public void Register<T, P1, P2, P3>(Func<P1, P2, P3, T> factory) => _factories[typeof(Func<P1, P2, P3, T>)] = factory;
 public void Register<T, P1, P2, P3, P4>(Func<T> factory) => _factories[typeof(Func<P1, P2, P3, P4, T>)] = factory;
 public void Register<T, P1, P2, P3, P4, P5>(Func<T> factory) => _factories[typeof(Func<P1, P2, P3, P4, P5, T>)] = factory;
 
 public void Unregister<T>() => _factories.Remove(typeof(Func<T>)); 
 public void Unregister<T,P>() => _factories.Remove(typeof(Func<P,T>));
 public void Unregister<T,P1, P2>() => _factories.Remove(typeof(Func<P1, P2, T>));
 public void Unregister<T,P1, P2, P3>() => _factories.Remove(typeof(Func<P1, P2, P3, T>)); 
 public void Unregister<T,P1, P2, P3, P4>() => _factories.Remove(typeof(Func<P1, P2, P3, P4, T>));
 public void Unregister<T,P1, P2, P3, P4, P5>() => _factories.Remove(typeof(Func<P1, P2, P3, P4, P5, T>));
 
 public T Create<T>() => ((Func<T>)_factories[typeof(Func<T>)])();
 public T Create<T, P>(P p) => ((Func<P,T>)_factories[typeof(Func<P,T>)])(p);
 public T Create<T, P1, P2>(P1 p1, P2 p2) => ((Func<P1, P2,T>)_factories[typeof(Func<P1, P2,T>)])(p1, p2);
 public T Create<T, P1, P2, P3>(P1 p1, P2 p2, P3 p3) => ((Func<P1, P2, P3, T>)_factories[typeof(Func<P1, P2, P3, T>)])(p1, p2, p3);
 public T Create<T, P1, P2, P3, P4>(P1 p1, P2 p2, P3 p3, P4 p4) => ((Func<P1, P2, P3, P4, T>)_factories[typeof(Func<P1, P2, P3, P4, T>)])(p1, p2, p3, p4);
 public T Create<T, P1, P2, P3, P4, P5>(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) => ((Func<P1, P2, P3, P4, P5, T>)_factories[typeof(Func<P1, P2, P3, T>)])(p1, p2, p3, p4, p5);
 
}


 
 

The registration of the factories then instantiate the instance. Note that we do not use the new operator when instantiating the object. We avoid using Activator.CreateInstance and reflection using this approach. Why use this pattern when we want to create 'blueprint' objects and want to have standard ways of instantiating them and for example avoid using concrete types. Most IoC frameworks allow you to create compound classes and supply parameters, so the abstract factory pattern seems a bit 'crude', but it is used many places still.

Friday, 1 April 2022

GraphQL in Asp.Net Core - Creating a flexible API

More and more .NET Developers have heard about GraphQL. This started as an in-house project in Facebook 2012 to provide a flexible way of sending customized data to mobile clients. Giving the clients the possible to query after tailored data meant sending less data over the wire to the mobiles with less bandwidth. As cell phones moves over to 5G networks, the issue means less and less (in urban areas with good base station coverage), however we should of course seek to always optimize our data transfer as pure bandwidth usage is always a valued thing to optimize. And added dimension is the less cost of creating APIs as we can tailor our data needs. Instead of creating methods for either returning lookup ids and then querying after entire data objects, we can project only the data we need to retrieve to present data on the mobile clients in a meaningful way. Whatever makes your boat rock for showing interest in GraphQL, this article will discuss how you can get started with GraphQL in Asp.Net Core. I have prepared a demo here:
 
  https://github.com/toreaurstadboss/AspNetCore-GraphQLDemo
 
The demo repository shows a list of the tallest mountains in the municipialites in Norway. Norway is a land of mountains and it is always to know which mountain is the very tallest in the municipiality you are visiting! (I enjoy mountain climbing and hiking now and then in my spare time). The demo page shows a text area where you can customize the data to load here. Of course we can only load the data provided for us. We can also use the Ui playground for GraphQL added for us here too:
First off, we need to grab some Nuget packages for GraphQL. We will be using Asp.Net Core 3.1. in this article.
 
        <PackageReference Include="GraphQL" Version="2.4.0" />
	<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="3.4.0" />
	<PackageReference Include="GraphQL.Server.Transports.WebSockets" Version="3.4.0" />
	<PackageReference Include="GraphQL.Server.Ui.Playground" Version="3.5.0-alpha0046" />  
 
Then we need to specify in our Startup class the needed setup.
 
Startup.cs
using AspNetCore_GraphQLDemo.GraphQL; using AspNetCore_GraphQLDemo.GraphQL.Messaging; using Data; using Data.Repositories; using GraphQL; using GraphQL.Server; using GraphQL.Server.Ui.Playground; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebSockets; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; namespace AspNetCore_GraphQLDemo { public class Startup { private readonly IWebHostEnvironment _env; public Startup(IConfiguration configuration, IWebHostEnvironment env) { _env = env; Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // If using IIS: services.Configure<IISServerOptions>(options => { options.AllowSynchronousIO = true; }); services.AddControllersWithViews(); services.AddHttpContextAccessor(); services.AddRazorPages().AddRazorRuntimeCompilation(); services.AddDbContext<MountainDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); }); services.AddScoped<IMountainRepository, MountainRepository>(); services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService)); services.AddScoped<MountainSchema>(); services.AddSingleton<MountainMessageService>(); services.AddSingleton<MountainDetailsDisplayedMessageService>(); services.AddGraphQL(x => { x.EnableMetrics = true; x.ExposeExceptions = _env.IsDevelopment(); x.SetFieldMiddleware = true; }).AddGraphTypes(ServiceLifetime.Scoped) .AddUserContextBuilder(httpContext => httpContext.User) .AddDataLoader() .AddWebSockets(); services.AddCors(options => { options.AddPolicy(name: "MyAllowSpecificOrigins", builder => { builder.AllowAnyOrigin().AllowAnyMethod(); }); }); } //static IEnumerable<Type> GetGraphQlTypes() //{ // return typeof(Startup).Assembly // .GetTypes() // .Where(x => !x.IsAbstract && // (typeof(IObjectGraphType).IsAssignableFrom(x) || // typeof(IInputObjectGraphType).IsAssignableFrom(x))); //} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { context.Response.Redirect("/Error"); context.Response.StatusCode = 500; var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var exception = exceptionHandlerPathFeature.Error; var result = JsonConvert.SerializeObject(new { error = exception.Message }); context.Response.ContentType = "application/json"; await context.Response.WriteAsync(result); }); }); app.UseStaticFiles(); app.UseRouting(); app.UseCors("MyAllowSpecificOrigins"); app.UseWebSockets(); app.UseGraphQLWebSockets<MountainSchema>("/graphql"); //app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); app.UseGraphQL<MountainSchema>(); if (env.IsDevelopment()) { app.UseGraphQLPlayground(new GraphQLPlaygroundOptions { }); } } } }
In ConfigureServices method above we register the schema for our GraphQL method.
 
 
    services.AddScoped<MountainSchema>(); 
 
 
We also add GraphQL itself and setup also web sockets (which are needed for GraphQL).
 
       services.AddGraphQL(x =>
                {
                    x.EnableMetrics = true; x.ExposeExceptions = _env.IsDevelopment(); x.SetFieldMiddleware = true; }).AddGraphTypes(ServiceLifetime.Scoped)
            .AddUserContextBuilder(httpContext => httpContext.User)
            .AddDataLoader()
            .AddWebSockets(); 
 
Just as a side note, you want to add Cors also:
 
     services.AddCors(options =>
            {
                options.AddPolicy(name: "MyAllowSpecificOrigins",
                    builder =>
                    {
                        builder.AllowAnyOrigin().AllowAnyMethod();
                    });
            });
 
Inside Configure method we add also the following to enable GraphQL:
 
           app.UseCors("MyAllowSpecificOrigins");

            app.UseWebSockets();

            app.UseGraphQLWebSockets<MountainSchema>("/graphql");

            //app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            });

            app.UseGraphQL<MountainSchema>();
            if (env.IsDevelopment())
            {
                app.UseGraphQLPlayground(new GraphQLPlaygroundOptions
                {
                    
                });
            }
        }  
 
Our Mountainschema looks like this:
MountainSchema.cs
using AspNetCore_GraphQLDemo.GraphQL.Types; using AspNetCore_GraphQLDemo.GraphQL.Types.Directives; using GraphQL; using GraphQL.Instrumentation; using GraphQL.Types; namespace AspNetCore_GraphQLDemo.GraphQL { public class MountainSchema : Schema { public MountainSchema(IDependencyResolver resolver) : base(resolver) { Query = resolver.Resolve<MountainQuery>(); Mutation = resolver.Resolve<MountainMutation>(); Subscription = resolver.Resolve<MountainSubscription>(); RegisterDirective(new LowercaseDirective()); RegisterDirective(new OrderbyDirective()); var builder = new FieldMiddlewareBuilder(); builder.Use<LowercaseFieldsMiddleware>(); builder.ApplyTo(this); builder.Use(next => { return context => { return next(context).ContinueWith(x => { var c = context; var result = x.Result; result = OrderbyQuery.OrderIfNecessary(context, result); return result; }); }; }); builder.ApplyTo(this); //builder.Use<CustomGraphQlExecutor<MountainSchema>>(); //builder.ApplyTo(this); } } }
We pass in a IDependencyResolver (dependency!) into the constructor and resolve the classes we desire (we inherit from Schema class). We wire up our schema here to the Query, Mutation and Subscription we desire and register directives. Here is how the Query property is set:
 
MountainQuery.cs
using AspNetCore_GraphQLDemo.GraphQL.Types; using Data; using Data.Repositories; using GraphQL.Types; namespace AspNetCore_GraphQLDemo.GraphQL { public class MountainQuery : ObjectGraphType { public MountainQuery(IMountainRepository mountainRepository) { Field<ListGraphType<MountainType>>("mountains", resolve: context => mountainRepository.GetAll() ); FieldAsync<MountainType>("mountain", arguments: new QueryArguments(new QueryArgument<NonNullGraphType<MountainIdInputType>> {Name = "id"}), resolve: async context => { var mountain = context.GetArgument<MountainInfo>("id"); var mountainFromDb = await mountainRepository.GetById(mountain.Id); return mountainFromDb; }); //FieldAsync<MountainType>("selectmountain", // arguments: new QueryArguments(new QueryArgument(typeof(int)) { Name = "id" }), // resolve: async context => // { // var mountain = context.GetArgument<MountainInfo>("id"); // var mountainFromDb = await mountainRepository.GetById(mountain.Id); // return mountainFromDb; // }); //sadly, we need to inherit from IGraphType and cannot just have simple scalar arguments in GraphQL.Net.. } } }
As you can see, we can define multiple queries. We inherit from ObjectGraphType and pass in a IMountainRepository. This is an interface for your repository, which fetches data via Entity Framework Core and you can then load data into GraphQL from the local database (The DEMO uses Sql Server (SQLEXPRESS)) via EF Core in a simple manner by only providing the repo via dependency injection. We define via the methods Field and FieldAsync our methods (note the use of string constants as a string value we can use in GraphQL queries of ours that resides in the Schema) and the resolve lambda tells how data is to be fetched. We can specify arguments also. The "mountain" FieldAsync method also accepts arguments via the
arguments lambda and this allows us parameterized access to our data. Over to the Subscription property. It looks like this:
 
using AspNetCore_GraphQLDemo.GraphQL.Messaging;
using AspNetCore_GraphQLDemo.GraphQL.Types;
using GraphQL.Resolvers;
using GraphQL.Types;

namespace AspNetCore_GraphQLDemo.GraphQL
{
    public class MountainSubscription : ObjectGraphType
    {
        public MountainSubscription(MountainDetailsDisplayedMessageService mountainDetailsDisplayedMessageService)
        {
            Name = "Subscription";
            AddField(new EventStreamFieldType
            {
                Name = "detailsDisplayed",
                Type = typeof(MountainDetailsMessageType),
                Resolver = new FuncFieldResolver<MountainDetailsMessage>(c => c.Source as MountainDetailsMessage),
                Subscriber = new EventStreamResolver<MountainDetailsMessage>(c => mountainDetailsDisplayedMessageService.GetMessages())
            });
        }
    }
}
 
 
Here we inherit from ObjectGraphType (as we did for Query) and we use the MountainDetailsDisplayedMessageService. This was added as a (concrete class) singleton in the Startup.cs file. The message service uses RxJs serverside to handle the Pub-sub pattern of the subscriber. We are using System.Reactive.Subjects here.
 
MountainSubscription.cs
using System; using System.Reactive.Linq; using System.Reactive.Subjects; namespace AspNetCore_GraphQLDemo.GraphQL.Messaging { public class MountainDetailsDisplayedMessageService { private readonly ISubject<MountainDetailsMessage> _messageStream = new ReplaySubject<MountainDetailsMessage>(1); public MountainDetailsMessage AddMountainDetailsMessage(int id) { var message = new MountainDetailsMessage { Id = id }; _messageStream.OnNext(message); return message; } public IObservable<MountainDetailsMessage> GetMessages() { return _messageStream.AsObservable(); } } }
The mutation looks like this:
MountainMutation.cs
using AspNetCore_GraphQLDemo.GraphQL.Messaging; using AspNetCore_GraphQLDemo.GraphQL.Types; using Data; using Data.Repositories; using GraphQL.Types; namespace AspNetCore_GraphQLDemo.GraphQL { public class MountainMutation : ObjectGraphType { public MountainMutation(IMountainRepository mountainRepository, MountainMessageService mountainMessageService) { FieldAsync<MountainType>("createMountain", arguments: new QueryArguments( new QueryArgument<NonNullGraphType<MountainInputType>> {Name = "mountain"}), resolve: async context => { var mountain = context.GetArgument<MountainInfo>("mountain"); await mountainRepository.AddMountain(mountain); mountainMessageService.AddMountainAddedMessage(mountain); return mountain; }); FieldAsync<MountainType>("removeMountain", arguments: new QueryArguments( new QueryArgument<NonNullGraphType<MountainIdInputType>> { Name = "id" }), resolve: async context => { var mountain = context.GetArgument<MountainInfo>("id"); await mountainRepository.RemoveMountain(mountain.Id); return mountain; }); } } }
We can create a mountain like this in GraphQL Query:
 
 mutation {
  createMountain(mountain: {
   county: "Svalbard"
  muncipiality: "Svalbard"
  officialName: "Newtontoppen"
  referencePoint: "Isbjønn på toppen"
  comments: "Husk rask snøskuter",
  metresAboveSeaLevel: "1713",
  primaryFactor: "1713"
  }) {    
    id
  }
} 
  
 
And we can remove a mountain (don't we all?) like this:
 

# Write your query or mutation here
mutation {
  removeMountain(id: {
    id: 370
  }) { id }
}
  
 
If you clone the repo you will find more source code concerning directives such as lowercase and sorting. As you saw in MountainSchema I use the FieldMiddlewareBuilder to do the sorting as this needs to tap into the pipeline more of GraphQL.Net. We also need some more code - for the client side of course. The client side code relies on Apollo Client lib like this:
 
index.cshtml
<script src="https://unpkg.com/apollo-client-browser@1.7.0"></script>
The libman.json file (the similar file to package.json when it comes to specifying client-side libraries in .net core mvc solutions) of the demo solution looks like this I have used looks like this:
 
libman.json
{ "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "library": "twitter-bootstrap@4.2.1", "destination": "wwwroot/lib/bootstrap", "files": [ "js/bootstrap.bundle.js", "css/bootstrap.min.css" ] }, { "library": "jquery@3.3.1", "destination": "wwwroot/lib/jquery", "files": [ "jquery.min.js" ] }, { "provider": "unpkg", "library": "font-awesome@4.7.0", "destination": "wwwroot/lib/font-awesome/" }, { "provider": "unpkg", "library": "toastr@2.1.4", "destination": "wwwroot/lib/toastr/" } ] }
We then need some client side code to load data from GraphQL server of ours.
 
  <script>

    function LoadGraphQLDataIntoUi(result) {

        var tableBody = $("#mountainsTableBody");
        tableBody.empty();

        var tableHeaderRow = $("#mountainsTableHeaderRow");
        tableHeaderRow.empty();

        var rowIndex = 0;

        result.data.mountains.forEach(mountain => {

            if (rowIndex == 0) {
                Object.keys(mountain).forEach(key => {
                    if (key === '__typename') {
                        return;
                    }
                    tableHeaderRow.append(`<th>${key}</th>`);
                });;
            }

            tableBody.append('<tr>');

            Object.keys(mountain).forEach(key => {
                if (key === '__typename') {
                    return;
                }
                if (key === 'id') {
                    tableBody.append(`<td><a href='/home/mountaindetails/?id=${mountain[key]}'><i class='fa fa-arrow-right'></i></a> ${mountain[key]}</td>`);
                    return;
                }
                tableBody.append(`<td>${mountain[key]}</td>`);

            });

            tableBody.append('</tr>');

            rowIndex++;

        });

        toastr.success('Loaded GraphQL data from server into the UI successfully.');


    }

    $("#btnConnect").click(function () {
        ConnectDemo();

    });


    $("#btnLoadData").click(function () {
        var gqlQueryContents = $("#GraphQLQuery").val();
        LoadGraphQLData(gqlQueryContents, LoadGraphQLDataIntoUi);
        toastr.info('Retrieving data from API using GraphQL.');
    });

    $(document).ready(function () {

        console.log('loading');

        var initialQuery = `
                {
                    mountains {
                        id
                        fylke: county
                        kommune: muncipiality
                        hoydeOverHavet: calculatedMetresAboveSeaLevel
                        offisieltNavn: officialName
                        primaerfaktor: calculatedPrimaryFactor
                        referansePunkt: referencePoint
                    }
                }`;

        $("#GraphQLQuery").val(initialQuery);

    });

</script>
 
 
And then a method using Apollo client lib to load the data:
 
 /**
 * Loads GraphQL data specified by query expression and passes the 'result' array to the callBackFunction
 * callBackFunction should be Js method (function) that accept one parameter, preferably called result, which is an object
 * that contains a result.data object.
 */
function LoadGraphQLData(gqlQuery, callBackFunction) {

    var apolloClient = new Apollo.lib.ApolloClient({
        networkInterface: Apollo.lib.createNetworkInterface({
            uri: 'http://localhost:2542/graphql',
            transportBatching: true,
        }), connectToDevTools: true
    });
    var query = Apollo.gql(gqlQuery);

    apolloClient.query({
        query: query,
        variables: {}
    }).then(result => {
        callBackFunction(result);
    }).catch(error => {
        //debugger
        toastr.error(error, 'GraphQL loading failed');
    });
}