Code modules in Pims365

Code modules usually are used as RouteHandlers, Data Import/Export and Jobs

Examples bellow are created using Pims365

Minimal example of a RouteHandler

A standard RouteHandler as of 2019 uses RouteHandlerBase instead of the previous ReusableRouteHandlerBase

Like all good tutorials we start with a public RouteHandler which will return the text "Hello World!"

using System.Threading; using System.Threading.Tasks; using Appframe365.Web; using Appframe365.Web.Context; using Appframe365.Web.RouteHandlers; using Appframe365.Web.Registries.Attributes; using Appframe365.Web.JSON; namespace RouteHandlers { [RouteUrl("api/docs/helloworld")] public class HelloWorld : RouteHandlerBase { protected override async Task ProcessAsync(RequestContext pContext, CancellationToken ct) { JSON.SerializeToResponse("Hello world!", pContext); await Task.FromResult(true); } } }

Passing data to a RouteHandler

There are many ways of passing data to a RouteHandler

By far the most common are RouteHandler Parameters and JSON

RouteHandler Parameters
using System.Threading; using System.Threading.Tasks; using Appframe365.Web; using Appframe365.Web.Context; using Appframe365.Web.RouteHandlers; using Appframe365.Web.Registries.Attributes; using Appframe365.Web.JSON; namespace RouteHandlers { [RouteUrl("api/docs/hello/{Target}/{Parameter}")] //Router with two parameters named Target and Parameter public class HelloWorld : RouteHandlerBase { private class RouteParameters : RouteParametersBase { public string Target { get; set; } //Parameter name needs to match a name in the RouteURL public string Parameter { get; set; } //Parameter name needs to match a name in the RouteURL public RouteParameters(RequestContext pContext) : base(pContext) { } } protected override async Task ProcessAsync(RequestContext pContext, CancellationToken ct) { //Create an instance of RouteParameters RouteParameters vParameters = new RouteParameters(pContext); //Serialize to Response JSON.SerializeToResponse("Hello " + vParameters.Target + ". Parameter: " + vParameters.Parameter + "!", pContext); await Task.FromResult(true); } } }
Passing JSON Data
using System.Collections; using System.Collections.Generic; //Needed for Dictionary using System.Threading; using System.Threading.Tasks; using Appframe365.Web; using Appframe365.Web.Context; using Appframe365.Web.RouteHandlers; using Appframe365.Web.Registries.Attributes; using Appframe365.Web.JSON; namespace RouteHandlers { [RouteUrl("api/docs/hellojson")] public class HelloWorld : RouteHandlerBase { protected override async Task ProcessAsync(RequestContext pContext, CancellationToken ct) { //Create a reference to pContext.JsonParameters to shorten writing :P var vJsonParams = pContext.JsonParameters; //pContext.JsonParameters is a Dictionary of string, object, lookup Dictionary on MSDN for more info. string vJsonTarget; if(vJsonParams.ContainsKey("Target")) { vJsonTarget = vJsonParams["Target"].ToString(); } else { vJsonTarget = "Default Value if Target isn't provided"; } //This can be shortened down like this: vJsonTarget = vJsonParams.ContainsKey("Target") ? vJsonParams["Number"].ToString() : "Default Value if Target isn't provided";; //Serialize to Response JSON.SerializeToResponse("Hello " + vJsonTarget + "!", pContext); await Task.FromResult(true); } } }

Communicating with the Database

There are many ways of communicating with the database but here are the most common

Data management in Code Modules
using System; //Needed for GUID; using System.Data; //Needed for DataTable using System.Collections.Generic; //Needed for Dictionary using System.Threading; using System.Threading.Tasks; using Appframe365.Web; using Appframe365.Web.Context; using Appframe365.Web.RouteHandlers; using Appframe365.Web.Registries.Attributes; using Appframe365.Web.JSON; using Appframe365.Common.Data; namespace RouteHandlers { [RouteUrl("api/docs/hello/{Operation}")] [RouteConstraint("Operation", "(create|retrieve|update|destroy)")] //Constrain the Operation to the following options //Why create, retrieve, update and destroy, google CRUD if you're insterrested, TL;DR is that we use it internally in Appframe :P public class HelloWorld : RouteHandlerBase { private class RouteParameters : RouteParametersBase { public string Operation { get; set; } public RouteParameters(RequestContext pContext) : base(pContext) { } } protected override async Task ProcessAsync(RequestContext pContext, CancellationToken ct) { //Create an instance of RouteParameters RouteParameters vParameters = new RouteParameters(pContext); //Retrieve current UserContext var vUserContext = UserContext.ForHttpContext(pContext); switch(vParameters.Operation) { case "create": //Same as INSERT in SQL Dictionary vNewRecord = new Dictionary(); vNewRecord.Add("Navn", "TestLokasjon"); vNewRecord.Add("Hmsregnr", 123456); vNewRecord.Add("GeoJson", "{\"type\":\"Point\",\"coordinates\":[10.799933,59.898145]}"); vUserContext.AddData("atbv_Hmsreg_Lokasjoner", vNewRecord, null, "atbl_Hmsreg_Lokasjoner"); break; case "retrieve": //Same as SELECT in SQL afRecordSource vSource = new afRecordSource("aviw_Hmsreg_Lokasjoner"); vSource.MaxRecords = 1; vSource.SelectOnly("Navn", "Hmsregnr", "GeoJson"); DataTable vDataTable = vUserContext.GetData(vSource); JSON.SerializeToResponse(vDataTable, pContext); break; case "update": //SAME as UPDATE in SQL Dictionary vNewValues = new Dictionary(); vNewValues.Add("Navn", "TestLokasjon"); vNewValues.Add("Hmsregnr", 123456); vNewValues.Add("GeoJson", "{\"type\":\"Point\",\"coordinates\":[10.799933,59.898145]}"); vUserContext.PutData("atbv_Hmsreg_Lokasjoner", vNewValues, null, "atbl_Hmsreg_Lokasjoner", new Guid(), null); break; case "destroy": //SAME as DELETE in SQL vUserContext.DelData("atbv_Hmsreg_Lokasjoner", new Guid()); break; } await Task.FromResult(true); } } } //Try to ignore these tags... formatting errors

Async Exception Handling

Anyone who have tried writing new Code Modules will know that using the old ErrorHandler methods doesn't work very well...
So here's one way to work around it...

using System; //Needed for Exception; using System.Threading; using System.Threading.Tasks; using Appframe365.Web; using Appframe365.Web.Context; using Appframe365.Web.RouteHandlers; using Appframe365.Web.Registries.Attributes; using Appframe365.Web.JSON; using Appframe365.Common.Data; namespace RouteHandlers { [RouteUrl("api/docs/hello/exceptions")] public class HelloWorld : RouteHandlerBase { protected override async Task ProcessAsync(RequestContext pContext, CancellationToken ct) { //This seems very messy but its the only way I have found to deal with async and try catch... Exception exampleException = null; try { /* Some code which can fail goes here */ } catch (Exception ex) { exampleException = ex; //Can't use await inside a catch statement... //So we move the exception out and handle it after... } if(exampleException != null) { await ErrorHandler.HandleExceptionAsync(exampleException, pContext); await ErrorHandler.WriteJSONErrorToResponseAsync(exampleException, pContext); } await Task.FromResult(true); } } }