Fluxera.Repository.OpenTelemetry 6.0.4

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Fluxera.Repository.OpenTelemetry --version 6.0.4                
NuGet\Install-Package Fluxera.Repository.OpenTelemetry -Version 6.0.4                
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="Fluxera.Repository.OpenTelemetry" Version="6.0.4" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Fluxera.Repository.OpenTelemetry --version 6.0.4                
#r "nuget: Fluxera.Repository.OpenTelemetry, 6.0.4"                
#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 Fluxera.Repository.OpenTelemetry as a Cake Addin
#addin nuget:?package=Fluxera.Repository.OpenTelemetry&version=6.0.4

// Install Fluxera.Repository.OpenTelemetry as a Cake Tool
#tool nuget:?package=Fluxera.Repository.OpenTelemetry&version=6.0.4                

Fluxera.Repository

A generic repository implementation.

This repository implementation hides the actual storage implementation from the user. The only part where the abstraction leaks storage specifics is the configuration of a storage specific repository implementation.

The repository can be used with or without a specialized implementation, f.e. one can just use one of the provided interfaces IRepository or IReadOnlyRepository. The repository implementation is async from top to bottom.

public class PersonController : ControllerBase 
{
    private readonly IRepository<Person> repository;

    public PersonController(IRepository<Person> repository)
    {
        this.repository = repository
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> Get(string id)
    {
        Person result = await this.repository.GetAsync(id);
        return this.Ok(result);
    }
}

If you prefer to create a specialized interface and implementation you can do so, but you have to register the service yourself.

public interface IPersonRepository : IRepository<Person, Guid>
{
}

public class PersonRepository : Repository<Person, Guid>, IPersonRepository
{
    public PersonRepository(IRepository<Person, Guid> innerRepository)
        : base(innerRepository)
    {
    }
}

As you can see, you have to provide the IRepository<T, TKey> as dependency to the constructor or your specialized repository. The overall architecture of the repository uses the decorator pattern heavily to split up the many features around the storage implementation, to keep things simple and have every decorator layer only impolement a single responseibility. Your specialized repository then acts as the outermost layout of decorators.

Additional Features

Besides to being able top perform CRUD operation with the underlying data storage, the repository implementation provides several additional features.

Traits

Sometimes you don't want to expose the complete repository interface to you specialized implementations and sometimes even the IReadOnlyRepository may be too much. For those cases you can just use the trait interfaces yo want to support.

  • ICanAdd Provides methods to add items.
  • ICanAggregate Provides methods to aggregate results, like Count and Sum.
  • ICanFind Provides methods to find single and multiple results.
  • ICanGet Provides methods to get items by ID.
  • ICanRemove Provides methods to remove items.
  • ICanUpdate Provides methods to update items.

Using this set of interfaces you cann create a specialized repository interface how you see fit.

public interface IPersonRepository : ICanGet<Person, Guid>, ICanAggregate<Person, Guid>
{
}

Specifications

In the most basic form you can execute find queries using expressions that provide the predicate to satify by the result items. But this may lead to queries cluttered around your application, maybe duplicating code in several places. Updating queries may then become cumbersome in the future. To prevent this from happening you can create specialized specification classes that encapsulate queries, or parts of queries and can be re-used in your application. Any specification class can be combines with others using operations like And, Or, Not.

You can f.e. have a specification that finds all persons by name and another one that finds all persons by age.

public sealed class PersonByNameSpecification : Specification<Person>
{
    private readonly string name;

    public PersonByNameSpecification(string name)
    {
        this.name = name;
    }

    protected override Expression<Func<Person, bool>> BuildQuery()
    {
        return x => x.Name == this.name;
    }
}

public sealed class PersonByAgeSpecification : Specification<Person>
{
    private readonly int age;

    public PersonByAgeSpecification(int age)
    {
        this.age = age;
    }

    protected override Expression<Func<Person, bool>> BuildQuery()
    {
        return x => x.Age == this.age;
    }
}

You can combine those to find any person with a specific name and age.

PersonByNameSpecification byNameQuery = new PersonByNameSpecification("Tester");
PersonByAgeSpecification byAgeQuery = new PersonByAgeSpecification(35);
ISpecification<Person> query = byNameQuery.And(byAgeQuery);

Interception

Sometimes you may want to execute actions before or after you execute methods of the repository. You can do that using the IInterceptor interface. All you have to do is implement this interface and register the interceptor when configuring the repository. Your interceptor will then execute the methods before and after repository calls.

You can use this feature f.e. to set audit timesstamps (CreatedAt, UpdatedAt, ...) or to implement more complex szenarios like multi-tenecy based on a discriminator colum. You can modify the queries that should be sent to the underlying storage. If the interceptor feature is enabled (i.e at least one custom interceptor is registered) it makes sure that any query by ID is redirected to a predicate based method, so you are sure that even a get-by-id will benefit from you modifiing the predicate.

Query Options

To control how you query data is returned, you can use the QueryOptions to create sorting and paging optiosn that will be applied to the base-query on execution.

Repository Decorators Hierarchy

The layers of decorators a executed in the following order.

  • Diagnostics This decorator produces diagnostic events using System.Diagnostic that can be instrumented by telemetry systems.
  • Exception Logging This decorator just catches every exception that may occur then logs the exception and throws it again.
  • Guards This decorator checks the inputs for sane values and checks if the repository instance was disposed and throws corresponding exception.
  • Validation This decorator validates the inputs to Add and Update methods for validity using the configures validators.
  • Caching If the caching is enabled this decorator manages the handling of the cache. It tries to get the result of a query from the cache and if none was found executes the query and stores the result, in the cache.
  • Domain Events This decorator executes added domain events before an item was added, updated or removed. After that it executes the events using specialized commit event handlers again.
  • Data Storage This is the base layer around wich all decorators are configures. This is the storage specific repository implementation.

Supported Storages

