Kontent.Ai.ModelGenerator 11.0.0-rc.1
Pre-releaseTargets .NET 10. Both packages move from net8.0 to net10.0, which is why this is a major release rather than a continuation of the 10.3.0 line. Generated output is unchanged.
Breaking changes
-
net8.0→net10.0.Kontent.Ai.ModelGenerator.Coreis a library, so a project on .NET 8 cannot reference this release at all — restore fails withNU1202. TheKontent.Ai.ModelGeneratorCLI likewise needs the .NET 10 runtime to run. Move to .NET 10 first. -
Two generator base-class properties became methods.
ClassCodeGenerator.Propertiesis nowGetProperties(), and the Delivery generator'sPropertyCodenameConstantsis nowGetPropertyCodenameConstants(). Both re-sort their input and build a fresh set of Roslyn syntax nodes on every access, so a property was misleading about the cost — two reads returned two different arrays.GetProperties()remainsvirtual, so overriding it still works; a derived generator changesoverride … Propertiestooverride … GetProperties(). Only affects code that subclasses these base classes. -
--withtypeprovider/-tandCodeGeneratorOptions.WithTypeProviderare removed, along with theTypeProviderCodeGeneratorthat backed them. The Delivery SDK generates its ownGeneratedTypeProviderat compile time fromKontent.Ai.Delivery.SourceGenerationand discovers it at runtime, so nothing needs a hand-written provider any more.The flag had in fact stopped doing anything before this release: the code path behind it lived on a method that hid its base rather than overriding it, and the CLI invokes the base, so passing
-tgenerated no provider and printed no warning. Passing it now fails withUnsupported parameter: -trather than being silently ignored. Remove it from your scripts and referenceKontent.Ai.Delivery.SourceGenerationfrom the project your models are generated into. -
CodeGeneratorBase.FilenameSuffixandGetFileClassNameare removed. The suffix has been the empty string since single-file generation landed, which madeGetFileClassName(name)an identity function. Generated file names are unchanged. Only affects code that subclassesCodeGeneratorBase. -
IOutputProvider.Outputreturnsboolinstead ofvoid—truewhen it wrote the file,falsewhen the file already existed andoverwriteExistingwas not set. The generator reports each file's outcome and had no way to tell the two apart. Only affects code that implementsIOutputProvider; a custom implementation adds areturn true;. -
The dropped custom-partial emission path is gone.
PartialClassCodeGenerator, thecustomPartialflag onIClassCodeGeneratorFactory.CreateClassCodeGenerator, andClassCodeGenerator.OverwriteExistingall existed to support emitting a second, user-extensible partial file. The CLI never asked for it — the flag was never passed astrue— so the generator was unreachable, andOverwriteExistingwas aGetType() != typeof(PartialClassCodeGenerator)check that could only ever answertrue. The factory method also took anIUserMessageLoggerit null-checked and never used; that parameter is gone too. -
IDeliveryElementServiceandDeliveryElementServiceare removed.GetElementType(string)returned its argument unchanged, and the injected options were never read — an interface, an implementation, a DI registration and an inheritance layer computing the identity function.DeliveryCodeGeneratornow readselement.Value.Typedirectly and derives fromCodeGeneratorBase;DeliveryCodeGeneratorBase, whose only purpose was carrying the service, is gone with it. -
The always-true emission seams are gone.
ClassCodeGenerator.IsRecordandUseFileScopedNamespacewerevirtualand defaulted tofalse, but every concrete generator overrode both totrue, so the class-emitting and block-namespace branches were unreachable.DeliveryClassCodeGeneratorBasehad one subclass left after the custom-partial removal and is folded intoDeliveryClassCodeGenerator, which is nowsealed. -
Dead public members are removed from
PropertyandTextHelpers. OnProperty:ObjectType,IsNullable,HasInitializer, the already-obsoleteRequiresDefaultInitializer, and theIsDateTimeElementType/IsRichTextElementType/IsModularContentElementTypepredicates — none reachable from any emission path. OnTextHelpers:GetEnumerableType, andGetUpperSnakeCasedIdentifierName, which despite its name producedPascal_Snake_Caserather than upper snake case and was called by nothing.Generated output is unaffected. Verified by generating against a live environment before and after: all 15 files, including the
--baserecordextender, are byte-identical. -
ClassDefinition.AddPropertyCodenameConstantis removed;AddPropertynow registers both the property and its codename constant. The two were always called as a pair, and calling them separately is what allowed a rejected property to leave its constant behind.CodeGeneratorBase.AddProperty(Property, ref ClassDefinition), which wrapped the pair and had no callers, is removed with it. Only affects code that drivesClassDefinitiondirectly.
Changed
-
Arguments are validated against the SDKs' own rules instead of a hand-written subset. The tool checked only that an environment id was present and non-blank, so
-i not-a-guidwas accepted and the run failed later against the API with a less obvious message. It now runs the validation the SDK options already declare — data annotations plusIValidatableObject— which is what the SDKs' own container-free constructors do. Every problem is reported at once rather than the first, so a run started with several bad arguments does not have to be repeated once per mistake.$ KontentModelGenerator -i not-a-guid The delivery configuration is not valid: - EnvironmentId: The environment ID must be a valid GUID. See http://bit.ly/k-params for more details on configuration.Configurations that were valid before remain valid. A configuration the tool used to accept and the API would then reject now fails at startup.
-
--baserecordno longer fetches the content model twice. Generating the base record re-read the whole content model rather than reusing what had just been fetched, so a run with-bmade every request twice — in management mode, both the content-type and snippet listings. The generated output was identical either way; only the number of API calls changes. -
Nothing about the code the generator emits, for any content model that generated valid code before. Model classes, enums and the mapping attributes are byte-identical to
10.3.0-beta-2, verified by the generator's own output assertions. Models that previously came out uncompilable are covered under Fixed.
Fixed
-
Element codenames that differ but produce the same C# identifier no longer emit uncompilable models. Duplicate detection compared raw codenames while emission used the PascalCased identifier, so
my_elementandmy__element— two codenames, one identifier — both got through. The generated record then declaredMyElementCodenametwice and did not compile. The same hole existed between the two kinds of member: an element namedtitleand one namedtitle_codenameproduced a constant and a property that were both calledTitleCodename, and that case emitted no warning at all. Everything a record declares is now checked against one registry of identifiers, and the offending element is skipped with a message naming both codenames and the identifier they collide on.Warning: Skipping element 'my__element'. Content type 'article': 'my__element' and 'my_element' both produce the identifier 'MyElement'. Rename one of the elements in Kontent.ai.A rejected element no longer half-registers either — the constant used to be recorded before the property could be refused, so skipping one element still corrupted the output.
-
An element that fails for an unanticipated reason is now reported instead of vanishing. Per-element failures were classified by a
switchwith arms for the three expected exception types and no default, so anything else was caught, matched nothing, and left the element out of the generated model with nothing written to the console. -
The tool no longer claims to have created a base record it did not write.
--baserecorddeliberately does not overwrite an existing file, so hand-written additions survive a rerun — but the run printed "<name>class was successfully created" either way. It now says the file was kept, andIOutputProvider.Outputreturns whether it wrote (see Breaking changes). -
The "no content type available" message names the environment in management mode. It read the Delivery options only, so a
--managementapirun against an empty environment reported the id as blank. -
A failure with more than one inner exception no longer exits silently.
Mainhad a special case forAggregateExceptionthat printed the message only when there was exactly one inner exception and otherwise returned exit code 1 with no output at all.awaitunwraps these anyway, so the case was vestigial; it is removed and the general handler reports every failure. -
Two content types that map to the same file no longer silently overwrite each other. Type codenames sanitize to a class name the same way element codenames do, so
my_typeandmy__typeboth wroteMyType.cs— the second overwrote the first, and the run reported both as created. The duplicate is now skipped with a warning, and the "N content type models were successfully created" count reflects what was actually written.
Dependencies
Shipped floors moved up:
Kontent.Ai.Delivery,Kontent.Ai.Delivery.Abstractions,Kontent.Ai.UrlsandKontent.Ai.Delivery.SourceGeneration19.4.0 → 20.0.0-rc.1, andKontent.Ai.Management9.0.0-beta-5 → 9.0.0-rc.1. The CLI packages these assemblies inside the tool, so until now a .NET 10 tool carried .NET 8 builds of both SDKs.Microsoft.CodeAnalysis4.13.0 → 5.6.0, aligning the generator with the Roslyn version the rest of the repo builds against.Microsoft.Extensions.*(Options,Configuration.CommandLine,Configuration.Json,DependencyInjection) 9.0.15 → 10.0.10.
Internal
No consumer-visible effect:
-
The generator's output no longer reaches for
Consolefrom three different places.UserMessageLoggernow takes the writers it should use, defaulting to standard output and standard error, and the two places that bypassed it - command-line validation and the SDK-version banner - go through it. Command-line validation returns the problems it finds rather than printing them, since it runs before there is any container to resolve a logger from. Terminal output is unchanged, character for character.This also lets the tool's test assembly run in parallel again. It had been forced sequential because one test captured output by reassigning
Console.Out, which is process-wide, and collided with another test writing toConsole.Error. -
A transient
HttpClientwas registered in delivery mode and resolved by nothing.AddDeliveryClientbuilds its transport throughIHttpClientFactory, so the registration was inert - and had anything picked it up, it would have bypassed the SDK's whole handler chain: no authentication, no tracking headers, no resilience. -
Three JSON fixtures under the Core test project were copied to the build output and referenced by no test. The tests construct their inputs in memory instead. Recoverable from history if realistic payloads are wanted later.
-
Identifier-sanitizing regular expressions are compiled at build time and carry an execution timeout. Codenames arrive from the environment's content model, so they are external input, and a generator that hangs on one malformed codename is worse than one that fails.
-
Kontent.Ai.ModelGenerator.Optionsis nowKontent.Ai.ModelGenerator.CommandLine. The namespace holds command-line argument handling —ArgHelpers,ArgMappingsRegister,UsedSdkInfo,ValidationExtensions— and nothing to do withIOptions. Sitting directly underKontent.Ai.ModelGenerator, it shadowedMicrosoft.Extensions.Optionsthroughout the assembly and both test projects, soOptions.Create(...)bound to the namespace and had to be written fully qualified to compile. Only the CLI tool assembly is affected:Kontent.Ai.ModelGenerator.Corehas no such namespace, and the tool is installed rather than referenced.
Installation
dotnet add package Kontent.Ai.ModelGenerator --prereleaseFull changelog: src/model-generator/CHANGELOG.md