Table of Contents

TRLS022 — [OwnedEntity] property uses init-only setter

  • Severity: Warning
  • Category: Trellis

What it detects

Flags { get; init; } properties on a type decorated with [OwnedEntity]. The supported, tested shape is { get; private set; }. The analyzer walks the inheritance chain — init-only properties declared on a base class (or grandparent, etc.) of the [OwnedEntity] type are reported on the derived type as well, unless they are hidden by a new-overriding property on the derived type.

Why it matters

[OwnedEntity] types are materialized by EF Core through a generator-emitted private parameterless constructor: the source generator (Trellis.EntityFrameworkCore.Generator) emits the EF-only constructor, and EF Core then populates each property via the runtime accessor. Init-only setters work for the language-level "set once" construct but are not covered by Trellis's owned-entity round-trip tests. Behaviour with EF Core change tracking, snapshot-based update detection, and the CompositeValueObjectJsonConverter reflective path is not guaranteed.

Using { get; private set; } matches what every Trellis owned-entity test fixture exercises today and is the shape the source generator was designed against.

Bad examples

[OwnedEntity]
public sealed partial class ShippingAddress : ValueObject
{
    public string Street { get; init; } = null!;                                          // TRLS022
    public string City { get; init; } = null!;                                            // TRLS022
    public string State { get; init; } = null!;                                           // TRLS022
    public string PostalCode { get; init; } = null!;                                      // TRLS022
    public string Country { get; init; } = null!;                                         // TRLS022
}

Good examples

[OwnedEntity]
public sealed partial class ShippingAddress : ValueObject
{
    public string Street { get; private set; } = null!;
    public string City { get; private set; } = null!;
    public string State { get; private set; } = null!;
    public string PostalCode { get; private set; } = null!;
    public string Country { get; private set; } = null!;

    // The source generator emits the private parameterless constructor used by EF Core.
    private ShippingAddress(string street, string city, string state, string postalCode, string country)
    {
        Street = street;
        City = city;
        State = state;
        PostalCode = postalCode;
        Country = country;
    }
}

Code fix available

No. The diagnostic is a one-token edit per property (init → private set) and is best applied with a single search/replace inside the offending type.

Configuration

Standard Roslyn configuration applies.

dotnet_diagnostic.TRLS022.severity = none
Tip

Do not declare a parameterless constructor on an [OwnedEntity] partial — the source generator emits it. Doing so produces a duplicate-constructor error from the generator (see also TRLS037 if it fires in your build).