LinkIt 2.0.0

dotnet add package LinkIt --version 2.0.0
NuGet\Install-Package LinkIt -Version 2.0.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="LinkIt" Version="2.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add LinkIt --version 2.0.0
#r "nuget: LinkIt, 2.0.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.
// Install LinkIt as a Cake Addin
#addin nuget:?package=LinkIt&version=2.0.0

// Install LinkIt as a Cake Tool
#tool nuget:?package=LinkIt&version=2.0.0

Getting Started

To execute this example, see LinkIt.Samples.

Linked Source

A linked source is a class that has a property called Model, and other properties called link targets. For example, let's say you have these two classes that you fetched separately by IDs.

public class Media {
    public int Id { get; set; }
    public string Title { get; set; }
    public List<int> TagIds { get; set; } //Tag references
}

public class Tag {
    public int Id { get; set; }
    public string Name { get; set; }
}

The linked source representing a media with its tags would be a class like this.

public class MediaLinkedSource : ILinkedSource<Media> {
    public Media Model { get; set; }
    public List<Tag> Tags { get; set; }
}

Usage

Load link by ID

var actual = _loadLinkProtocol.LoadLink<MediaLinkedSource>().ById(1);

Load link by IDs

var actual = _loadLinkProtocol.LoadLink<MediaLinkedSource>().ByIds(
    new List<int>{1, 2, 3}
);

However, prior to being able to use _loadLinkProtocol.LoadLink, you need to define a reference loader and a load link protocol.

ReferenceLoader

LinkIt can load objects spread across many data sources (web services, SQL databases, NoSQL databases, in-memory caches, file systems, Git repositories, etc.) and link them together as long as those objects can be fetched by IDs. LinkIt is not responsible for defining how the objects are loaded, you must define this process for each possible reference type in a reference loader.

Here is a reference loader that loads fake data for our example.

public class FakeReferenceLoader:IReferenceLoader
{
    public void LoadReferences(LookupIdContext lookupIdContext, LoadedReferenceContext loadedReferenceContext){
        foreach (var referenceType in lookupIdContext.GetReferenceTypes()){
            LoadReference(referenceType, lookupIdContext, loadedReferenceContext);
        }
    }

    private void LoadReference(Type referenceType, ILookupIdContext lookupIdContext, ILoadedReferenceContext loadedReferenceContext)
    {
        if (referenceType == typeof(Media)) {
            LoadMedia(lookupIdContext, loadedReferenceContext);
        }
        if (referenceType == typeof(Tag)){
            LoadTags(lookupIdContext, loadedReferenceContext);
        }
    }

    private void LoadMedia(ILookupIdContext lookupIdContext, ILoadedReferenceContext loadedReferenceContext) {
        var lookupIds = lookupIdContext.GetReferenceIds<Media, int>();
        var references = lookupIds
            .Select(id =>
                new Media{
                    Id = id,
                    Title = "title-" + id,
                    TagIds = new List<int> { 
                        1000+id,
                        1001+id
                    }
                }
            )
            .ToList();

        loadedReferenceContext.AddReferences(
            references,
            reference => reference.Id
        );
    }

    private void LoadTags(ILookupIdContext lookupIdContext, ILoadedReferenceContext loadedReferenceContext){
        var lookupIds = lookupIdContext.GetReferenceIds<Tag, int>();
        var references = lookupIds
            .Select(id=>
                new Tag {
                    Id = id, 
                    Name = id+"-name"
                }
            )
            .ToList();
        
        loadedReferenceContext.AddReferences(
            references,
            reference => reference.Id
        );
    }

    public void Dispose(){
        //In case you need to dispose database connections or other resources.
 
        //Will always be invoked as soon as the load phase is completed or
        //if an exception is thrown
    }
}

ReferenceLoader and the Select N + 1 Problem

LinkIt will always provide all lookup IDs for a reference type in one batch; avoiding the select N + 1 problem.

For example, if we load link the MediaLinkedSource for IDs "one" and "two", and the state of those media is

[
    {
        "id":"one",
        "title":"title-one",
        "tagIds":[1,2]
    },
    {
        "id":"two",
        "title":"title-two",
        "tagIds":[2,3]
    }
]

the lookup IDs for Tag would then be 1,2,3.

Note that lookup IDs are always provided without duplicates and without null IDs. Moreover, all the lookup IDs for the same reference type will always be loaded in one batch regardless of the complexity of the linked sources.

LoadLinkProtocol

A load link protocol has to be defined in order to load and link a linked source. Here is the protocol configuration for MediaLinkedSource.

public class MediaLinkedSourceConfig : ILoadLinkProtocolConfig {
    public void ConfigureLoadLinkProtocol(LoadLinkProtocolBuilder loadLinkProtocolBuilder) {
        loadLinkProtocolBuilder.For<MediaLinkedSource>()
            .LoadLinkReferenceById(
                linkedSource => linkedSource.Model.TagIds,
                linkedSource => linkedSource.Tags
            );
    }
}

However, we favor convention over configuration.

By default, the load link protocol is defined by convention

  • when a model property name with the suffix Id or Ids matches a link target name
  • when a model property name is the same as a link target name

Since the model property Model.TagIds match the link target Tags, the MediaLinkedSourceConfig defined above is unnecessary and can be omitted.

When invoking loadLinkProtocolBuilder.Build, all the assemblies passed by argument will be scanned in order to apply the conventions and the configuration classes implementing ILoadLinkProtocolConfig. If a convention conflicts with a configuration explicitly defined in a ILoadLinkProtocolConfig, the configuration defined in a ILoadLinkProtocolConfig wins.

var loadLinkProtocolBuilder = new LoadLinkProtocolBuilder();
_loadLinkProtocol = loadLinkProtocolBuilder.Build(
    ()=>new FakeReferenceLoader(),
    new[] { Assembly.GetExecutingAssembly() },
    LoadLinkExpressionConvention.Default
);

To create your own conventions, please see how the existing conventions are built. Then, simply pass your convention by argument when calling loadLinkProtocolBuilder.Build.

Read more

See also

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net45 is compatible.  net451 was computed.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.5

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.

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.0.0 10,881 5/9/2019
2.0.0-rc07 570 11/8/2018
2.0.0-rc06 587 10/25/2018
2.0.0-rc03 738 6/15/2018
2.0.0-rc01 851 3/14/2018
2.0.0-beta4 806 3/1/2018
1.1.0 1,147 1/9/2018
1.0.2 1,879 11/2/2016