Class SupportCase
A support conversation belonging to a team.
public record SupportCase : IEquatable<SupportCase>
- Inheritance
-
SupportCase
- Implements
- Inherited Members
Remarks
The team owns the case; the author is a link that may dangle. A case is raised by a person but is
about the team's account, and the team is what still exists a year later. Deleting a user runs
ITeamService.RemoveUserFromAllTeamsAsync, which strips that person's membership from every team —
so a case keyed on a member would be orphaned by design. Authorization keys on TeamKey
alone, and no check requires the author to still resolve.
The transcript is not carried here. A case can accumulate years of messages, so history is read separately and paged; see GetMessagesAsync(string, string, string, int, CancellationToken). This record is the header.
Properties
AssistantState
Whether an assistant is answering this case.
public SupportAssistantState AssistantState { get; init; }
Property Value
Remarks
Defaults to None, which is what every case raised before assistants existed deserializes as — so nothing already stored changes meaning.
AuthorIdentity
The stable authentication subject of whoever raised the case. May no longer resolve to a user or a member, which is expected rather than exceptional.
public required string AuthorIdentity { get; init; }
Property Value
AuthorName
The author's display name as it was when the case was raised.
public required string AuthorName { get; init; }
Property Value
Bindings
External projections. Empty until the channel work lands.
public SupportChannelBinding[] Bindings { get; init; }
Property Value
ClosedAt
public DateTime? ClosedAt { get; init; }
Property Value
ClosedBy
The stable subject of whoever closed the case, or null while it is open.
public string ClosedBy { get; init; }
Property Value
Remarks
May be AutoClose, when the case closed itself rather than being closed by a person.
ClosedReason
Why the case is closed, or null while it is open.
public SupportCaseClosureReason? ClosedReason { get; }
Property Value
Remarks
Derived from ClosedBy rather than stored, and deliberately so. The store already records who closed a case; adding a reason to CloseCaseAsync(string, string, DateTime, string, SupportMessage, CancellationToken) would change a signature that hosts implement for their own storage, which is a compile-time break in somebody else's repository for a value that can be read from what is already there.
It follows that the reason is exactly as trustworthy as the actor, which is the right coupling: a case closed by the sweeper is closed for inactivity by definition, not by a flag that could disagree with who closed it.
CreatedAt
public required DateTime CreatedAt { get; init; }
Property Value
Id
public required string Id { get; init; }
Property Value
MessageCount
Number of entries in the transcript, so a list can be rendered without reading it.
public required int MessageCount { get; init; }
Property Value
Status
public required SupportCaseStatus Status { get; init; }
Property Value
Subject
public required string Subject { get; init; }
Property Value
TeamKey
The owning team, or null when the case is not assigned to one.
public string TeamKey { get; init; }
Property Value
Remarks
Unassigned is a durable state, not a staging area. A case that arrived by mail from somebody
who belongs to several teams — or to none the toolkit can see — belongs to no team until a support
agent says which, and it can be answered, closed and reopened meanwhile. The person who knows which
tenant a problem concerns is a human reading it, not an algorithm reading a From: header.
The invariant it replaces still holds where there is a team. A case with one is loaded through it and authorized by membership exactly as before, so holding a case id from another tenant still gains nothing. What changed is that "no team" became expressible, not that team scoping became optional.
An unassigned case is governed system-wide, because a team scope has no team to be held against. See AssignCaseAsync(string, string, CancellationToken).