Arbeidstilsynet.Common.AltinnApp 2.6.0

dotnet add package Arbeidstilsynet.Common.AltinnApp --version 2.6.0
                    
NuGet\Install-Package Arbeidstilsynet.Common.AltinnApp -Version 2.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Arbeidstilsynet.Common.AltinnApp" Version="2.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Arbeidstilsynet.Common.AltinnApp" Version="2.6.0" />
                    
Directory.Packages.props
<PackageReference Include="Arbeidstilsynet.Common.AltinnApp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Arbeidstilsynet.Common.AltinnApp --version 2.6.0
                    
#r "nuget: Arbeidstilsynet.Common.AltinnApp, 2.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Arbeidstilsynet.Common.AltinnApp@2.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Arbeidstilsynet.Common.AltinnApp&version=2.6.0
                    
Install as a Cake Addin
#tool nuget:?package=Arbeidstilsynet.Common.AltinnApp&version=2.6.0
                    
Install as a Cake Tool

Introduction

A collection of common patterns and extensions for cross-cutting concerns in Altinn applications.

📖 Installation

To install the package, you can use the following command in your terminal:

dotnet add package Arbeidstilsynet.Common.AltinnApp

🚀 Features

  • Extension Methods for common Altinn operations
  • Abstract Data Processors for handling data changes
  • Country Code Lookup with country dialing codes and names
  • Altinn Options Provider for country dropdowns
  • Embedded Resource Utilities for JSON data

🧑‍💻 Usage

Dependency Injection Setup

Altinn Application Utilities

Add services to your service collection:

public void ConfigureServices(IServiceCollection services)
{
    // Add country code lookup functionality
    services.AddLandskoder();
    
    // OR add country options for Altinn dropdowns (includes AddLandskoder)
    services.AddLandOptions();

}

Structured Data

1. Add the structured data extension to your service collection, specifying the data model and structured data types
    // Adds a DataElement to you instance to hold structured data of type TStructuredData
    // Content type will be "application/json".
    services.AddStructuredData<TDataModel, TStructuredData>((datamodel) =>
    {
        return new TStructuredData
        {
            // Property mappings here
        };
    });
2. Add the data element to your App/config/applicationmetadata.json dataTypes
{
  "dataTypes": [
    {
      "id": "structured-data",
      "allowedContentTypes": [
        "application/json"
      ],
      "allowedContributors": [
        "app:owned"
      ]
    },
    /// ...Other data types
  ]
}

Extension Methods

Instance Extensions

Easily extract common information from Altinn instances:

using Arbeidstilsynet.Common.AltinnApp.Extensions;

public class MyService
{
    public void ProcessInstance(Instance instance)
    {
        // Extract instance GUID
        Guid instanceId = instance.GetInstanceGuid();
        
        // Get application name
        string appName = instance.GetAppName();
        
        // Get party ID of instance owner
        int partyId = instance.GetInstanceOwnerPartyId();
        
        Console.WriteLine($"Processing {appName} instance {instanceId} for party {partyId}");
    }
}
DataClient Extensions

Simplified data operations with Altinn instances:

using Arbeidstilsynet.Common.AltinnApp.Extensions;

public class DataService
{
    private readonly IDataClient _dataClient;
    
    public DataService(IDataClient dataClient)
    {
        _dataClient = dataClient;
    }
    
    public async Task<MyDataModel?> GetFormData(Instance instance)
    {
        // Get form data with default data type "structured-data"
        return await _dataClient.GetSkjemaData<MyDataModel>(instance);
        
        // Or specify custom data type
        return await _dataClient.GetSkjemaData<MyDataModel>(instance, "customDataType");
    }
    
    public async Task CleanupAttachments(Instance instance, List<DataElement> unusedAttachments)
    {
        foreach (var attachment in unusedAttachments)
        {
            // Delete data element and remove from instance
            await _dataClient.DeleteElement(instance, attachment, delay: true);
        }
    }
}
Assembly Extensions

Load and deserialize embedded JSON resources:

using Arbeidstilsynet.Common.AltinnApp.Extensions;

public class ConfigurationService
{
    public Task<MyResourceType> GetResource()
    {
        var assembly = Assembly.GetExecutingAssembly();
        
        // Load embedded JSON resource and deserialize
        return assembly.GetEmbeddedResource<MyResourceType>("resource.json");
    }
}

Country Code Lookup

Use the built-in country code lookup service:

public class AddressService
{
    private readonly ILandskodeLookup _landskodeLookup;
    
    public AddressService(ILandskodeLookup landskodeLookup)
    {
        _landskodeLookup = landskodeLookup;
    }
    
