Skip to content

SAIF.Framework.Common business rules

This page captures business rules enforced by this library.

BR-001 A date range is valid only when BeginDate is less than or equal to EndDate

Field Value
Statement A date range is valid only when BeginDate is less than or equal to EndDate.
Trigger Any range validity test
Enforcement DateRangeExtensions.IsValidRange(IDateRange) — returns false for an inverted range.
Outcome Callers treat an invalid range as non-overlapping / non-current.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-002 A range is "open ended" when BeginDate is DateTime.MinValue or EndDate is DateTime.MaxValue

Field Value
Statement A range is "open ended" when BeginDate is DateTime.MinValue or EndDate is DateTime.MaxValue; these sentinels replace nulls throughout the library.
Trigger Open-ended / sentinel test
Enforcement DateRangeExtensions.IsOpenEnded(IDateRange), DateExtensions.IsMinOrMax(DateTime)
Outcome Returns true, marking the range as unbounded on that side.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs, Common/SAIF.Common/Extensions/DateExtensions.cs

BR-003 Two ranges with identical begin and end dates overlap only when they are not a single-day

Field Value
Statement Two ranges with identical begin and end dates overlap only when they are not a single-day (zero-length) range.
Trigger Pairwise overlap test
Enforcement DateRangeExtensions.IsOverlapping
Outcome A degenerate single-day range equal to another does not report as overlapping.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-004 Whether shared endpoints count as an overlap is caller-selectable

Field Value
Statement Whether shared endpoints count as an overlap is caller-selectable: inAllowSameDateOverlap = true uses Exclusive comparison, false (default) uses Inclusive.
Trigger Pairwise overlap test
Enforcement DateRangeExtensions.IsOverlapping
Outcome Endpoint-touching ranges report overlap by default.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-005 A set of fewer than two date ranges is always contiguous

Field Value
Statement A set of fewer than two date ranges is always contiguous.
Trigger Contiguity test
Enforcement DateRangeExtensions.IsContiguous
Outcome Returns true without comparison.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-006 Contiguity is evaluated on ranges sorted ascending by BeginDate, and fails on the first violating pair

Field Value
Statement Contiguity is evaluated on ranges sorted ascending by BeginDate, and fails on the first violating pair.
Trigger Contiguity test
Enforcement DateRangeExtensions.IsContiguous
Outcome Returns false immediately.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-007 The default contiguity mode is StartIsNextDay

Field Value
Statement The default contiguity mode is StartIsNextDay: each range must begin exactly one day after the previous range ends. StartMatchesEnd requires exact endpoint equality; AllowGaps only forbids going backwards.
Trigger Contiguity test
Enforcement DateRangeExtensions.IsContiguous(IEnumerable<IDateRange>, ContiguousOptions)
Outcome Mode-specific pass/fail.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-008 A set of fewer than two date ranges never self-overlaps

Field Value
Statement A set of fewer than two date ranges never self-overlaps.
Trigger Collection overlap test
Enforcement DateRangeExtensions.IsOverLapping
Outcome Returns false without comparison.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-009 A range whose begin and end are both sentinel values is treated as a null date and ignored during collection overlap evaluation

Field Value
Statement A range whose begin and end are both sentinel values is treated as a null date and ignored during collection overlap evaluation.
Trigger Collection overlap test
Enforcement DateRangeExtensions.IsOverLapping
Outcome The range is skipped.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-010 An open-ended predecessor followed by any range with a real begin date is always an overlap

Field Value
Statement An open-ended predecessor followed by any range with a real begin date is always an overlap.
Trigger Collection overlap test
Enforcement DateRangeExtensions.IsOverLapping
Outcome Returns true immediately.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-011 The default collection-overlap mode is DatesMayNotMatch

Field Value
Statement The default collection-overlap mode is DatesMayNotMatch: a range beginning on or before the previous range's end date is an overlap. DatesMayMatch requires a strict <.
Trigger Collection overlap test
Enforcement DateRangeExtensions.IsOverLapping(IEnumerable<IDateRange>, OverlappingOptions)
Outcome Mode-specific overlap verdict.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-012 The overlap of two nullable-end ranges is classified into exactly one of ten enumerated states

Field Value
Statement The overlap of two nullable-end ranges is classified into exactly one of ten enumerated states, defaulting to NoOverlap when no condition matches.
Trigger Categorical overlap classification
Enforcement NullableDateRange.GetOverlapState
Outcome A single DateRangeOverlapResult value.
Source Common/SAIF.Common/Classes/NullableDateRange.cs, Common/SAIF.Common/Enums/DateRangeOverlapResult.cs

BR-014 Title casing of a null or empty string is a no-op

Field Value
Statement Title casing of a null or empty string is a no-op.
Trigger Title-case request
Enforcement TitleCase.ToTitleCase
Outcome The input is returned unchanged.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-015 A word that already contains both uppercase and lowercase characters is assumed intentionally cased

Field Value
Statement A word that already contains both uppercase and lowercase characters is assumed intentionally cased and is never re-cased.
Trigger Per-word title casing
Enforcement TitleCase.TitleCaseSingleWord
Outcome The word is returned unchanged.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-016 Words in the approved acronym list

