Using multiple FHIR versions in one project

In a .NET project you may need to work with multiple FHIR versions at the same time (for example, STU3 and R4). This raises the question: how do you distinguish a Patient from STU3 and a Patient from R4?

The recommended approach is to add assembly aliases to the different Firely .NET SDK packages and then use extern alias in your C# files. The following csproj snippet shows how to assign aliases to each FHIR package:

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
         <PackageReference Include="Hl7.Fhir.STU3" Version="6.0.1" Aliases="stu3" />
         <PackageReference Include="Hl7.Fhir.R4" Version="6.0.1" Aliases="r4" />
         <PackageReference Include="Hl7.Fhir.R4B" Version="6.0.1" Aliases="r4b" />
         <PackageReference Include="Hl7.Fhir.R5" Version="6.0.1" Aliases="r5" />
</ItemGroup>

</Project>

To use both the STU3 and R4 versions of Patient in the same C# file, declare the extern alias identifiers at the top of the file. This creates a new root-level namespace for each aliased assembly, which lets you refer to types from each version explicitly:

extern alias r4;
extern alias stu3;

namespace MultipleVersions
{
         // Somewhere inside a method:
         var patientR4 = new r4::Hl7.Fhir.Model.Patient();
         var patientSTU3 = new stu3::Hl7.Fhir.Model.Patient();
}

To avoid repeating the fully qualified names, you can introduce a using alias for the model namespace of each version:

using R4 = r4::Hl7.Fhir.Model;

 // and then later...
var patient = new R4.Patient():

Putting it all together:

extern alias r4;
extern alias stu3;

using System;
using Hl7.Fhir.Model;
using R4 = r4::Hl7.Fhir.Model;
using STU3 = stu3::Hl7.Fhir.Model;

namespace MultipleVersions
{
        class Program
        {
                static void Main(string[] args)
                {
                        Console.WriteLine("Hello World!");

                        var code = new Code();

                        var patient1 = new STU3.Patient();
                        var patient2 = new R4.Patient();

                }
        }
}

Dealing with Specification.zip

Although the preferred way to work with FHIR metadata in the SDK is to use the FhirPackageSource, older versions relied on a specification.zip file. Because different SDK packages can reference the same physical specification.zip, using those packages together can cause conflicts. You can avoid this by giving each referenced specification.zip a unique name at build time:

<ItemGroup>
           <PackageReference Include="Hl7.Fhir.Specification.Data.STU3" Version="5.5.0" GeneratePathProperty="true" ExcludeAssets="contentFiles" />
           <PackageReference Include="Hl7.Fhir.Specification.Data.R4" Version="5.5.0" GeneratePathProperty="true" ExcludeAssets="contentFiles" />
</ItemGroup>

<ItemGroup>
           <Content Include="$(PkgHl7_Fhir_Specification_Data_STU3)\contentFiles\any\any\specification.zip">
                           <Link>specification_STU3.zip</Link>
                           <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
                           <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
                           <Pack>false</Pack>
           </Content>
           <Content Include="$(PkgHl7_Fhir_Specification_Data_R4)\contentFiles\any\any\specification.zip">
                           <Link>specification_R4_0.zip</Link>
                           <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
                           <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
                           <Pack>false</Pack>
           </Content>
</ItemGroup>

The GeneratePathProperty property exposes the package path so you can link each specification.zip under a unique file name that includes the FHIR version. When the project is built, these files are copied to the output folder with their new names; you can then load the appropriate zip file when creating a validation resolver:

IResourceResolver zipSource = fhirVersion switch
{
                FHIRVersion.N3_0 =>
                                stu3::Hl7.Fhir.Specification.Source.ZipSource.CreateValidationSource(Path.Combine(CommonDirectorySource.SpecificationDirectory, "specification_STU3.zip")),
                FHIRVersion.N4_0 =>
                                r4::Hl7.Fhir.Specification.Source.ZipSource.CreateValidationSource(Path.Combine(CommonDirectorySource.SpecificationDirectory, "specification_R4_0.zip")),
                _ => throw new NotSupportedException()
}

For more information, see Brian Postlethwaite’s blog post on using multiple FHIR versions with the Firely SDK.