Ternary.Code.Core
10.0.2
dotnet add package Ternary.Code.Core --version 10.0.2
NuGet\Install-Package Ternary.Code.Core -Version 10.0.2
<PackageReference Include="Ternary.Code.Core" Version="10.0.2" />
<PackageVersion Include="Ternary.Code.Core" Version="10.0.2" />
<PackageReference Include="Ternary.Code.Core" />
paket add Ternary.Code.Core --version 10.0.2
#r "nuget: Ternary.Code.Core, 10.0.2"
#:package Ternary.Code.Core@10.0.2
#addin nuget:?package=Ternary.Code.Core&version=10.0.2
#tool nuget:?package=Ternary.Code.Core&version=10.0.2
Ternary.Code
Turns a JSON spec into C# code, a project and a compiled assembly, and reads C# projects back into a spec. A fluent API builds specs in code. It is the code engine behind a low-code app builder: importers (CSV headers, spreadsheet columns, database tables) describe types as a spec, and Ternary.Code makes them real.
JSON spec ⇄ CodeAssembly ──► CSharpGenerator ──► .g.cs files + .csproj ──► CodeWriter (disk)
▲ │
│ └──► CodeCompiler ──► DLL + PDB (in memory)
└────────────── CSharpParser ◄── .csproj + .cs
Quick start
using Ternary.Code.Core;
// Describe the types. Names are repaired into identifiers: "first_name" becomes FirstName.
var spec = CodeAssembly.Create("Contacts").WithVersion(1, 0, 0);
spec.SourceNameAttribute = CodeTypeRef.Parse("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute");
spec.HasClass("Person")
.HasProperty(p => p.WithName("first_name").WithType<string>())
.HasProperty(p => p.WithName("age").WithType<int>().Nullable())
.HasMethod(m => m.WithName("Greeting").Returns<string>().RequiresImplementation());
// Check it. Every problem is a diagnostic with a path such as Classes[0].Properties[1].
var validation = spec.Validate();
// Generate the files: Person.g.cs, Contacts.csproj and a Person.cs scaffold for your code.
var code = CSharpGenerator.Generate(spec).GetValueOrThrow();
// Write them. Only files Ternary.Code generated are ever overwritten or deleted.
CodeWriter.WriteTo("out/Contacts", code, new CodeWriterOptions { ScaffoldPartials = true });
// Or compile in memory and use the types straight away.
var compiled = CodeCompiler.Compile(spec).GetValueOrThrow();
using var context = compiled.LoadCollectible();
var person = Activator.CreateInstance(context.GetType("Contacts.Person"));
// Save the spec, and read code back into one.
var json = spec.ToJson();
var parsed = CSharpParser.ParseProject("out/Contacts/Contacts.csproj").GetValueOrThrow();
Person.g.cs comes out as:
// <auto-generated>
// Generated by Ternary.Code 0.1.0 from spec 'Contacts'.
// Changes to this file are lost when it is regenerated. Put custom code in the partial class file.
// </auto-generated>
#nullable enable
using System.ComponentModel.DataAnnotations.Schema;
namespace Contacts;
[global::System.CodeDom.Compiler.GeneratedCode("Ternary.Code", "0.1.0")]
public partial class Person
{
[Column("first_name")]
public string FirstName { get; set; }
[Column("age")]
public int? Age { get; set; }
public partial string Greeting();
}
How it works
- The spec (
CodeAssembly) holds interfaces, classes and enums, with properties, constructors, partial method signatures, attributes and project references. Types are descriptions (CodeTypeRef), never loaded, so a spec can name types that don't exist yet.CodeSpecJsonreads and writes it (with a JSON Schema for editors), migrating older versions. - Your code lives in partial classes. Generated files end in
.g.csand carry a marker;CodeWritertouches nothing else, and scaffolds are created once and then left alone. Methods are partial signatures:partial void OnSaving()hooks, orRequiresImplementationfor ones you must write. - Domain conventions are configured, not built in.
SourceNameAttributerecords where a property came from ([Column("first_name")],[DbColumn(...)], ...). Ternary.Code knows nothing about any particular framework. - Compiling resolves framework references from the SDK's targeting packs, packages from the NuGet cache, and project or assembly references, then emits a DLL, a portable PDB and XML docs. Compiler errors map back to the spec element that caused them.
- Parsing reads a project (or generated code) back through the compiler's semantic model.
What the generator added comes back out, and anything a spec can't express is reported, never
dropped silently. Spec → code → spec gives the spec's normal form (
CodeSpecNormalizer), and code → spec → code gives the same files. - Nothing throws for bad input. Every entry point returns a
CodeResultwith diagnostics (TC0xxx-TC4xxx). Pass anILoggerin the options to log.
More
- docs/Diagnostics.md: every diagnostic code, and the logging levels.
- docs/Templates.md: generating into a
dotnet newtemplate's folder, next to developers' code. - PLAN.md: design decisions, roadmap and known limits.
Targets net9.0 and net10.0. Depends on Microsoft.CodeAnalysis.CSharp,
Microsoft.Extensions.Logging.Abstractions and Ternary.Data.Conversion. Early and actively
evolving; the spec format is versioned (SpecVersion) and migrated on load.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. 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 is compatible. 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. |
-
net10.0
- Microsoft.CodeAnalysis.CSharp (>= 5.9.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Ternary.Data.Conversion (>= 10.0.2)
-
net9.0
- Microsoft.CodeAnalysis.CSharp (>= 5.9.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Ternary.Data.Conversion (>= 10.0.2)
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 |
|---|---|---|
| 10.0.2 | 51 | 10/27/2026 |