Field Value
Statement Words in the approved acronym list (SAIF, KPD, BW, AIC, KBI, JBL, PRL, USI, ISU, AAAP, OEC, LLC, LLP, DDS, MD, DC, PPC, JV, JD, DMD, PC, LMT, LLLP, LTD, ND, OD, OT, PT, CMT, PCP, GP, VP, CEO, CFO, CST, AG, RC, MJ, PMS, II, III, IV, AC, NE, NW, SE, SW, PO, USA, CA, WA, BPOE, BHG, KOA, OHSU, AOI, TNT, MCO, GSA, AAA, IHC, ODS, OGSERP, CC) are rendered fully uppercase.
Trigger Per-word title casing
Enforcement TitleCase.UpperCaseWordList + TitleCaseSingleWord
Outcome The word is uppercased.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-017 Minor words (is, in, the, an, of, on, and, de) are rendered fully lowercase

Field Value
Statement Minor words (is, in, the, an, of, on, and, de) are rendered fully lowercase.
Trigger Per-word title casing
Enforcement TitleCase.LowerCaseWordList + TitleCaseSingleWord
Outcome The word is lowercased.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-018 Known mixed-case words (PhD, LaCombe) keep their exact registered casing

Field Value
Statement Known mixed-case words (PhD, LaCombe) keep their exact registered casing; known mixed-case prefixes (Mc, O') cause the remainder of the word to be proper-cased (McMahon, O'Brien).
Trigger Per-word title casing
Enforcement TitleCase.MixedCaseWordList, TitleCase.MixedCasePrefixList + TitleCaseSingleWord
Outcome Canonical mixed casing is applied.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-019 A numeric word with an ordinal suffix (1st, 2nd, 3rd, 4th) keeps the suffix lowercase

Field Value
Statement A numeric word with an ordinal suffix (1st, 2nd, 3rd, 4th) keeps the suffix lowercase.
Trigger Per-word title casing
Enforcement TitleCase.HasNumberSuffix / GetNumberSuffixParts / NumberSuffixes
Outcome The ordinal is left lowercase.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-020 A trailing possessive 's is normalized to lowercase after casing

Field Value
Statement A trailing possessive 's is normalized to lowercase after casing.
Trigger Final title-case pass
Enforcement TitleCase.ApplyExactSuffixList / MixedCaseSuffixList
Outcome The suffix is re-cased.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-021 The first character of any title-cased result is always uppercase, regardless of every other rule

Field Value
Statement The first character of any title-cased result is always uppercase, regardless of every other rule.
Trigger Final title-case pass
Enforcement TitleCase.ToTitleCase (final statement)
Outcome First letter uppercased.
Source Common/SAIF.Common/Utility/TitleCase.cs

BR-022 A Julian date must contain at least 4 digits

Field Value
Statement A Julian date must contain at least 4 digits.
Trigger Julian validation
Enforcement Julian.IsValid(int, out string)
Outcome Returns false with message "The julian must contain at least 4 numbers"; ToDate escalates to ApplicationException.
Source Common/SAIF.Common/Classes/Julian.cs

BR-023 A Julian string that cannot be converted to Int32 is invalid

Field Value
Statement A Julian string that cannot be converted to Int32 is invalid.
Trigger Julian validation
Enforcement Julian.IsValid(string, out string)
Outcome Returns false (exception swallowed); ToDate throws ApplicationException with an empty message.
Source Common/SAIF.Common/Classes/Julian.cs

BR-024 When no millennium is supplied (or it is below 1000), the millennium is derived from the current system year's first digit x 1000, and a 1- or 2-digit Julian year is offset by it

Field Value
Statement When no millennium is supplied (or it is below 1000), the millennium is derived from the current system year's first digit × 1000, and a 1- or 2-digit Julian year is offset by it.
Trigger Julian year resolution
Enforcement Julian.GetYear
Outcome A 4-digit calendar year.
Source Common/SAIF.Common/Classes/Julian.cs

BR-025 Leap-year status is determined using the Julian calendar (System.Globalization.JulianCalendar), not the Gregorian calendar

Field Value
Statement Leap-year status is determined using the Julian calendar (System.Globalization.JulianCalendar), not the Gregorian calendar.
Trigger Julian year resolution
Enforcement Julian.GetYear
Outcome isLeapYear flag; drives the 365/366 day cap.
Source Common/SAIF.Common/Classes/Julian.cs

BR-026 The day-of-year portion of a Julian date must not exceed 365, or 366 in a leap year

Field Value
Statement The day-of-year portion of a Julian date must not exceed 365, or 366 in a leap year.
Trigger Julian decode
Enforcement Julian.ToDate
Outcome Throws ApplicationException("The julian day must be < {n}.").
Source Common/SAIF.Common/Classes/Julian.cs

BR-027 A Julian string is encoded as the year (2-digit by default, 4-digit on request) followed by the zero-padded 3-digit day-of-year

Field Value
Statement A Julian string is encoded as the year (2-digit by default, 4-digit on request) followed by the zero-padded 3-digit day-of-year.
Trigger Julian encode
Enforcement Julian.ToJulian
Outcome A Julian-format string.
Source Common/SAIF.Common/Classes/Julian.cs

BR-028 In EDI date parsing, a 6-character date is a 2-digit year and is always treated as 21st century

