Axinom Toolkit
This is a pile of fairly arbitrary helper functionality that has proven useful on many different projects and was thus made reusable. The most important and useful features are described here separately, with the rest being in the API documentation.
Compatibility
Axinom Toolkit targets:
- Any .NET Standard 2.0 platform.
- .NET Framework 4.7.2 (some platform-specific features).
Installation
Install the Axinom.Toolkit NuGet package from the Axinom cloud NuGet repository. This contains all the core functionality.
There also exists a separate Axinom.Toolkit.NLog NuGet package that contains the functionaltiy to integrate NLog with the Axinom Toolkit logging channels.
There also exists a separate Axinom.Toolkit.Jose NuGet package that contains JOSE protected messaging functionality. It is a separate package due to a dependency on a 3rd party library.
Helper method usage
For boring technical reasons, you must access helper methods using the structure Helpers.Argument.ValidateIsNotNull()
, always starting from the Helpers
object. In code and documentation, these helpers are actually structured differently, for example as NetStandardHelpers.ValidateIsNotNull()
.
This does not apply to stand-alone classes that are not merely helper methods.
Logging channels
Axinom Toolkit uses and provides its own logging channels, which can be easily redirected into other standard logging systems such as the .NET Framework tracing channel or libraries such as NLog
.
Here is a highly abbreviated usage reference:
// Register a listener that writes log entries to the tracing channel.
Log.Default.RegisterListener(new TraceLogListener());
// Register a listener that writes log entries to file.
Log.Default.RegisterListener(new StreamWriterLogListener(new StreamWriter(File.Create("Log.log"))));
// Register a listener that writes all log entries to an NLog LogFactory.
// The Axinom Toolkit log entry source name is translated to the NLog logger name.
// Obviously, you also need to configure NLog to do something with the entries in real world code!
Log.Default.RegisterListener(new NLogListener(new LogFactory()))
// Write a log entry.
Log.Default.Info("Hello worolor.")
// Create a child log event source to categorize log entries.
var log = Log.Default.CreateChildSource("Example");
log.Warning("This is a log entry logged under the Example source.")
// Clean up and flush all listsners at application shutdown.
// Mandatory! Without this, some log entries near the end may go missing, depending on the listeners you use!
Log.Default.Dispose();
Error reports
To easily log an exception and capture basic information about the operating environment, use the ErrorReport
class.
// Basic usage, logs the error to the Axinom Toolkit logging channels under the "ErrorReport" source.
ErrorReport.Log(new Exception("just an example"));
// You can also add any custom data you wish to error reports.
ErrorReport.Log(new Exception("another example"), new
{
CustomData = "my name is",
Something = new[] { 1, 2, 3, 4 }
});
Web.config configuration section loading
You often want to have XML configuration files. The .NET Framework config file system is somewhat difficult to work with, so Axinom Toolkit provides some assistance for you, enabling you to load configuration sections using XmlSerializer
and ensure they are always validated on load.
First, create a class to represent your configuration:
[XmlRoot("CheckDashboard")]
public sealed class CheckDashboardConfiguration : IValidatable
{
// Default values, if not provided in web.config.
internal const int DefaultMaxConcurrentChecks = 100;
[XmlElement]
public string DashboardTitle { get; set; }
[XmlElement]
public int MaxConcurrentChecks { get; set; }
#region Singleton
internal static CheckDashboardConfiguration Current
{
get
{
var section = ConfigurationManager.GetSection("CheckDashboard") as CheckDashboardConfiguration;
if (section == null)
throw new ConfigurationErrorsException("CheckDashboard configuration section is missing or invalid.");
return section;
}
}
#endregion
public CheckDashboardConfiguration()
{
// Initialize defaults here.
MaxConcurrentChecks = DefaultMaxConcurrentChecks;
}
// Always called on load.
void IValidatable.Validate()
{
if (string.IsNullOrWhiteSpace(DashboardTitle))
this.ThrowValidationFailureFor(nameof(DashboardTitle), "No dashboard title has been set in the check dashboard configuration.");
if (MaxConcurrentChecks <= 0)
this.ThrowValidationFailureFor(nameof(MaxConcurrentChecks), "Must be positive.");
}
}
Then write the equivalent in the web.config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="CheckDashboard" type="Axinom.Toolkit.DynamicConfigurationSection, Axinom.Toolkit.NetFramework" />
</configSections>
<CheckDashboard type="Axinom.Monitoring.CheckDashboard.CheckDashboardConfiguration, Axinom.Monitoring.CheckDashboard">
<DashboardTitle>Sample Dashboard</DashboardTitle>
<MaxConcurrentChecks>3</MaxConcurrentChecks>
</CheckDashboard>
</configuration>
Naturally, this also works for app.config, in addition to web.config.
Object dumping
To transform any object to a human-readable string representation, use Helpers.Debug.ToDebugString(x)
.
Protected message exchange
Axinom Toolkit provides helper methods for wrapping data in encrypted and signed envelopes, either in XML or JOSE format. The envelope is encrypted for a specific recipient (identified by X.509 certificate) and signed by a specific author (also identified by X.509 certificate). You can use this for general-purpose secure data exchange.
XML secure messaging is provided for .NET Framework only. JOSE secure messaging is universal. The JOSE messaging support is in the Axinom.Toolkit.Jose NuGet package, as it depends on an external library for the JOSE serialization support.
XML messaging example code:
XmlDocument document = LoadDocument();
// The document is modified in-place.
Helpers.ProtectedXml.EncryptAndSign(document, recipientCertificate, signerCertificateWithPrivateKey);
X509Certificate2 signedBy;
// The document is modified in-place.
Helpers.ProtectedXml.VerifyAndDecrypt(document, out signedBy, recipientCertificateWithPrivateKey);
JOSE messaging example code:
byte[] data = LoadData();
var envelope = Helpers.Jose.EncryptAndSign(data, recipientCertificate, signerCertificateWithPrivateKey);
X509Certificate2 signedBy;
var decryptedData = Helpers.Jose.VerifyAndDecrypt(envelope, out signedBy, recipientCertificateWithPrivateKey);
The envelope format is custom, as no widely usable general purpose message format exists. The specific algorithms and cryptographic configuration are hardcoded but may change in future versions of Axinom Toolkit, though backward compatibility is guaranteed for read operations.
API reference
There are many different helper methods included. Refer to the .NET API reference to leran about them all.
Changelog
14.0.0
- Added Helpers.Random.GetBoolean().
- Added Helpers.Random.GetWords() with length limit.
- Added more words to random test data words.
- Added Helpers.Random.GetRandomItems().
- Reduced string formatting overhead in logging system.
- Removed oldschool string.Format() style logging methods. Use interpolation.
- Added DelegatingDisposable.
- Helpers.Debug.GetAllExceptionMessages() now returns also exception types and unrolls aggregate exceptions.
- Helpers.Debug.ToDebugString() now uses a local parameterless ToString() if one exists.
13.0.0
- .NET Framework specific features now require 4.7.2 because it is 2019.
- Updated NLog dependency to latest stable.
- Added Helpers.Argument.ValidateLength
() generic overload.
12.1.2
- WeakEventListener is now thread-safe.
12.1.1
- Fixed crash due to overflow when comparing GrpcAddress.
12.1.0
- Addewd GrpcAddress.
12.0.0
- Added ExtensionsForCollections.Shuffle().
- TestClass -> BaseTestClass to reduce naming conflicts.
11.2.1
- .NET Core 2.1 compatibility for ExternalTool. They changed an exception type.
11.2.0
- Wildcard now supports multiple patterns as alternatives.
11.1.1
- Performance optimizations for CompositingStream.
11.1.0
- Added ExtensionsForTask.LogExceptions() for easy exception logging without having to await.
11.0.0
- Removed Helpers.Timeout.GetCancellationToken() as it used some bad practices for CTS lifetime management.
- ExternalTool now supports asynchronous usage.
10.0.4
- ExternalTool now closes standard input after use, to signal apps that all input has been provided.
10.0.3
- Helpers.Filesystem.GetBinDirectory() now also works for development environment .NET Core apps.
10.0.2
- Helpers.Filesystem.GetBinDirectory() now also works for web apps.
10.0.0
- Targeting .NET Standard 2.0 with primary library. This moved a fair amount of types around, beware!
- Axinom.Toolkit.DotNet assembly renamed to Axinom.Toolkit.NetFramework to better reflect its platform.
- Removed some super obsolete stuff not used in 5 years. Most things survived the porting (NS 2.0 is pretty good).
- Added changelog to documentation website.
- Helpers.Async.BackgroundThreadInvoke() now returns a Task for the created operation.
- Helpers.Argument.ValidateIsNotNullOrEmpty() supports more types of collections.
- Helpers.Debug.ToDebugString() now reports correct length for collections that contain more than 64 items.
- Added Helpers.Filesystem.GetBinDirectory().
9.3.2
- Fixed race-condition in fast-exiting ExternalTool.
9.3.1
- Removed extraneous newlines from GetAllExceptionMessages.
9.3.0
- Added Helpers.Debug.GetAllExceptionMessages to dump all messages of exceptions, including inner exceptions.
9.2.2
- Censored strings for ExternalTool that are empty are now ignored.
9.2.0
- ExternalTool now supports censoring sensitive command line arguments.
9.1.0
- SemaphoreLock now has overloads with CancellationToken.
- Added DelegatingLogListener.
- Added Helpers.Timeout.GetCancellationToken() for easy creation of finite cancellation tokens.
- Logging API now accepts FormattableString to help with potential future optimization.
9.0.0
- .NET Standard DOES NOT WORK. Exterminated!
8.0.0
- Now targeting .NET Standard 1.4 + .NET Framework 4.6.2 + UWP 10240.
- Updated dependencies.
- Renamed Core -> NetStandard.
- Removed xunit console runner from use in development environment. It had issues with UWP. Not worth the effort.
7.1.2
- Removed misleading commentary about SDK versions. We target 10240, that's it.
7.1.1
- Fixed defect in PlayReadyLicenseAcquirer that caused reactive PlayReady activation to fail.
7.1.0
- Added base64url to/from conversion helpers.
- Added ProtectedXml helpers. You can now encrypt&sign and verify&decrypt XmlDocuments with a one-liner helper method. DotNet only, for now.
- Added Jose helpers. You can now encrypt&sign and verify&decrypt arbitrary data packaged into JOSE objects with a one-liner helper method. DotNet only, for now. Uses jose-jwt underneath.
7.0.0
- ErrorReport.Send() replaced with ErrorReport.Log(). The error reports are now written to the logging system instead of sent via email and event log. You are still able to accomplish the latter but you must use an appropriate log listener to do the actual delivery. This significantly simplifies the implementation and maintenace of ErrorReport.
- ErrorReport contents rewritten to include less irrelevant spam. Some edge cases may no longer be supported. Scream if some important info is missing and it will likely be added to the next version.
- Added Task.WithAbandonment() extension method, to abandon tasks.
- Added timeout-enabled overload for HttpResponseMessage.EnsureSuccessStatusCodeAndReportFailureDetailsAsync().
- UWP target platform upgraded to 10586 (minimum remains at 10240).
- Updated UWP and NLog NuGet package dependencies.
6.9.0
- ExternalTool now reports part of any error output in its exception message when an external tool failure is detected, for ease of diagnostics.
6.8.0
- Added DASH support to Helpers.Media.GetKeyIds().
6.7.0
- Added PlayReadyLicenseAcquirer class, which enables UWP apps to support PlayReady without the need for Microsoft Player Framework.
- Refactored PlayReady client code to use PlayReadyClientConfiguration class instead of stand-alone arguments for configuring the client behavior.
- Added capability for adding custom HTTP request headers to PlayReady client requests.
- Helpers.Random.GetRandomItem() can now also work with read-only lists.
6.6.0
- Moved Helpers.XmlSerialization from DotNet to Core.
6.5.0
- Added Helpers.Media.CreatePsshBox() for easy generation of ISO Base Media File Format compatible PSSH boxes.
- Added Helpers.Widevine.GenerateWidevineCencHeader() for easy generation of a Widevine CENC header.
6.4.0
- Added Helpers.Certificate.CleanWindowsThumbprint() for cleaning the certificate thumbprint values from the Windows GUI.
- Helpers.Debug.ToDebugString() now attempts to prevent recursion with static struct fields like IntPtr.Zero.
6.3.0
- Optimized Helpers.Media.Crop to be more faithful to aspect ratio while avoiding needless cropping.
6.2.0
- Added Helpers.Media.GetKeyIds() and related Helpers.PlayReady methods.
6.1.0
- Added support for automatic domain joining when acquiring PlayReady licenses.
- Added Helpers.PlayReady.FulfillServiceRequest().
6.0.0 - Axinom Toolkit is now compatible with .NET 4.6 and UWP 10.0.
- Created assembly Core that houses portable classes.
- Services.Client package merged into Core assembly and NuGet package marked obsolete.
- Refactored helper method access patterns from FilesystemHelper.DoWhatever() to Helpers.Filesystem.DoEhatever() to better facilitate multi-framework library composition using extension methods.
- Removed thread information from logging system log entries, since it was not portable.
- Helpers.Async.BackgroundThreadInvoke() now uses Task instead of explicitly using Thread, for enhanced portability.
- AsyncHelper.WaitSafe() had confusing naming, now renamed to just .WaitAndUnwrapExceptions() extension method - nothing particularly safe about it, we just unwarp the exceptions from AggregrateException and that's it.
- Added more async helper methods that become relevant with async programming.
- Added DurationLogger, DebugLogListener, StreamWriterLogListener.
- Helpers.Argument.ValidateLength now takes any collection, not just array.
- Added Uwp library for UWP specific features, starting out with FixedAspectRatioBox and an assortment of converters.
- RandomText renamed to Helpers.Random and added various more random data retrieval methods.
- WeakEventListener now supports IDispose for easy detaching.
- Added Helpers.PlayReady.GenerateRightsManagementHeader() and Helpers.PlayReady.EnsureActivatedAsync() (UWP).
- Added ExtensionsForObservableCollection.WeakObserve().
- Added Helpers.Network.IsInternetAvailable() (UWP).
- Added Helpers.PlayReady.AcquireLicenseAsync() (UWP).
- Added ExtensionsForIAsyncAction and ExtensionsForIAsyncOperation (UWP).
- Added Helpers.Filesystem.GetFreeSpace() (UWP).
- Added Helpers.PlayReady.IsPersistentLicensePresent() (UWP).
- Added ExtensionsForUwpHttpResponseMessage to target the UWP version of HttpClient with the same feature set as .NET.
- Added ExtensionsForIRandomAccessStream (UWP) to easily clear streams (got to remember to seek!).