    public async Task<string> GetCountryInfo(string countryCode)
    {
        // Look up specific country by ISO code
        var country = await _landskodeLookup.GetLandskode("NOR");
        if (country != null)
        {
            return $"{country.Land} ({country.Kode})"; // "Norway (+47)"
        }
        
        return "Unknown country";
    }
}

Abstract Data Processors

Create data processors that respond to form data changes:

Simple Data Processor
using Arbeidstilsynet.Common.AltinnApp.Abstract.Processing;

public class MyFormDataProcessor : BaseDataProcessor<MyDataModel>
{
    private readonly ILogger<MyFormDataProcessor> _logger;
    
    public MyFormDataProcessor(ILogger<MyFormDataProcessor> logger)
    {
        _logger = logger;
    }
    
    protected override async Task ProcessData(MyDataModel current, MyDataModel? previous)
    {
        if (previous == null)
        {
            _logger.LogInformation("New form data created");
            return;
        }
        
        _logger.LogInformation("Form data updated");
        
        // Your custom processing logic here
    }
}
Member-Specific Processor
public class EmailChangeProcessor : MemberProcessor<MyDataModel, string>
{    
    protected override string? AccessMember(MyDataModel dataModel)
    {
        return dataModel.Email;
    }
    
    protected override async Task ProcessMember(
        string? currentEmail, 
        string? previousEmail, 
        MyDataModel currentDataModel, 
        MyDataModel previousDataModel)
    {
        // Your custom logic to handle the changed member
    }
}
List Processor
public class AttachmentListProcessor : ListProcessor<MyDataModel, AttachmentInfo>
{
    protected override List<AttachmentInfo>? AccessMember(MyDataModel dataModel)
    {
        return dataModel.Attachments; // Point to the list we want to process
    }
    
    protected override async Task ProcessListChange(
        List<AttachmentInfo>? currentList, 
        List<AttachmentInfo>? previousList, 
        MyDataModel currentDataModel, 
        MyDataModel previousDataModel)
    {
        // Process that attachments have been removed or added
    }
    
    protected override async Task ProcessItem(
        AttachmentInfo currentItem, 
        AttachmentInfo previousItem, 
        MyDataModel currentDataModel, 
        MyDataModel previousDataModel)
    {
        // Process individual item changes
    }
}

Registration of Data Processors

Register your data processors in dependency injection:

public void ConfigureServices(IServiceCollection services)
{
    // Register data processors
    services.AddTransient<IDataProcessor, MyFormDataProcessor>();
    services.AddTransient<IDataProcessor, EmailChangeProcessor>();
    services.AddTransient<IDataProcessor, AttachmentListProcessor>();
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.6.0 87 3/6/2026
2.5.0 83 2/25/2026
2.4.1 105 2/18/2026
2.4.1-beta1 87 2/18/2026

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added <!-- for new features. -->

### Changed <!--  for changes in existing functionality. -->

### Deprecated <!--  for soon-to-be removed features. -->

### Removed <!-- for now removed features. -->

### Fixed <!-- for any bug fixes. -->

### Security <!-- in case of vulnerabilities. -->

## 2.6.0

### Changed

- changed(deps): Applied minor and patch updates to dependencies

## 2.5.0

### Changed

- ListProcessor no longer emits ProcessList -- only ProcessItem, and items are compared using AltinnRowId. If you need to process the list as a whole, you should use MemberProcessor pointing to the list in question.

## 2.4.1

### Changed

- chore: moved package to nuget.org

## 2.4.0

- changed: Differentiate structured data and main content.
 - More configurable, though defaults should for most.
 - The altinn app will declare which data elemnts are the main content, and structured data, using data values

## 2.3.0

- added: PreSubmitProcessor to make it easier to implement custom pre-submit logic in AltinnApps.
- added: A few more extension methods for `IApplicationClient` and `IDataClient`

## 2.2.0

- added: Option to disable deletion of app datamodel after mapping structured data. This can be useful for testing and some legacy use cases.

## 2.1.0

### Changed

- changed(deps): Applied minor and patch updates to dependencies

## 2.0.0

### Changed

- changed(deps): Major dotnet updated (v10)

## 1.5.1

- added: Option to include error details related to mapping structured data in the validation response. This intended for development environments.

## 1.5.0

- added: Extension to manage DataElements for structured data. It deletes the xml-based DataModel, and replaces it with application/json of a user-provided type.

## 1.4.1

### Changed

- applied new default skjema type `structured-data` as default

## 1.4.0

- added: More customization options for LandOptions
 - Custom order function for country list in LandOptions (default is alphabetical by country name)
 - Option for using either alpha2 or alpha3 country codes as value in dropdowns (default is alpha3)

## 1.3.0

### Changed

- changed: Moved all AltinnApp logic (from the Altinn package) to this new dedicated package, only intended to be used by AltinnApps.