Field Value
Statement In EDI date parsing, a 6-character date is a 2-digit year and is always treated as 21st century (prefixed with "20"); no 20th-century handling exists.
Trigger EDI date parse
Enforcement DateMaker.MakeDateTime
Outcome The year resolves to 20xx.
Source Common/SAIF.Common/Classes/DateMaker.cs

BR-029 A short EDI time string is right-padded with zeros to hh24miss

Field Value
Statement A short EDI time string is right-padded with zeros to hh24miss; the default time when none is supplied is midnight ("000000").
Trigger EDI date parse
Enforcement DateMaker.MakeDateTime
Outcome Missing time components default to zero.
Source Common/SAIF.Common/Classes/DateMaker.cs

BR-030 EDI file date strings use the yyyyMMdd format and the - separator between a start and an end date

Field Value
Statement EDI file date strings use the yyyyMMdd format and the - separator between a start and an end date.
Trigger EDI date format / split
Enforcement DateMaker.DateFormat, DateMaker.DateSeperator, EdiFileDateString, ExtractDateRange
Outcome yyyymmdd or yyyymmdd-yyyymmdd.
Source Common/SAIF.Common/Classes/DateMaker.cs

BR-031 Parsing a malformed date-range string never throws — it silently yields the unbounded default range

Field Value
Statement Parsing a malformed date-range string never throws — it silently yields the unbounded default range.
Trigger DateRange.Parse
Enforcement DateRange.Parse (catch-all catch (Exception))
Outcome Returns new DateRange() = MinValue..MaxValue.
Source Common/SAIF.Common/Classes/DateRange.cs

BR-032 A DateRange constructed with no end date is unbounded forward

Field Value
Statement A DateRange constructed with no end date is unbounded forward: EndDate defaults to DateTime.MaxValue. A DateRange with no dates at all is MinValue..MaxValue.
Trigger DateRange construction
Enforcement DateRange() / DateRange(DateTime) / DateRange.Empty
Outcome Sentinel endpoints assigned.
Source Common/SAIF.Common/Classes/DateRange.cs

BR-033 An end date supplied as DateTime.MinValue is reinterpreted as no end date and coerced to DateTime.MaxValue

Field Value
Statement An end date supplied as DateTime.MinValue is reinterpreted as "no end date" and coerced to DateTime.MaxValue.
Trigger DateRange(DateTime, DateTime)
Enforcement DateRange(DateTime beginDate, DateTime endDate)
Outcome EndDate becomes MaxValue.
Source Common/SAIF.Common/Classes/DateRange.cs

BR-034 Two date ranges are equal if and only if both BeginDate and EndDate match

Field Value
Statement Two date ranges are equal if and only if both BeginDate and EndDate match; equality against a non-IDateRange object is always false, and IsEqual treats a null counterpart as unequal.
Trigger Range comparison
Enforcement DateRange.operator == / != / Equals, DateRangeExtensions.IsEqual
Outcome Boolean equality verdict.
Source Common/SAIF.Common/Classes/DateRange.cs, Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-035 As of filtering of a range collection selects ranges containing the supplied date under the chosen inclusivity mode

Field Value
Statement "As of" filtering of a range collection selects ranges containing the supplied date under the chosen inclusivity mode.
Trigger AsOf / IsInRange
Enforcement DateRangeExtensions.AsOf<T>, DateRangeExtensions.IsInRange
Outcome A filtered deferred sequence.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-036 The default date-in-range inclusivity is FirstOnly — begin date inclusive, end date exclusive

Field Value
Statement The default date-in-range inclusivity is FirstOnly — begin date inclusive, end date exclusive.
Trigger IsInRange / IsBetween
Enforcement DateRangeExtensions.IsInRange, IComparableExtensions.IsBetween
Outcome Begin-inclusive, end-exclusive membership.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs, Common/SAIF.Common/Extensions/IComparableExtensions.cs

BR-037 A null date is never in range

Field Value
Statement A null date is never in range.
Trigger IsInRange(DateTime?, IncludeComparisons)
Enforcement DateRangeExtensions.IsInRange(IDateRange, DateTime?, IncludeComparisons)
Outcome Returns false.
Source Common/SAIF.Common/Extensions/DateRangeExtensions.cs

BR-038 A guard call must itself supply a non-blank parameter name

Field Value
Statement A guard call must itself supply a non-blank parameter name.
Trigger Guard invocation
Enforcement Enforce.ArgumentNotNull
Outcome Throws ArgumentNullException("parameterName").
Source Common/SAIF.Common/Classes/Enforce.cs

BR-039 A guarded argument must not be null

Field Value
Statement A guarded argument must not be null.
Trigger Guard invocation
Enforcement Enforce.ArgumentNotNull (and every method that calls it)
Outcome Throws ArgumentNullException(parameterName).
Source Common/SAIF.Common/Classes/Enforce.cs

BR-040 A guarded string argument must not be empty

Field Value
Statement A guarded string argument must not be empty.
Trigger Guard invocation
Enforcement Enforce.ArgumentNotNullOrEmpty
Outcome Throws ArgumentException("Parameter ({name}) cannot be null or empty.").
Source Common/SAIF.Common/Classes/Enforce.cs

BR-041 A guarded string argument must not be all white space