  • In-Memory
  • LiteDB
  • MongoDB

Coming soon: EntityFramework Core and OData

OpenTelemetry

The repository produces Activity events using System.Diagnistic. Those events are used my the OpenTelemetry integration to support diagnostic insights. To enable the support for OpenTelemetry just add the package Fluxera.Repository.OpenTelemetry to your OpenTelemetry enabled application and add the instrumentation for the Repository shown below.

// Configure important OpenTelemetry settings, the console exporter, and automatic instrumentation.
builder.Services.AddOpenTelemetryTracing(builder =>
{
builder
    .AddConsoleExporter()
    .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("WebApplication1", "1.0.0"))
    .AddHttpClientInstrumentation()
    .AddAspNetCoreInstrumentation()
    .AddMongoDBInstrumentation()
    // Add the instrumentation for the Repository.
    .AddRepositoryInstrumentation();
});

Usage

You can configure different repositories for different aggregate root types, f.e. you can have persons in a MongoDB and invoices in a MS SQL database.

services.AddRepository(builder =>
{
    // Add default services and the repositories.
    builder.AddMongoRepository("MongoDB", options =>
    {
        // Confiure for what aggregate root types this repository is used.
        options.UseFor<Person>();

        // Configure the domain events (optional).
        options.AddDomainEventHandling(events =>
        {
            events.AddEventHandlers(typeof(Person).Assembly);
        });

        // Configure validation providers (optional).
        options.AddValidation(validation =>
        {
            validation.AddValidatorFactory(factory =>
            {
                factory.AddDataAnnotations(validation.RepositoryName);
            });
        });

        // Configure caching (optional).
        options.AddCaching();

        // Configure the interceoptors (optional).
        options.AddInterception(interception =>
        {
            interception.AddInterceptors(typeof(Person).Assembly);
        });

        // Set storage specific settings.
        options.AddSetting("Mongo.ConnectionString", "mongodb://localhost:27017");
        options.AddSetting("Mongo.Database", "test");
    });
});

References

The OpenTelemetry project.

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 netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen 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.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Fluxera.Repository.OpenTelemetry:

Package Downloads
Fluxera.Extensions.Hosting.Modules.Persistence

A module that enables persistence.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
9.0.1 180 11/16/2024
9.0.0 90 11/14/2024
8.7.3 197 11/1/2024
8.7.2 85 11/1/2024
8.7.1 251 6/17/2024
8.7.0 119 6/16/2024
8.6.5 269 6/15/2024
8.6.4 96 6/12/2024
8.6.3 99 6/12/2024
8.6.2 103 6/8/2024
8.6.1 286 6/2/2024
8.6.0 290 5/26/2024
8.5.0 405 4/28/2024
8.4.2 108 4/28/2024
8.4.1 115 4/28/2024
8.4.0 511 4/26/2024
8.3.0 322 4/25/2024
8.2.0 122 4/24/2024
8.1.0 293 4/22/2024
8.0.10 944 4/18/2024
8.0.9 601 4/13/2024
8.0.8 136 4/13/2024
8.0.7 350 3/20/2024
8.0.6 882 2/22/2024
8.0.5 238 2/22/2024
8.0.4 886 1/23/2024
8.0.3 1,829 1/4/2024
8.0.2 803 11/24/2023
8.0.1 466 11/23/2023
8.0.0 882 11/16/2023
7.2.6 856 7/23/2023
7.2.5 649 7/20/2023
7.2.4 835 6/21/2023
7.2.3 889 4/29/2023
7.2.2 871 4/25/2023
7.2.1 856 4/25/2023
7.2.0 1,370 4/13/2023
7.1.4 897 3/22/2023
7.1.3 692 3/16/2023
7.1.2 1,211 2/28/2023
7.1.1 1,263 1/22/2023
7.1.0 944 1/18/2023
7.0.11 1,446 12/28/2022
7.0.10 755 12/28/2022
7.0.9 801 12/22/2022
7.0.8 775 12/13/2022
7.0.7 907 12/13/2022
7.0.6 775 12/12/2022
7.0.5 904 12/12/2022
7.0.4 876 12/9/2022
7.0.3 801 11/21/2022
7.0.2 794 11/21/2022
7.0.1 808 11/19/2022
7.0.0 808 11/13/2022
6.2.1 1,486 10/12/2022
6.2.0 2,660 10/1/2022
6.1.19 1,446 9/20/2022
6.1.18 2,061 9/15/2022
6.1.17 1,501 7/30/2022
6.1.16 1,520 6/30/2022
6.1.15 1,507 6/15/2022
6.1.14 1,528 6/7/2022
6.1.13 915 6/7/2022
6.1.12 904 6/3/2022
6.1.8 904 6/3/2022
6.1.7 898 6/1/2022
6.1.6 933 5/31/2022
6.1.5 867 5/31/2022
6.1.4 1,515 5/30/2022
6.1.3 1,499 5/29/2022
6.1.2 928 5/29/2022
6.1.1 922 5/29/2022
6.1.0 1,516 5/28/2022
6.0.21 1,549 5/27/2022
6.0.19 2,559 5/18/2022
6.0.18 1,517 5/10/2022
6.0.16 1,475 5/5/2022
6.0.14 917 4/20/2022
6.0.13 904 4/20/2022
6.0.12 899 4/19/2022
6.0.11 896 4/19/2022
6.0.10 910 4/11/2022
6.0.9 918 4/7/2022
6.0.8 891 3/26/2022
6.0.6 891 3/25/2022
6.0.4 914 3/24/2022