Skip to content

SAIF.NetCore.ServiceContracts business rules

This page captures business rules enforced by this library. SAIF.NetCore.ServiceContracts is a contracts-only assembly, so all twelve rules below are enforced by executable code in this library itself (constructors, validating setters, and fluent option methods) — there is no database or SQL involvement for any rule.

BR-001 Raw OIDC credential string must not be null

Field Value
Statement The raw credential string passed to construct OidcAppCredentials must not be null.
Trigger A consumer calls new OidcAppCredentials(raw) with raw == null.
Enforcement OidcAppCredentials..ctor(string) throws ArgumentNullException(nameof(raw)) before attempting to split the string.
Outcome Construction fails immediately; no OidcAppCredentials instance is produced.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/OidcAppCredentials.cs

BR-002 Raw OIDC credentials must be two pipe delimited segments

Field Value
Statement The raw credential string must split into exactly two \|-delimited segments — clientId\|clientSecret — with the first segment assigned to ClientId and the second to ClientSecret.
Trigger OidcAppCredentials construction with a non-null raw value.
Enforcement OidcAppCredentials..ctor(string) splits on '\|' and throws ArgumentException("Raw credential parameter does not contain valid credential data.") when the split does not yield exactly two parts; empty segments (e.g. "\|") are accepted.
Outcome A correctly-shaped string populates both readonly fields; any other shape (0, 1, or 3+ segments) throws and no instance is produced.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/OidcAppCredentials.cs

BR-003 Shutdown exempt exception type must not be null