Field Value
Statement A guarded string argument must not be all white space.
Trigger Guard invocation
Enforcement Enforce.ArgumentNotNullOrWhiteSpace
Outcome Throws ArgumentException("Parameter ({name}) cannot be null or white space.").
Source Common/SAIF.Common/Classes/Enforce.cs

BR-042 A guarded state value (as opposed to an argument) must not be null

Field Value
Statement A guarded state value (as opposed to an argument) must not be null; the violation is an InvalidOperationException, not an ArgumentNullException.
Trigger State guard
Enforcement Enforce.NotNull<T> → GenericExtensions.IfNullThrow<T,TException>
Outcome Throws InvalidOperationException("The type {FullName} cannot be null.").
Source Common/SAIF.Common/Classes/Enforce.cs, Common/SAIF.Common/Extensions/GenericExtensions.cs

BR-043 A caller-supplied boolean or Func condition must evaluate true, or the caller-specified exception type/instance is thrown

Field Value
Statement A caller-supplied boolean or Func<bool> condition must evaluate true, or the caller-specified exception type/instance is thrown.
Trigger Condition guard
Enforcement Enforce.True (4 overloads)
Outcome Throws T or the supplied Exception.
Source Common/SAIF.Common/Classes/Enforce.cs

BR-044 A SaifValidationException always exposes a non-null IValidationResult

Field Value
Statement A SaifValidationException always exposes a non-null IValidationResult.
Trigger Validation exception construction
Enforcement SaifValidationException constructors
Outcome ValidationResult is never null.
Source Common/SAIF.Common/Classes/SaifValidationException.cs

BR-045 A validation exception built from a bare message carries a single failure whose PropertyName is the empty string, and the two-argument overload binds the failure to a named property

Field Value
Statement A validation exception built from a bare message carries a single failure whose PropertyName is the empty string; the two-argument overload binds the failure to a named property.
Trigger Validation exception construction
Enforcement SaifValidationException.CreateValidationResult
Outcome A one-failure ValidationResult.
Source Common/SAIF.Common/Classes/SaifValidationException.cs

BR-046 A ValidationResult always exposes non-null Errors and Messages collections, even when constructed from a null failure sequence

Field Value
Statement A ValidationResult always exposes non-null Errors and Messages collections, even when constructed from a null failure sequence.
Trigger ValidationResult construction
Enforcement ValidationResult() / ValidationResult(IEnumerable<ValidationFailure>)
Outcome Empty lists rather than nulls.
Source Common/SAIF.Common/Classes/ValidationResult.cs

BR-047 A validation result is valid if and only if it holds no errors

Field Value
Statement A validation result is valid if and only if it holds no errors; informational Messages never affect validity.
Trigger IsValid read
Enforcement ValidationResult.IsValid (virtual)
Outcome !Errors.Any().
Source Common/SAIF.Common/Classes/ValidationResult.cs

BR-048 Each context failure condition maps to a fixed default target view name

Field Value
Statement Each context failure condition maps to a fixed default target view name: NoContext, InvalidContext, InvalidContextForUser, DisabledView; a caller may override the view, and DisabledViewException may additionally carry the model for redisplay.
Trigger Context exception construction
Enforcement NoContextException, InvalidContextException, InvalidContextForUserException, DisabledViewException constructors; ContextException.TargetView / Model
Outcome TargetView (and optionally Model) is populated for the presentation layer to route on.
Source Common/SAIF.Common/Classes/ContextException.cs

BR-049 Formatting a null exception yields an empty string rather than throwing

Field Value
Statement Formatting a null exception yields an empty string rather than throwing.
Trigger Exception formatting
Enforcement ExceptionExtensions.GetFullFormattedException, GetErrorLogFormattedException
Outcome Returns string.Empty.
Source Common/SAIF.Common/Extensions/ExceptionExtensions.cs

BR-050 Exception chains are reported innermost (root cause) first by default

Field Value
Statement Exception chains are reported innermost (root cause) first by default.
Trigger Exception formatting
Enforcement ExceptionExtensions.GetFullFormattedException(innerToOuter = true), GetErrorLogFormattedException
Outcome The exception list is reversed before rendering.
Source Common/SAIF.Common/Extensions/ExceptionExtensions.cs

BR-051 The formatted exception report renders a summary block (type, message, source) for every exception

Field Value
Statement The formatted exception report renders a summary block (type, message, source) for every exception in the chain, followed by a separate stack-trace block per exception; empty Source and empty StackTrace values are omitted.
Trigger Exception formatting
Enforcement ExceptionExtensions.GetFullFormattedException
Outcome A two-part formatted report.
Source Common/SAIF.Common/Extensions/ExceptionExtensions.cs

BR-052 An error-log entry begins with the innermost exception's message, followed by the sentinel @#ELOG#@, followed by the full report, so a log reader can split summary from detail

Field Value
Statement An error-log entry begins with the innermost exception's message, followed by the sentinel @#ELOG#@, followed by the full report — so a log reader can split summary from detail.
Trigger Error-log formatting
Enforcement ExceptionExtensions.GetErrorLogFormattedException, ExceptionExtensions.ErrorLogMessageString
Outcome Sentinel-delimited log string.
Source Common/SAIF.Common/Extensions/ExceptionExtensions.cs

BR-053 A string search pattern over a binary payload is encoded as ASCII, not Unicode

