Ste11ar.Core
1.0.7
Stellar.Core is taken.
dotnet add package Ste11ar.Core --version 1.0.7
NuGet\Install-Package Ste11ar.Core -Version 1.0.7
<PackageReference Include="Ste11ar.Core" Version="1.0.7" />
<PackageVersion Include="Ste11ar.Core" Version="1.0.7" />
<PackageReference Include="Ste11ar.Core" />
paket add Ste11ar.Core --version 1.0.7
#r "nuget: Ste11ar.Core, 1.0.7"
#:package Ste11ar.Core@1.0.7
#addin nuget:?package=Ste11ar.Core&version=1.0.7
#tool nuget:?package=Ste11ar.Core&version=1.0.7
Stellar Core
Extensions and utility library supporting the Stellar family of products, mostly subtracted from Stellar.IO and Stellar.DAL Copyright (c) 2025 Cloudkitects, LLC under MIT License.
These classes and extensions tend to common Extract Transform and Load (ETL) use cases, lower the barrier between applications and data repositories and support ETL-as-Code.
Bucket
A bucket wraps a <TKey, bool>
dictionary initialized with a set of distinct expected elements. As elements are added to it, the bucket flips the bits and is eventually full:
var bucket = new Bucket<string>([ "apples", "oranges", "bananas" ]);
bucket.Add("apples");
Assert.True(bucket["apples"]);
Assert.False(bucket["oranges"]);
Assert.False(bucket["bananas"]);
Assert.False(bucket.IsFull);
bucket.Add("oranges");
bucket.Add("bananas");
if (bucket.IsFull)
{
// peel and chop!
}
A real-world use case kicks off the next ETL hop once expected files are loaded in batch processes, e.g., refresh the model with today's customer
, accounts
and balances
data.
In essence, a bucket is a simpler user-defined synchronous stand-in for Task.WhenAll()
.
Buffer
A generic, resizable, random-access buffer initialized to the given capacity and with an optional fill callback and a bookmark equality comparer.
Extirpated from Stellar.IO, it essentially supports I/O readers:
// buffer fill callback wraps TextReader.Read() to handle range and signal EOF...
private int BufferFillCallback(char[] buffer, int offset)
{
ArgumentNullException.ThrowIfNull(buffer);
if (offset < 0 || buffer.Length <= offset)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if (EOF)
{
return 0;
}
var count = TextReader.Read(buffer, offset, buffer.Length - offset);
EOF = count <= 0;
return count;
}
// somewhere within the reader ctor...
buffer = new Buffer<char>(bufferSize, BufferFillCallback);
// buffer supports token-based reading
protected override ReadResult ReadCore(Buffer<char> buffer, IList<string> values)
{
...
while (buffer.Refill() && !IsNewLine(buffer.Current))
{
keepValue = DynamicFieldCount || values.Count < Fields.Count && !Fields[values.Count].Skip || readingHeaders;
var value = ParseField(buffer, keepValue, out var endsWithDelimiter, out var parsingErrorOccurred);
if (!parsingErrorOccurred)
{
values.Add(value);
}
else
{
HandleParseError(new MalformedRecordException(new string(buffer.Items, 0, buffer.Count), buffer.Position, CurrentRecordIndex, values.Count));
return ReadResult.ParseError;
}
...
DefaultValueDictionary
Implements IDictionary<TKey, TValue> where TKey : notnull
and returns the TValue?
default when accessing keys that do not exist in the dictionary.
The constructor optionally takes in a dictionary and a TKey
equality comparer that comes in handy to ignore case-sensitivity.
Extirpated from the DAL, it essentially supports DynamicInstance
.
DynamicInstance
Implements a DynamicObject
overiding call-site binders to return entries from a private default value dictionary.
// a built-in dynamic object throws runtime binder exceptions
// for mispelled or non-initialized properties...
dynamic person = new { Age = 42 };
var age = person.age; // ...throws (person does not contain a definition for 'age')...
var name = person.Name; // ...throws (person does not contain a definition for 'Name')...
// ...whereas a dynamic instance that ignores case is very forgiving :)
var person = new DynamicInstance();
var first = person.FirstName; // first == null
person.Alterego = "Superman";
person["PARTS HAIR TO THE"] = "Right";
Assert.Equals("Superman", person.ALTEREGO);
Assert.Equals("Right", person["parts hair to the"]);
Strongly-typed yet loosely-defined objects can help reduce impedance between objects and data transfer object, or what we call the "BLT no tomato" impedance problem: the API delivers a BLT and the UI ignores the tomato, or the API delivers a BL and tomato is null to the UI. In other words, the API and the UI can disagree on the definition of an object without breaking.
DynamicDictionary
The DAL leverages dynamic dictionaries as controller sit-ins in the Model-View-Controller (MVC) pattern and for Rapid Application Development (RAD) use cases:
// somewhere in a model...
public static List<dynamic> GetReport(string period)
{
return GetCommand()
.SetCommandText(report_sql)
.AddParameter("period", period)
.ExecuteToDynamicList();
}
// somewhere in a view...
var entries = model.GetReport("2025");
foreach (var entry in entries)
{
Report.AddNewRow();
Report.AddCell((string)entry.period, 0, align: TextAlignment.End);
Report.AddCell((string)entry.product, 1, align: TextAlignment.Start);
Report.AddCell($"{entry.inventory:N0}", 2);
Report.AddCell($"{entry.sold:N0}", 3);
Report.AddCell($"{entry.sales:C}", 4, align: TextAlignment.End);
Report.AddCell($"{entry.fees:C}", 5, align: TextAlignment.End);
Report.AddCell($"{entry.profit:C}", 6, align: TextAlignment.End);
}
// ...controller'd be like... wth?
Code could of course be moved to the controller, and this feature does not preclude the use of strongly-defined objects with the DAL. Using loosely-defined objects is just a quick way to get things (and builds) going. This is of course a double-edged sword, as it can still lead to runtime errors and information loss.
EnumHelper
An in-depth mapper of declared string or underlying values to enum members, with helpers checking if a value is defined, getting member names, etc. It again aims at reducing the impedance between ETL-as-Code (JSON, YAML) and CSharp.
Extensions
Safe versions of intrinsic methods, e.g., try/catch wrappers with fallback values.
FileInfoEx
Wraps FileInfo
with a few safe wrappers and file name metadata. Used by the FileNameParser
, next.
FileNameParser
Another ETL chore, this class parses a file name using a regular expression with both built-in and custom groups to produce a new file name based on a template. Built-in placeholders include file metadata including the file name, base file name (file name without extension), created and modified date times and file name segments identiying parts such as type and timestamp.
var result = FileNameParser.TryParse(
filename: "Hello20241231.txt",
pattern: "Hello(?<timestamp>.+).txt",
template: "{timestamp:yyyy-MM-dd}-hello.tsv", out var output);
Assert.Equal("2024-12-31-hello.tsv", output.Filename);
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- YamlDotNet (>= 16.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|