X-Road Type Providers in C# Projects
Although Type Providers are very F# specific feature, it is possible, with a small amount of work, to use X-Road provider goodness in C# projects too. This article describes neccessary steps to make X-Road providers functionality available to C# projects.
Separate F# Project for X-Road Dependencies
Type providers are F# language specific feature, so we cannot do it without a F# project. But since the setup is very minimal, you do not need to be familiar with F# programming language to get started. You can relate to this project as a specific DSL for X-Road service interface generation.
Using Visual Studio, add new F# library project to your solution. Add dependency to
XRoadProvider NuGet package. Remove all placeholder
code, that was generated by the template (there should not be too much). Add new F# source file
Producers.fs
(or whatever name suits you best). Write following initialization code, which generates
the service interfaces:
namespace MyProject.Externals
open XRoad.Providers
let [<Literal>] WsdlLocalFile = __SOURCE_DIRECTORY__ + "/path/to/file.wsdl"
type SomeSystem = XRoadProducer<WsdlLocalFile>
Given that your WSDL file is correct and uses features that XRoadProvider
understands, the project
should be ready to compile. Also you can reference it from your C# project and use the type SomeSystem
and its children to access provided X-Road services.
Tips and Tricks
WSDL files can be big and you may have a lot of different external dependencies. That makes compilation of external dependencies F# project possibly very time consuming. Since WSDL should change quite rarely in production, then there is usually no need for recompilation of the project every time.
Instead, it is recommended to exclude this project from solutions compilation under Configuration Management and recompile the project manually when there is really a need for it (new dependencies or changes in WSDL files).
Also, you should track the project output assemblies in source code repository, so that every developer should not have to compile their own version the library.
When versioning the project assemblies we are only interested in the project own output not its dependencies. The dependencies are usually not needed, because referencing projects have to define their own dependencies to same packages, if they need to use them.
That allows us to keep F# project output folder clean, by making all NuGet dependencies not private. Modify your F# project file, so that its dependencies are not copied to output directory. If you do it manually, you have to change your project file dependencies to non-private.
<Reference Include="FSharp.Core"> <HintPath>..\..\packages\FSharp.Core\lib\net45\FSharp.Core.dll</HintPath> <Private>False</Private> </Reference> <Reference Include="System.ValueTuple"> <HintPath>..\..\packages\System.ValueTuple\lib\net461\System.ValueTuple.dll</HintPath> <Private>False</Private> </Reference> <Reference Include="XRoadProvider"> <HintPath>..\..\packages\XRoadProvider\lib\net461\XRoadProvider.dll</HintPath> <Private>False</Private> </Reference>
In MSBuild 15 style project files, the same behavior can be achieved by using following property value:
<PropertyGroup> <CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies> </PropertyGroup>
Since type provider produces a static root type and nests all neccessary types under it in hierarchy, the fully qualified name of the type can be very long, and usage of the types becomes hard.
This can be handled by using C# feature called static using which allows to import subtypes of static type into current namespace without requiring full qualification of parent types:
using static MyProject.Externals.Producers.SomeSystem.DefinedTypes.SomeXRoadEu; using static MyProject.Externals.Producers.SomeSystem.DefinedTypes.SomeXRoadEu.SomeServiceRequestType; using static MyProject.Externals.Producers.SomeSystem.portService;