Field Value
Statement A string search pattern over a binary payload is encoded as ASCII, not Unicode.
Trigger Byte search
Enforcement ByteExtensions.Search(byte[], string, int, int)
Outcome Encoding.ASCII.GetBytes needle.
Source Common/SAIF.Common/Extensions/ByteExtensions.cs

BR-054 A byte search returns -1 when the haystack is empty, the needle is empty, the needle is longer than the haystack, or the start offset leaves too few bytes

Field Value
Statement A byte search returns -1 when the haystack is empty, the needle is empty, the needle is longer than the haystack, or the start offset leaves too few bytes.
Trigger Byte search
Enforcement ByteExtensions.Search(byte[], byte[], int, int)
Outcome Returns -1 (not found).
Source Common/SAIF.Common/Extensions/ByteExtensions.cs

BR-055 A byte search may be capped

Field Value
Statement A byte search may be capped: when maxBytesToSearch > 0 the scan stops once the index exceeds that cap. Header stripping caps the scan at 100 bytes by default.
Trigger Byte search / header strip
Enforcement ByteExtensions.Search, ByteExtensions.StripHeader
Outcome The scan terminates early; a signature beyond the cap is reported not-found.
Source Common/SAIF.Common/Extensions/ByteExtensions.cs

BR-056 A header is stripped only when the signature is found at a position greater than zero

Field Value
Statement A header is stripped only when the signature is found at a position greater than zero; a payload already starting at its signature (or with no signature) is returned unchanged with found = false.
Trigger Header strip
Enforcement ByteExtensions.StripHeader
Outcome Original or forward-skipped array, plus the found flag.
Source Common/SAIF.Common/Extensions/ByteExtensions.cs

BR-057 Disposable cryptographic and stream resources are created inside using blocks so they are released deterministically

Field Value
Statement Disposable cryptographic and stream resources are created inside using blocks so they are released deterministically.
Trigger Hashing / stream materialisation
Enforcement ByteExtensions.GetMD5Hash, FileExtensions.ToByteArray
Outcome Resources disposed on scope exit.
Source Common/SAIF.Common/Extensions/ByteExtensions.cs, Common/SAIF.Common/Extensions/FileExtensions.cs

BR-058 JSON serialization uses JavaScriptSerializer (System.Web.Extensions) and XML serialization uses XmlSerializer; serializable targets must be reference types (where T : class) except for SaveToJson, which is unconstrained

Field Value
Statement JSON serialization uses JavaScriptSerializer (System.Web.Extensions) and XML serialization uses XmlSerializer; serializable targets must be reference types (where T : class) except for SaveToJson<T>, which is unconstrained.
Trigger Serialization
Enforcement Serializer (all members), GenericExtensions.ToJsonString / ToXmlString, DotNet40Extensions
Outcome Serializer selection and type constraint.
Source Common/SAIF.Common/Classes/Serializer.cs, Common/SAIF.Common/Extensions/GenericExtensions.cs, Common/SAIF.Common/Extensions/DotNet40Extensions.cs

BR-059 XML deserialization that produces an incompatible type yields null rather than throwing, because the result is cast with as T

Field Value
Statement XML deserialization that produces an incompatible type yields null rather than throwing, because the result is cast with as T.
Trigger XML load
Enforcement Serializer.LoadFromXml<T>
Outcome Returns null.
Source Common/SAIF.Common/Classes/Serializer.cs

BR-060 Serialized output written to a file is explicitly flushed before the writer is disposed

Field Value
Statement Serialized output written to a file is explicitly flushed before the writer is disposed.
Trigger Save to file
Enforcement Serializer.SaveToJson<T>, Serializer.SaveToXml<T>
Outcome writer.Flush() is called.
Source Common/SAIF.Common/Classes/Serializer.cs

BR-061 A property-selector lambda must select a member, not invoke a method

Field Value
Statement A property-selector lambda must select a member, not invoke a method.
Trigger Property expression resolution
Enforcement ExpressionExtensions.GetPropertyInfo
Outcome Throws ArgumentException("Expression '{propertyLambda}' refers to a method, not a property.").
Source Common/SAIF.Common/Extensions/ExpressionExtensions.cs

BR-062 A property-selector lambda must select a property, not a field

Field Value
Statement A property-selector lambda must select a property, not a field.
Trigger Property expression resolution
Enforcement ExpressionExtensions.GetPropertyInfo
Outcome Throws ArgumentException("Expression '{propertyLambda}' refers to a field, not a property.").
Source Common/SAIF.Common/Extensions/ExpressionExtensions.cs

BR-063 The selected property must be declared on the target type or one of its base types

Field Value
Statement The selected property must be declared on the target type or one of its base types.
Trigger Property expression resolution
Enforcement ExpressionExtensions.GetPropertyInfo
Outcome Throws ArgumentException("Expresion '{propertyLambda}' refers to a property that is not from type {T}.").
Source Common/SAIF.Common/Extensions/ExpressionExtensions.cs

BR-064 A property may be set only when it has a setter, but that setter may be non-public - reflective assignment deliberately bypasses accessibility

Field Value
Statement A property may be set only when it has a setter, but that setter may be non-public — reflective assignment deliberately bypasses accessibility.
Trigger Reflective property assignment
Enforcement ExpressionExtensions.SetPropertyValue
Outcome Throws ArgumentException("Expresion '{propInfo.Name}' refers to a property that is not settable."), or assigns through the non-public setter.
Source Common/SAIF.Common/Extensions/ExpressionExtensions.cs