Field Value
Statement A ShutdownExemptExceptionType entry must reference a non-null Type.
Trigger Calling new ShutdownExemptExceptionType(null) (the constructor's required Type t parameter) or any other assignment of null to Type.
Enforcement The private Type setter in ShutdownExemptExceptionType throws InvalidOperationException("Type cannot be null.").
Outcome Construction fails; the exempt-type entry is never added to IWindowsServiceErrorHandler.ShutdownExemptExceptionTypes.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ShutdownExemptExceptionType.cs

BR-004 Shutdown exempt type must be a strict Exception subclass

Field Value
Statement A ShutdownExemptExceptionType entry's Type must be a strict subclass of System.Exception; Exception itself is rejected because IsSubclassOf is strict.
Trigger Construction with a non-null Type value.
Enforcement The private Type setter checks !value.IsSubclassOf(typeof(Exception)) and throws InvalidOperationException("Type must be subclass of Exception.") when the check fails.
Outcome Only strict subclasses of System.Exception can be registered as shutdown-exempt; typeof(Exception) is always rejected.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ShutdownExemptExceptionType.cs

BR-005 Shutdown exempt type defaults to always allowed

Field Value
Statement When no isAllowed predicate is supplied to ShutdownExemptExceptionType, every exception of the exempt type is treated as allowed (handled without triggering shutdown).
Trigger Construction with isAllowed == null.
Enforcement ShutdownExemptExceptionType..ctor assigns IsAllowed = isAllowed ?? (ex => true).
Outcome The exempt-type entry matches unconditionally unless the caller supplies a narrower predicate.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ShutdownExemptExceptionType.cs

BR-006 HTTP client option defaults

Field Value
Statement HttpClientOptions defaults to clearing headers after each request (PersistHeadersAfterRequest = false), a 1000 ms retry delay, retries limited to HTTP 401/502/503, and the Default serializer.
Trigger new HttpClientOptions().
Enforcement Property initializers on HttpClientOptions set the four defaults listed in the statement.
Outcome Any IHttpJsonClient configured with unmodified HttpClientOptions behaves per these defaults; interpretation of the values (the retry loop, header clearing) is implemented by SAIF.NetCore.HttpClients*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs

BR-007 PersistHeaders with no argument enables persistence

Field Value
Statement Calling PersistHeaders() with no argument turns header persistence on.
Trigger options.PersistHeaders().
Enforcement HttpClientOptions.PersistHeaders(bool option = true) sets PersistHeadersAfterRequest = option and returns this for fluent chaining.
Outcome PersistHeadersAfterRequest becomes true unless the caller explicitly passes false.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs

BR-008 UseNewtonsoftJson selects the Newtonsoft serializer

Field Value
Statement Calling UseNewtonsoftJson() selects HttpClientSerializer.NewtonsoftJson for the options instance.
Trigger options.UseNewtonsoftJson().
Enforcement HttpClientOptions.UseNewtonsoftJson() sets Serializer = HttpClientSerializer.NewtonsoftJson and returns this.
Outcome Subsequent serialization by the implementing IHttpJsonClient uses Newtonsoft.Json instead of the default serializer*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs

BR-009 HttpStatusCodeException defaults to text plain content type

Field Value
Statement The default HTTP response content type carried by a newly constructed HttpStatusCodeException is text/plain; StatusCode defaults to 0 until the consumer sets it.
Trigger new HttpStatusCodeException(message[, inner]).
Enforcement HttpStatusCodeException.ContentType initializes to "text/plain"; StatusCode is an uninitialized int field.
Outcome Error handlers that render the response body as the declared ContentType fall back to plain text unless the thrower overrides it*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpStatusCodeException.cs

BR-010 Error handling event args default to unhandled with special actions enabled

Field Value
Statement A newly raised BeginErrorHandlingEventArgs defaults to Handled = false and FireSpecialActions = true; a subscriber may set either flag to change the outcome.
Trigger new BeginErrorHandlingEventArgs(ex, container), typically raised by an IWindowsServiceErrorHandler implementation.
Enforcement BeginErrorHandlingEventArgs property initializers set the two defaults; no null checks are performed on the constructor arguments.
Outcome Error handling proceeds and special error actions fire by default; the effect of the flags is enforced by the error-handler implementation, not by this library*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/BeginErrorHandlingEventArgs.cs

BR-011 Special error action result defaults to not executed

Field Value
Statement A SpecialErrorActionResult constructed without an explicit status defaults Status to SpecialErrorActionStatus.NotExectued.
Trigger new SpecialErrorActionResult(title) (status parameter omitted).
Enforcement SpecialErrorActionResult..ctor(actionTitle, status = default, ex = null) uses the enum's default value for status, which is NotExectued (first declared member).
Outcome Callers that do not explicitly record an outcome report the action as not executed rather than succeeded or failed.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SpecialErrorActionResult.cs

BR-012 Generic HTTP async overloads are prohibited

Field Value
Statement The generic DeleteAsync<T>, GetAsync<T>, PatchAsync<T>, PostAsync<T>, and PutAsync<T> members of IHttpJsonClient are prohibited because they are documented as not thread safe; callers must use the non-generic Task<IHttpRequestResult> overloads instead.
Trigger A consumer calls one of the five generic async overloads.
Enforcement Each generic overload carries [Obsolete("This method not thread safe and has been depracated. Please use the overload GetAsync(string urlPart, object body).", true)] (wording specific to each verb) on IHttpJsonClient.
Outcome Compilation fails for any caller of the five generic overloads; only the non-generic overloads are usable.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpJsonClient.cs

Contract-declared rules (enforced by implementers)

The following semantics are declared by this library's XML documentation comments but are enforced by the implementing libraries, not by code here. They are not local business rules of SAIF.NetCore.ServiceContracts and are listed for traceability only — each implementing library's own published business-rules.md is the authoritative source for verifying these.

CR-001 Result and content deserialization semantics

Field Value
Statement GetResult<T> deserializes only when the request succeeded (otherwise returns default(T)); GetContent<T> deserializes regardless of status.
Contract member IHttpRequestResult.GetResult<T>, .GetContent<T>
Trigger A consumer calls GetResult<T>() or GetContent<T>() on an IHttpRequestResult instance returned by an IHttpJsonClient call.
Enforcement Declared only by XML documentation comments on IHttpRequestResult in this library; the conditional-deserialization logic is implemented by SAIF.NetCore.HttpClients' HttpRequestResult class, not by code in this package.
Outcome Callers that use GetResult<T> receive default(T) for an unsuccessful request instead of an attempted deserialization, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpRequestResult.cs

CR-002 Status code and success semantics

Field Value
Statement StatusCode is 0 when the request never reached the endpoint; IsSuccessful means a 2xx status.
Contract member IHttpRequestResult.StatusCode, .IsSuccessful
Trigger A consumer reads IHttpRequestResult.StatusCode or .IsSuccessful after an IHttpJsonClient call completes.
Enforcement Declared only by XML documentation comments on IHttpRequestResult in this library; populating these properties correctly is implemented by SAIF.NetCore.HttpClients, not by code in this package.
Outcome Callers can distinguish a transport failure (StatusCode == 0) from an HTTP-level failure and use IsSuccessful as a 2xx shorthand, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpRequestResult.cs

CR-003 System hash expiry validation

Field Value
Statement RemoveSystemHashSaltAndValidateLifetime throws when the embedded create time is older than expireSeconds.
Contract member ISystemHashService.RemoveSystemHashSaltAndValidateLifetime
Trigger A consumer calls ISystemHashService.RemoveSystemHashSaltAndValidateLifetime(systemHashValue, expireSeconds, nowBufferSeconds).
Enforcement Declared only by XML documentation comments on ISystemHashService in this library; the salt-removal, lifetime comparison, and throw behavior are implemented by SAIF.NetCore.SystemHash, not by code in this package.
Outcome Callers receive the de-salted hash value when it is still within its lifetime, or an exception when it has expired, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ISystemHashService.cs

CR-004 Cached system hash computation

Field Value
Statement Get(type) computes the hash once (SHA256 per XML doc) and returns the cached value afterwards.
Contract member ISystemHashService.Get
Trigger A consumer calls ISystemHashService.Get(SystemType type).
Enforcement Declared only by XML documentation comments on ISystemHashService.Get in this library; the SHA256 computation and single-compute-then-cache behavior are implemented by SAIF.NetCore.SystemHash, not by code in this package.
Outcome Repeated calls to Get with the same type return the same cached hash value instead of recomputing it, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ISystemHashService.cs

CR-005 LogDetails disclosure restriction

Field Value
Statement SAIFException.LogDetails are logged but must never be sent back to the client.
Contract member SAIFException.LogDetails
Trigger An implementation library's error handler catches a SAIFException (or subclass) carrying a populated LogDetails array.
Enforcement Declared only by the XML documentation comment on SAIFException.LogDetails in this library; withholding this content from the client response is implemented by SAIF.NetCore.ErrorHandler, not by code in this package.
Outcome LogDetails content reaches the log sink but is withheld from the HTTP response body, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIFException.cs

CR-006 HTTP error response controls

Field Value
Statement HttpStatusCodeException.StatusCode/ContentType control the error response the error handler sends.
Contract member HttpStatusCodeException
Trigger Implementation-library code throws an HttpStatusCodeException with a non-default StatusCode and/or ContentType.
Enforcement Declared only by the XML documentation comments on HttpStatusCodeException in this library; reading these properties and shaping the HTTP response accordingly is implemented by SAIF.NetCore.ErrorHandler, not by code in this package.
Outcome The client receives the thrower-specified status code and content type instead of a generic error response, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpStatusCodeException.cs

CR-007 Logging threshold configuration

Field Value
Statement The logging threshold defaults to the Environment API setting tracingdefaults.logthreshold, overridable per-app with <AppName>.logthreshold.
Contract member ISaifLogService
Trigger A logging call is made through an ISaifLogService implementation.
Enforcement Declared only by XML documentation comments and naming convention associated with ISaifLogService in this library; reading these Environment API settings and applying the threshold is implemented by SAIF.NetCore.Logging, not by code in this package.
Outcome Log entries below the configured threshold are suppressed, with the per-app override taking precedence over the default, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ISaifLogService.cs

CR-008 OIDC setting group

Field Value
Statement IOidcSettings values come from the NetCore_OIDC Environment API setting group.
Contract member IOidcSettings
Trigger A consumer reads a property on an IOidcSettings implementation.
Enforcement Declared only by XML documentation comments and naming convention associated with IOidcSettings in this library; retrieving the values from the NetCore_OIDC setting group is implemented by SAIF.NetCore.OIDC's OidcSettings, not by code in this package.
Outcome OIDC configuration values are sourced from the NetCore_OIDC Environment API setting group rather than another source, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IOidcSettings.cs

CR-009 Database connection caller requirements

Field Value
Statement IDBConnectionClient requires the calling application to run under an IIS service account to retrieve SQL/Oracle connection strings from DBConnectionsAPI.
Contract member IDBConnectionClient
Trigger A consumer calls an IDBConnectionClient member to retrieve a SQL Server or Oracle connection string.
Enforcement Declared only by XML documentation comments on IDBConnectionClient in this library; the IIS-service-account requirement and the call to DBConnectionsAPI are implemented by SAIF.NetCore.DBConnectionClient, not by code in this package.
Outcome Calls made outside an authorized IIS service-account context are rejected by DBConnectionsAPI rather than by this contract, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IDBConnectionClient.cs

CR-010 Anonymous IIS authentication scheme

Field Value
Statement IIdentityService.AuthenticationScheme is Windows for anonymous requests in IIS.
Contract member IIdentityService
Trigger A consumer reads IIdentityService.AuthenticationScheme for an anonymous (unauthenticated) IIS request.
Enforcement Declared only by XML documentation comments and naming convention associated with IIdentityService in this library; detecting the anonymous IIS case and returning Windows is implemented by SAIF.NetCore.AuthService, not by code in this package.
Outcome Anonymous IIS requests report Windows as their authentication scheme rather than null or an empty string, provided the implementing library honors this documented contract*.
Source src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IIdentityService.cs

Notes

  • Rules marked with * in their Outcome field were inferred from XML documentation comments or the implementing library's naming conventions rather than directly observed in this library's compiled code, because the interpretation happens in another package.
  • CR-001 through CR-010 document declared contract behavior only; their enforcement and runtime verification belong to the implementing library's own business-rules.md, named by the Expected enforcer in each rule's Enforcement field above.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/OidcAppCredentials.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ShutdownExemptExceptionType.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpStatusCodeException.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/BeginErrorHandlingEventArgs.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SpecialErrorActionResult.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpJsonClient.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpRequestResult.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ISystemHashService.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIFException.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ISaifLogService.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IOidcSettings.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IDBConnectionClient.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IIdentityService.cs