Jinaga.UnitTest 1.1.12

dotnet add package Jinaga.UnitTest --version 1.1.12
                    
NuGet\Install-Package Jinaga.UnitTest -Version 1.1.12
                    
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="Jinaga.UnitTest" Version="1.1.12" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Jinaga.UnitTest" Version="1.1.12" />
                    
Directory.Packages.props
<PackageReference Include="Jinaga.UnitTest" />
                    
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 Jinaga.UnitTest --version 1.1.12
                    
#r "nuget: Jinaga.UnitTest, 1.1.12"
                    
#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 Jinaga.UnitTest@1.1.12
                    
#: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=Jinaga.UnitTest&version=1.1.12
                    
Install as a Cake Addin
#tool nuget:?package=Jinaga.UnitTest&version=1.1.12
                    
Install as a Cake Tool

Jinaga.NET

Resilient, reliable data transfer for .NET.

What it Does

In Jinaga, you define a data model in terms of immutable facts. A fact represents an entity, a change to an entity, or a decision that a user or service has made.

In Jinaga.NET, facts are C# records.:

[FactType("Corporate.Company")]
record Company(string identifier) {}

[FactType("Corporate.Employee")]
record Employee(Company company, int employeeNumber) {}

When a user or service makes a decision, you will add a fact to the system. This will store it in the local database. It will also update the local UI. And it will publish it so that others can learn about the decision.

var contoso = await j.Fact(new Company("Contoso"));
var jane = await j.Fact(new Employee(contoso, 1));
var bob = await j.Fact(new Employee(contoso, 2));

To query facts, write a specification. Start at a know fact and find all related facts that match that specification.

var employeesOfCompany = Given<Company>.Match((company, facts) =>
    from employee in facts.OfType<Employee>()
    where employee.company == company
    select employee
);

var contosoEmployees = await j.Query(contoso, employeesOfCompany);

A query returns results at a point in time. If you want to keep a user interface up-to-date, you will need to set up an event-based watch.

var observer = j.Watch(contoso, employeesOfCompany, employee =>
{
    var component = AddEmployeeComponent(employee);
    return () =>
    {
        RemoveEmployeeComponent(component);
    };
});

Finally, if you want to be notified in real time of new information, just change Watch to Subscribe.

var subscription = j.Subscribe(contoso, employeesOfCompany, employee =>
{
    var component = AddEmployeeComponent(employee);
    return () =>
    {
        RemoveEmployeeComponent(component);
    };
});

The client will open a persistent connection with the server. The server will notify the client the moment a new employee is hired. Because the client already set up a watch, the new employee will appear on the UI.

What a Result Promises

Query, Watch, and Subscribe all answer from the graph of facts that this replica holds. Knowing what that answer promises, and what it does not, is what keeps a consumer from acting on information that has not settled.

  • A result is exact for this replica's own graph. That graph is always a consistent state. A fact is never stored before its predecessors, a store saves each batch of facts together, and a read sees one snapshot, so no result is computed over half of a change.
  • Replicas are peers. There is no global order and no authoritative answer. A result can differ from another replica's until the same facts have reached both, and then the two agree. A replicator is a peer with a graph of its own, so comparing a local watch against a query of the replicator compares two replicas. It does not check one of them against the truth.
  • A negation is provisional. A notExists condition is evaluated over the local graph. A row whose negative condition holds here may leave the result when a witness written elsewhere arrives. Watch and Subscribe then call that row's removal function, and Query, run again, omits the row.

Record the Premise

For an action that must not run on a provisional negation, record the premise in the model. The fact that the handler writes names the facts it acted on. A concurrent contradiction is then visible in history, and a specification can find it.

Suppose a facility manager assigns an office to each employee who has not been terminated.

[FactType("Corporate.Facility")]
record Facility(User manager, Company company, string name) {}

[FactType("Corporate.Termination")]
record Termination(Employee employee) {}

[FactType("Corporate.OfficeAssignment")]
record OfficeAssignment(Facility facility, Employee employee, string roomNumber) {}

The decision reads a negation, so it is provisional.

var employeesToAssign = Given<Company>.Match((company, facts) =>
    from employee in facts.OfType<Employee>()
    where employee.company == company
    where !facts.Any<Termination>(termination => termination.employee == employee)
    select employee
);

The assignment names the employee that the decision acted on. That reference is the premise.

var assignment = await j.Fact(new OfficeAssignment(facility, employee, "101"));

A Termination written on another replica does not erase that assignment, and it does not need to. Both facts are in history, so a specification finds the pairs that contradict one another.

var contradictedAssignments = Given<Facility>.Match((facility, facts) =>
    from assignment in facts.OfType<OfficeAssignment>()
    where assignment.facility == facility
    from termination in facts.OfType<Termination>()
    where termination.employee == assignment.employee
    select assignment
);

A watch on that specification tells the facility manager which assignments to revisit. Without the premise, the contradiction would show up only as a row that quietly left a result.

To narrow who can make a decision, name that principal with an authorization rule. Only the facility's manager can write an assignment for that facility. That does not make the decision unique: the same manager on two devices can still write competing assignments. It leaves one principal, who can see each conflict in history and resolve it.

private static AuthorizationRules Authorization(AuthorizationRules r) => r
    .Any<User>()
    .Type<OfficeAssignment>(assignment => assignment.facility.manager)
    ;

Running a Replicator

A Jinaga front end connects to a device called a Replicator. The Jinaga Replicator is a single machine in a network. It stores and shares facts. To get started, create a Replicator of your very own using Docker.

docker pull jinaga/jinaga-replicator
docker create --name my-replicator -p8080:8080 jinaga/jinaga-replicator
docker start my-replicator

This creates and starts a new container called my-replicator. The container is listening at port 8080 for commands. Configure Jinaga to use the replicator:

var j = JinagaClient.Create(options =>
{
    options.HttpEndpoint = new Uri("http://localhost:8080/jinaga");
});

Roadmap

Jinaga.NET is co-evolving with Jinaga.JS. Each of these projects has a front end and a back end. Either front end is intended to work with either back end. And the back ends are intended to interconnect to form a decision substrate.

Jinaga Roadmap

Storage

Jiaga.NET currently has support for SQLite and memory storage. The next storage solution to implement is PostgreSQL to support Docker deployment. After that, the MS SQL Server implementation will support enterprise solutions that need to keep the journal transactionally consistent with the projection.

Release

Each packable project declares its own version as a <VersionPrefix> in its .csproj. See Versioning. To release a new version of the Jinaga.NET library, create a new release in the format yyyymmdd.i. For example, 20240917.1. All of the packages are versioned separately. A package whose version has not changed is skipped on publish. The packages will be published to NuGet.

To accomplish this from the command line, use the following gh command.

gh release create 20240917.1 --generate-notes
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.  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 was computed.  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. 
.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.
  • .NETStandard 2.1

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
1.1.12 121 11/1/2026
1.1.11 165 9/26/2026
1.1.8 141 8/29/2026
1.1.6 126 8/29/2026
1.1.4 406 3/13/2025
1.1.2 272 12/26/2024
1.0.0 326 7/16/2024
0.12.8 311 7/14/2024
0.12.7 325 7/14/2024
0.12.6 314 7/13/2024
0.12.5 319 7/7/2024
0.12.4 327 7/6/2024
0.12.3 319 7/3/2024
0.12.2 344 6/29/2024
0.12.1 277 6/9/2024
0.12.0 323 5/26/2024
0.11.18 301 5/18/2024
0.11.17 336 4/19/2024
0.11.16 328 4/14/2024
0.11.15 346 4/9/2024
Loading failed