BR-065 A sequence generator starts at the caller-supplied start value, defaulting to 0, and may be repositioned at any time via SetValue

Field Value
Statement A sequence generator starts at the caller-supplied start value, defaulting to 0, and may be repositioned at any time via SetValue.
Trigger Sequence construction / reposition
Enforcement SequenceGenerator<T> ctor and SetValue, Sequence ctor, LongSequence ctor
Outcome CurrentValue assigned.
Source Common/SAIF.Common/Classes/SequenceGenerator.cs, Common/SAIF.Common/Classes/Sequence.cs, Common/SAIF.Common/Classes/LongSequence.cs

BR-066 NextValue is pre-increment

Field Value
Statement NextValue is pre-increment: it advances the counter by 1 and returns the new value, so the first value issued from a zero start is 1.
Trigger Sequence read
Enforcement Sequence.NextValue, LongSequence.NextValue
Outcome Incremented value returned.
Source Common/SAIF.Common/Classes/Sequence.cs, Common/SAIF.Common/Classes/LongSequence.cs

BR-067 Reset() returns the counter to literal zero, not to the original start value

Field Value
Statement Reset() returns the counter to literal zero, not to the original start value.
Trigger Sequence reset
Enforcement Sequence.Reset, LongSequence.Reset
Outcome CurrentValue = 0.
Source Common/SAIF.Common/Classes/Sequence.cs, Common/SAIF.Common/Classes/LongSequence.cs

BR-068 The application name is URL segment 1

Field Value
Statement The application name is URL segment 1; a null URI or a URI with fewer than 2 segments yields an empty string rather than an error.
Trigger URL parse
Enforcement UriExtensions.ApplicationName
Outcome Segment 1 without slashes, or string.Empty.
Source Common/SAIF.Common/Extensions/UriExtensions.cs

BR-069 The page name is URL segment 2, defaulting to "Index" when absent

Field Value
Statement The page name is URL segment 2, defaulting to "Index" when absent; unlike the other URI helpers, this member throws on a null URI.
Trigger URL parse
Enforcement UriExtensions.PageName
Outcome Segment 2 or "Index"; ArgumentNullException on null.
Source Common/SAIF.Common/Extensions/UriExtensions.cs

BR-070 The default controller/action route is Home/Index

Field Value
Statement The default controller/action route is Home/Index; with 3 segments the action defaults to Index; with 4 or more, segments 2 and 3 are the controller and action.
Trigger URL parse
Enforcement UriExtensions.ControllerAndActionName
Outcome A controller/action string.
Source Common/SAIF.Common/Extensions/UriExtensions.cs

BR-071 A Windows identity name is interpreted as DOMAIN\user

Field Value
Statement A Windows identity name is interpreted as DOMAIN\user: the account id is the last backslash-delimited segment and the domain is the first.
Trigger Identity parse
Enforcement SecurityPrincipalExtensions.UserId, SecurityPrincipalExtensions.Domain
Outcome Account or domain substring.
Source Common/SAIF.Common/Extensions/SecurityPrincipalExtensions.cs

BR-072 A string is truthy only when it uppercases to one of Y, YES, TRUE, 1, or ON

Field Value
Statement A string is truthy only when it uppercases to one of Y, YES, TRUE, 1, or ON; null and every other value are false.
Trigger String-to-boolean conversion
Enforcement StringExtensions.ToBoolean
Outcome true / false.
Source Common/SAIF.Common/Extensions/StringExtensions.cs

BR-073 Booleans render to flags as Y/N, YES/NO, or On/Off depending on the method chosen

Field Value
Statement Booleans render to flags as Y/N, YES/NO, or On/Off depending on the method chosen.
Trigger Boolean-to-string conversion
Enforcement BooleanExtensions.ToY_N_String, ToYesNoString, ToOnOffString
Outcome The corresponding flag string.
Source Common/SAIF.Common/Extensions/BooleanExtensions.cs

BR-074 Non-ASCII typographic characters are folded to ASCII equivalents before text leaves the system

Field Value
Statement Non-ASCII typographic characters are folded to ASCII equivalents before text leaves the system: curly single quotes → ', curly/angled double quotes → ", bullet glyphs → *, all dash/hyphen variants → -, and NUL → space. A null input yields an empty string.
Trigger Text normalization
Enforcement StringExtensions.ReplaceUnicodeCharactersWithAscii
Outcome ASCII-safe string.
Source Common/SAIF.Common/Extensions/StringExtensions.cs

BR-075 Character-class filters retain (or, for the Remove variants, exclude) exactly the characters matching the corresponding char.Is predicate, and treat a null input as an empty string

Field Value
Statement Character-class filters retain (or, for the Remove* variants, exclude) exactly the characters matching the corresponding char.Is* predicate, and treat a null input as an empty string.
Trigger Text filtering
Enforcement StringUtilities.Digits / Letters / LettersOrDigits / Punctuation / Symbols / ControlCharacters / RemoveControlCharacters / RemoveWhiteSpaces
Outcome A filtered string, never null.
Source Common/SAIF.Common/Utility/StringUtilities.cs

