📔 Contents
- 📢 Introduction
- 🏋️ Stability
- 🏡 In-House
- 🏛️ History
- 🍱 Modularization
- 💁 Platform Compatibility
- 📋 Release Notes
- 💬 Feedback
JJ.Framework are extensions of the .NET Framework: the nuts, bolts and screws of software development: reusable components. Some of which might be expected to be there in .NET, but seem to be missing there.
JJ.Framework contains reusable code, that was produced along with real-life applications.
No software is bug free, but extra effort was made, testing the JJ.Framework components, that were published to NuGet which includes automated testing.
The ones not on NuGet are available as a sneek peek in JJs-Pre-Release-Package-Feed. But those may be used with caution, because they may not be tested that well. Those may need some more unit testing and could still be a bug or two in it.
JJ.Framework was made in the spirit of: small things: develop your own, instead of importing a huge framework.
JJ.Framework components are compact: a couple of kilobytes each.
Developed in-house so that it could be adapted and extended as needed. It did not have to be complete, because then we would just extend it.
If a bug was found in the development of an app, we could just correct the JJ.Framework as needed and release a stable app.
Of course, this 'in-house development' argument does not apply, when other people use it. But I think these extensions could be of use to others too.
Any reusable code independent of a particular business domain, was moved to this JJ.Framework, for reuse. The reusable code was gathered up between 2010 and 2026. It was a hobby project. But something similar to this framework, was also developed professionally in a team of 7 of which I was the technical lead at the time.
JJ.Framework tends to have a separate assembly for each part of the .NET Framework that it extends. Separation may be found between technical aspects. That may allow being specific, on which parts of .NET an application becomes dependent.
JJ.Framework also likes to decouple platform-specific implementation details and hide them behind generalized interfaces.
That grip on dependency, was once a good plan for platform compatibility. Some of JJ.Framework is still based on .NET 4.6.1 trying to keep it compatible with Mono. Around 2015 JJ.Framework was tested to be reasonably compatible with Mono and by extension Android, iOS and Windows Phone. This was before newer technology like .NET Standard and .NET 5 + emerged, which could make platform-compatibility a bit more doable. For this reason, some of the JJ.Framework is currently being ported to a newer .NET, but this is still in progress.
- Internal tooling
- Triggers initialization at start of
JJ.Frameworksolution build.
JJ.Framework.Existence.Core- Initial release
JJ.Framework.Existence.CorespaceMattersflags everywherecaseMattersreplacedignoreCaseStringBulder/stringmore combos possiblex.In(a, b, c)extension favored over staticIn(x, a, b, c)
JJ.Framework.Existence.CoreJJ.Framework.Reflection.Legacy- Separate
Legacyversion lineages - For more stable package updates.
- Expanded IntelliSense docs.
JJ.Framework.Existence.CoreJJ.Framework.Reflection.Legacy- Compatibility with AOT "Ahead-Of-Time" native compilation, code trimming and single-executable, self-contained apps
JJ.Framework.Existence.Core- Flag when you consider
0meaningful (not nully)
- Upgraded with explicit support for
.NET 10
JJ.Framework.Existence.Core- Simplify overload picking removing some
[Prio]attributes - Sacrifices direct use of keywords e.g.
Coalesce(null, "Hallo"); not a real use case anyway.
JJ.Framework.Existence.CoreJJ.Framework.Reflection.Legacy- Flags at the beginning of the parameter list, now don't come with a performance penalty.
JJ.Framework.Existence.Core- More notation options for flags for the
Containsmethod
JJ.Framework.Existence.Core- Support flags in front for the
FilledInmethod
JJ.Framework.Existence.Core- Leading flags for
Has/Is/In/IsNully(supplements trailing flags) - Improved
Coalesceresolution and performance for 4+ values and collections. - Added overloads for reduced clashes between
boolflags and value lists. - Various non-critical renames for clarity and modularity.
JJ.Framework.Common.CoreOverloadByNamehelper for an optional parameter trick to simulate overloading by name, not normally possible in C#.
JJ.Framework.Business.Legacy- Release of historic version. Fullly covered with meaningful tests. Features:
EntityStatusManagerandGetListIsDirty:- Framework-agnostic
New/Deleted/Dirtyflag container. ISideEffectfor keeping business logic units separated.- Bug fix: Entity status
NewandDeletedwere accidentally stored asDirty.
JJ.Framework.IO.Legacy- Full meaningful test coverage, and member docs.
- Release of historic version
0.250. - Features:
CsvReader: simple streaming CSV reader supporting quoted fields and escaped double-quotes ("").StreamHelper: convenience conversions betweenStream,byte[]andstringReadStruct/WriteStruct: methods for simple reading and writing structs to streams.- Bug fix:
CsvReaderpreserves a trailing escaped quotes - Bug fix:
CsvReadersolved crash over trailing new line
- 2018 variant of
ReflectionCacheLegacymoved from Reflection.CoretoReflection.Legacywith meaningful test coverage.- Trimming/AOT compatiblity.
- Auto-run trim test
- upon build
- and inside NCrunch.
JJ.Framework.Validation.Legacy- Historic version + full meaningful test coverage / IntelliSense docs / code trimming / native AOT support
- .NET
10,9,8,7,6,5,4.6.1/ .NET Standard2.0,2.1 FluentValidatorsyntax:
For(() => player.TotalScore, "Total Score").Min(0).Max(100);NotNull,NotNullOrWhiteSpace,In,Is,IsNot,NotZero,Above,Min,Max,NotInteger,IsEnumValueIValidator/ValidatorBasemembers:
IsValid(boo),ValidationMessages,Verify()(throws)- Sub-validators and message prefixes:
"Address: Street is required." - Localization: English (
en-US) and Dutch (nl-NL)
JJ.Framework.PlatformCompatibility.Core- Run new code on older .NETs
- Shims for argument checks, language features, nullability, trimming/AOT, threading and hash codes
- Full coverage with meaningful tests and member docs
JJ.Framework.Validation.Legacy- More IntelliSense popups
- Reworked README
JJ.Framework.Mathematics.Legacy- Integer-based
PowandLog,GetRandomItemandRandomizer.GetInt32, number base converter. - 100% meaningful test coverage, compatibility with Code Trimming and AOT / native compilation, and a whole spread of .NET versions, and IntelliSense doc everywhere.
- Fix:
PowandLogedge-cases - Fix: Removed dependency on JJ.Framework.Reflection.Legacy
JJ.Framework.Testing.Core- Removed lambda from delta-based
AreEqual(trim-friendly) JJ.Framework.Testing.Core.MSTestless- Added stubs for data-driven tests.
- Improved Testing Architecture
+ Integrity Checker + Assert.Throws Syntax
JJ.Framework.StringResources.Legacy2015: Limited set of reusable string resources2020: Reusable tester verifying integrity2026: Extensions:- Placeholder verification
- Resource and formatter classes
- Static and instance members
- Free to chose fallback culture
- Optional trace logging
- Customization options
- Wide .NET version compatibility, Trimming/AOT support
- IntelliSense docs, 100% meaningful test coverage
JJ.Framework.Testing.CoreThrowsmethods syntax shorter
+ Various Fixes
JJ.Framework.Compilation.Core- Brand new dotnet.exe CLI wrapper. Run compilations and other dotnet actions programmatically at runtime.
- Preview release. Works in general. More coverage desired. More features desired.
JJ.Framework.Configuration.Core- Auto-copy config files from proj to bin as classic System.Configuration did for you.
JJ.Framework.Configuration.Legacy- Prevent leaking System.Configuration.ConfigurationManager reference transiently to avoid clashes with older .NET versions.
JJ.Framework.Testing.Core- Show tested argument expression more often, even when a custom message provided, which would formerly override it.
- Complete test suite against regression.
- TestRunner parallel by default but respect [DoNotParallelize] attribute. Race conditions mitigated.
- Throws methods now return Exception in case they need further inspection.
JJ.Framework.Exceptions.Core- ThrowIf method for guard statements to assert any condition.
- Overload added for chainable NotNull assertion method.
JJ.Framework.Reflection.Core- Fix DynamicallyAccessedMembers annotation for code trimming on AccessorCore's target object auto-instantiation (AllCtors instead of just PublicCtors).
- Add Assembly-related overloads.
JJ.Framework.PlatformCompatibility.Core- Fix Lock shim that was accidentally not thread-safe.
- Add NotNullIfNotNull shim for nullable annotation support.
- Shim for ProcessSupport.Kill with entireProcessTree arg (does not actually kill entire process tree, but prevents compiler errors).
JJ.Framework.SharedProject.Core- Improve reusable NoTrimReason strings, so they can be combined without redundancies with this syntax:
- [TrimWarn(PropertyTypeAnd + ObjectGetType)]
- The And-version includes a space separator and the non-And variant includes the DoWhatInstead.
DevOps:- Split Directory.Build.props into separate XML files / made modular.
- Use .NET versions MSBuild convenience variables.
- Add AutoPublish var for central conditions and the possibility to override (per project).
- ClearCache calls new ClearMisc to wipe Azure Pipelines folders.
- AzurePipelinesClearRetainFlags scripts helping to clean up locked Azure DevOps objects clearing excessive storage use.
- RunTrimTest.bat now operates on current dir, not bat file dir.
+ Related Fixes
JJ.Framework.Mathematics.Legacy- Moved
RandomizerLegacyclass from.Corevariant of project to.Legacyproject. - Fully tested and now release as part of the .Legacy lib.
- Expanded for coherence. And ported back to 2021 legacy branch.
- (RandomizerLegacy is a 2021 version. Legacy projects contain code from 2015; Core projects code from 2025. Code from 2021 is somewhere in the middle. It made more sense to move those from .Core to .Legacy since the workflow around Legacy code is so much different than Core, that it makes more architectural sense to group them.)
JJ.Framework.IO.Core- Fix
MutexUnauthorizedAccess in Azure Pipelines. - For numbering file names (in highly concurrent situations).
- Mutex created by other user, wouldn't be accessible to one in a different security context.
MutexSecurityspecifying wider security access, so it can be used machine-wide as intended.JJ.Framework.PlatformCompatibility.CoreOperatingSystemSupport.IsWindows(): a check for feature toggles (not present on all .NET version)- (Used for conditional MutexSecurity instantion.)
- Added
net462target framework extra hardening. NotNullIfNotNullretroactively added (accidentally not released last time).JJ.Framework.Common.CoreGenericOverload/GenOvl:- Placeholder type for adding optional parameters to generic overloads, that would otherwise clash.
- This type adds docs explaning the technique, and avoids further overload clashes, from chosing a different parameter type.
JJ.Framework.IO.Legacy- Move
StreamHelperLegacyfrom.Corevariant of project to.Legacyvariant - Port their tests from
legacybranch tomain - Tighten nullability checks.
JJ.Framework.IO.Core- Share
NumberedFilePath/SafeFileStreamTestsback and forth betweenlegacyandmain. MutexUnauthorizedAccessfixes for Azure Pipelines after introducing more tests.
JJ.Framework.Business.LegacyResulttypes.- Moved from
.Coreto the.Legacyproject. - Removed
JJ.Business.IO.Coreproject variant. - Full, meaningful test coverage, IntelliSense docs.
- Tighter nullability, diagnostic texts.
JJ.Framework.IO.CoreSafeFileStreamMutex- Use
Localnamespace instead ofGlobal(for per-user security) - Create with
ACL(Access Control Descriptor) first, then fallback to without. - Refactor Mutex creation methods to reuse the same logic.
JJ.Framework.Logging.CoreTracelogging (alongside Console and Debug) for certain debugging scenarios.JJ.Framework.PlatformCompatibility.Corerequiredkeyword .NET 5 / .NET 6 support.
JJ.Framework.Business.Legacy- Success =
falseby default (previously true) improving compatibility and security. - Add
SuccesfulandVoidResultaliases for Success and Result for compatibility. Messagesnot separated by commas in diagnostics texts. Punctuation left up to the messages themselves.
Got feedback or questions? You can reach me here.