BR-076 Truncate is length-safe (clamps and tolerates null)

Field Value
Statement Truncate is length-safe (clamps and tolerates null); Left and Right are not and will throw if the requested length exceeds the string.
Trigger String slicing
Enforcement StringExtensions.Truncate, Left, Right
Outcome Clamped substring, or ArgumentOutOfRangeException.
Source Common/SAIF.Common/Extensions/StringExtensions.cs

BR-077 A string contains only numeric characters only when it is non-empty and contains no non-digit character at all

Field Value
Statement A string "contains only numeric characters" only when it is non-empty and contains no non-digit character at all (regex \D must not match).
Trigger Numeric-content test
Enforcement StringExtensions.ContainsOnlyNumericCharacters
Outcome true / false.
Source Common/SAIF.Common/Extensions/StringExtensions.cs

BR-078 A null or whitespace decimal string converts to zero rather than raising an error

Field Value
Statement A null or whitespace decimal string converts to zero rather than raising an error.
Trigger Decimal conversion
Enforcement StringExtensions.ConvertToDecimalOrZero
Outcome 0 for blank input.
Source Common/SAIF.Common/Extensions/StringExtensions.cs

BR-079 The IsNumeric test is 32-bit-integer-bounded

Field Value
Statement The IsNumeric test is 32-bit-integer-bounded: decimals and values beyond Int32.MaxValue report as non-numeric.
Trigger Numeric test
Enforcement NumericExtensions.IsNumeric
Outcome int.TryParse result.
Source Common/SAIF.Common/Extensions/NumericExtensions.cs

BR-080 A decimal's precision is its declared scale, read from the scale byte extracted via BitConverter and decimal.GetBits, with trailing zeros counted

Field Value
Statement A decimal's precision is its declared scale, read from the scale byte of BitConverter.GetBytes(decimal.GetBits(decimalValue)[3])[2]; trailing zeros count.
Trigger Precision read
Enforcement NumericExtensions.Precision
Outcome Decimal-place count.
Source Common/SAIF.Common/Extensions/NumericExtensions.cs

BR-081 Reducing a decimal's precision truncates

Field Value
Statement Reducing a decimal's precision truncates; it never rounds.
Trigger Precision reduction
Enforcement NumericExtensions.TruncatePrecision
Outcome Truncated decimal.
Source Common/SAIF.Common/Extensions/NumericExtensions.cs

BR-082 Merging dictionaries overwrites existing keys by default

Field Value
Statement Merging dictionaries overwrites existing keys by default; passing replaceExisting = false preserves the target's existing values.
Trigger Dictionary merge
Enforcement DictionaryExtensions.Merge<TValue>
Outcome Target dictionary mutated accordingly.
Source Common/SAIF.Common/Extensions/DictionaryExtensions.cs

BR-083 A dictionary read with a supplied default never throws on a missing key

Field Value
Statement A dictionary read with a supplied default never throws on a missing key.
Trigger Dictionary read
Enforcement DictionaryExtensions.TryGetValue<TKey,TValue>
Outcome Stored value or the default.
Source Common/SAIF.Common/Extensions/DictionaryExtensions.cs

BR-084 An upsert overwrites an existing key or adds a new one and always returns the supplied value

Field Value
Statement An upsert overwrites an existing key or adds a new one and always returns the supplied value.
Trigger Dictionary upsert
Enforcement DictionaryExtensions.UpdateOrAdd<TKey,TValue>
Outcome Dictionary updated; value returned.
Source Common/SAIF.Common/Extensions/DictionaryExtensions.cs

BR-085 Joining a sequence to a string requires a non-null separator

Field Value
Statement Joining a sequence to a string requires a non-null separator (and, for the projection overload, a non-null projection function); elements are rendered with ToString() unless a projection is supplied.
Trigger Sequence-to-string
Enforcement IEnumerableExtensions.ToStringAll (2 overloads)
Outcome Joined string, or ArgumentNullException.
Source Common/SAIF.Common/Extensions/IEnumerableExtensions.cs

BR-086 Taking N items from the end of a sequence preserves their original relative order

Field Value
Statement Taking N items from the end of a sequence preserves their original relative order.
Trigger Trailing take
Enforcement IEnumerableExtensions.TakeFromEnd<T>
Outcome The last N items in source order.
Source Common/SAIF.Common/Extensions/IEnumerableExtensions.cs

BR-087 Key-based distinct retains the first occurrence of each key

Field Value
Statement Key-based distinct retains the first occurrence of each key.
Trigger Keyed distinct
Enforcement IEnumerableExtensions.Distinct<TSource,TKey>
Outcome One element per distinct key.
Source Common/SAIF.Common/Extensions/IEnumerableExtensions.cs

BR-088 Outer joins compare keys with the caller's IEqualityComparer when supplied, otherwise EqualityComparer.Default, and substitute caller-supplied defaults for unmatched sides

Field Value
Statement Outer joins compare keys with the caller's IEqualityComparer<TKey> when supplied, otherwise EqualityComparer<TKey>.Default, and substitute caller-supplied defaults for unmatched sides.
Trigger Outer join
Enforcement IEnumerableExtensions.FullOuterGroupJoin, FullOuterJoin, LeftOuterJoin
Outcome Projected join results including unmatched rows.
Source Common/SAIF.Common/Extensions/IEnumerableExtensions.cs

BR-089 Converting a stream to a byte array copies from the stream's current position - the caller owns rewinding - and does not dispose the source stream

Field Value
Statement Converting a stream to a byte array copies from the stream's current position — the caller owns rewinding — and does not dispose the source stream.
Trigger Stream materialisation
Enforcement FileExtensions.ToByteArray
Outcome Byte array from the current position onward.
Source Common/SAIF.Common/Extensions/FileExtensions.cs

BR-090 Property reflection returns only the type's public properties

Field Value
Statement Property reflection returns only the type's public properties (default Type.GetProperties() binding flags).
Trigger Property enumeration
Enforcement Reflector.GetPropertyInfos<TTarget>
Outcome PropertyInfo[] of public properties.
Source Common/SAIF.Common/Classes/Reflector.cs

BR-091 A log file must be registered against a log id before it can be written to, and log writers are retrieved by that id

Field Value
Statement A log file must be registered against a log id before it can be written to, and log writers are retrieved by that id.
Trigger Log contract usage
Enforcement IConsoleLogService.RegisterLogFile / GetLog(string) / Logs / Flush / LogException; ILogWriter.SetTimeFormat / SetTimeDelimiter / SetWriteLinePrefix
Outcome Contract obligation on the implementing assembly*
Source Common/SAIF.Common/Interfaces/IConsoleLogService.cs, Common/SAIF.Common/Interfaces/ILogWriter.cs

BR-092 A console application may address at most five conventional log files (File0-File4) plus a Custom category

Field Value
Statement A console application may address at most five conventional log files (File0–File4) plus a Custom category.
Trigger Log contract usage
Enforcement SAIF.Common.Enums.LogType, IConsoleLogService.GetLog(LogType)
Outcome Bounded set of well-known log slots.
Source Common/SAIF.Common/Enums/LogType.cs, Common/SAIF.Common/Interfaces/IConsoleLogService.cs

BR-093 WriteToAppLog appends a line break by default when addressed by string log id, but not by default when addressed by LogType

Field Value
Statement WriteToAppLog appends a line break by default when addressed by string log id, but not by default when addressed by LogType.
Trigger Log contract usage
Enforcement IConsoleLogService.WriteToAppLog (2 overloads)
Outcome Differing default line-break behavior between overloads.
Source Common/SAIF.Common/Interfaces/IConsoleLogService.cs

BR-094 Log services and log writers are disposable resources and must be released by the consuming application

Field Value
Statement Log services and log writers are disposable resources and must be released by the consuming application.
Trigger Log contract usage
Enforcement IConsoleLogService : IDisposable, ILogWriter : IDisposable
Outcome Contract obligation on the implementing assembly*
Source Common/SAIF.Common/Interfaces/IConsoleLogService.cs, Common/SAIF.Common/Interfaces/ILogWriter.cs

Notes

  • Mark inferred statements with * and unknown statements with **. Do not use bold inline confidence labels.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

Sources

  • Common/SAIF.Common/Utility/TitleCase.cs
  • Common/SAIF.Common/Classes/Julian.cs
  • Common/SAIF.Common/Classes/DateMaker.cs
  • Common/SAIF.Common/Classes/DateRange.cs
  • Common/SAIF.Common/Extensions/DateRangeExtensions.cs
  • Common/SAIF.Common/Extensions/IComparableExtensions.cs
  • Common/SAIF.Common/Classes/Enforce.cs
  • Common/SAIF.Common/Extensions/GenericExtensions.cs
  • Common/SAIF.Common/Classes/SaifValidationException.cs
  • Common/SAIF.Common/Classes/ValidationResult.cs
  • Common/SAIF.Common/Classes/ContextException.cs
  • Common/SAIF.Common/Extensions/ExceptionExtensions.cs
  • Common/SAIF.Common/Extensions/ByteExtensions.cs
  • Common/SAIF.Common/Extensions/FileExtensions.cs
  • Common/SAIF.Common/Classes/Serializer.cs
  • Common/SAIF.Common/Extensions/DotNet40Extensions.cs
  • Common/SAIF.Common/Extensions/ExpressionExtensions.cs
  • Common/SAIF.Common/Classes/SequenceGenerator.cs
  • Common/SAIF.Common/Classes/Sequence.cs
  • Common/SAIF.Common/Classes/LongSequence.cs
  • Common/SAIF.Common/Extensions/UriExtensions.cs
  • Common/SAIF.Common/Extensions/SecurityPrincipalExtensions.cs
  • Common/SAIF.Common/Extensions/StringExtensions.cs
  • Common/SAIF.Common/Extensions/BooleanExtensions.cs
  • Common/SAIF.Common/Utility/StringUtilities.cs
  • Common/SAIF.Common/Extensions/NumericExtensions.cs
  • Common/SAIF.Common/Extensions/DictionaryExtensions.cs
  • Common/SAIF.Common/Extensions/IEnumerableExtensions.cs
  • Common/SAIF.Common/Classes/Reflector.cs
  • Common/SAIF.Common/Interfaces/IConsoleLogService.cs
  • Common/SAIF.Common/Interfaces/ILogWriter.cs
  • Common/SAIF.Common/Enums/LogType.cs