https://github.com/markderman/ordinaryinfrastructure
A pragmatic set of .NET componentry addressing ordinary use cases for line-of-business application development.
https://github.com/markderman/ordinaryinfrastructure
csharp design-by-contract dotnet email logging razor-templating result string-enum
Last synced: about 2 months ago
JSON representation
A pragmatic set of .NET componentry addressing ordinary use cases for line-of-business application development.
- Host: GitHub
- URL: https://github.com/markderman/ordinaryinfrastructure
- Owner: MarkDerman
- License: mit
- Created: 2023-07-30T19:26:24.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2026-02-06T06:55:48.000Z (5 months ago)
- Last Synced: 2026-02-06T14:48:30.553Z (5 months ago)
- Topics: csharp, design-by-contract, dotnet, email, logging, razor-templating, result, string-enum
- Language: C#
- Homepage:
- Size: 3.88 MB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 24
-
Metadata Files:
- Readme: README.md
- License: LICENSE.TXT
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README

# OrDinary INfrastructure
[](https://opensource.org/licenses/MIT)
## The Odin components
... are a collection born after years of building many line-of-business applications on .NET...
The result of componentising various recurring ordinary use-cases that we kept repeating in client systems at [Soulv Software](https://soulv.co.za/).
As at Dec 2025, the library is a hodge-podge of miscellaneous bits and bobs.
With .Net Core almost 10 years old now, I have never stopped missing using invariants on my domain entities with
the old Code Contracts from .NET Framework days, so I am now putting my attention to creating some form of runtime
support for preconditions, postconditions and class invariants for .NET 8 and up.
## Design Contracts :pencil2:
Coming soon... :construction:
## Result Pattern: Result and ResultValue
[](https://www.nuget.org/packages/Odin.System.Result) 
[Odin.System.Result](System/Result),
provides several **'Result'** classes, which all encapsulate the success of an operation, together with a list of messages.
**Result** is the simplest concept.
**ResultValue** adds a generic **Value** property.
**Result** and **ResultValue** add support for the **Messages** list to be of any type.
**ResultEx** and **ResultValueEx** come with a TMessage type that is aligned with logging failure issues.
[Getting starting with Result, ResultValue, and more...](System/Result)
1 - Success() and Failure()
```csharp
public class HeartOfGoldService
{
public Result WarpSpeedToMilliways()
{
if (_eddie.IsOK()) return Result.Success();
return Result.Failure(["Zaphod, that is not possible...", "Error 42"])
}
}
```
[Result documentation...](System/Result)
## Email Sending :email:
[Odin.Email](https://www.nuget.org/packages/Odin.Email) provides an IEmailSender with email sending support currently for Mailgun and Office365.
1 - Add configuration
```json
{
"EmailSending": {
"Provider": "Mailgun",
"DefaultFromAddress": "team@domain.com",
"DefaultFromName": "MyTeam",
"DefaultTags": [ "QA", "MyApp" ],
"SubjectPrefix": "QA: ",
"Mailgun": {
"ApiKey": "XXX",
"Domain": "mailgun.domain.com",
"Region": "EU"
}
}
}
```
2 - Add package references to Odin.Email, and in this case Odin.Email.Mailgun
3 - Add IEmailSender to DI in your startup code...
```csharp
builder.Services.AddOdinEmailSending();
```
4 - Use IEmailSender from DI
```csharp
MyService(IEmailSender emailSender)
{
_emailSender = emailSender;
}
```
5 - Send email
```csharp
IEmailMessage email = new EmailMessage(to, from, subject, htmlBody);
ResultValue sendResult = await _emailSender.SendEmail(email);
```
| Package | Description | Latest Version |
|:----------------------------------------------------------------------------|:---------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| [Odin.Email](https://www.nuget.org/packages/Odin.Email) | IEmailSender and IEmailMessage concepts | [](https://www.nuget.org/packages/Odin.Email)  |
| [Odin.Email.Mailgun](https://www.nuget.org/packages/Odin.Email.Mailgun) | Mailgun V3 API support | [](https://www.nuget.org/packages/Odin.Email.Mailgun)  |
| [Odin.Email.Office365](https://www.nuget.org/packages/Odin.Email.Office365) | Microsoft Office365 support (via MS Graph) | [](https://www.nuget.org/packages/Odin.Email.Office365)  |
## Odin.Logging :clipboard:
[](https://www.nuget.org/packages/Odin.Logging) 
Provides an **ILoggerWrapper of T** that extends .NET's ILogger of T with all the LogXXX(...) calls as provided by the .NET LoggerExtensions extension methods (and a few more), for simpler logging assertion verifications.
[Read more...](Logging/)
```csharp
// Log as you always do in your app...
_logger.LogWarning("Ford Prefect is missing!");
// Assert logging calls more simply in your tests...
_loggerWrapperMock.Verify(x => x.LogWarning(It.Is(c =>
c.Contains("Ford Prefect"))), Times.Once);
// as opposed to this with ILogger
_iLoggerMock.Verify(
x => x.Log(
LogLevel.Warning,
It.IsAny(),
It.Is((state, _) =>
state.ToString() == "Ford Prefect is missing!"),
It.IsAny(),
It.IsAny>()),
Times.Once);
```
## Razor Templating
Provides an IRazorTemplateRenderer for rendering .cshtml Razor files outside of the context of ASP.Net.
```csharp
// 1 - Add to DI in startup...
services.AddOdinRazorTemplating(typeof(AppBuilder).Assembly, "App.EmailViews.");
// 2 - Render cshtml views by passing in a model
ResultValue result = await _razorTemplateRenderer
.RenderAsync("AlertsEmail", alertingEmailModel);
myEmail.Body = result.Value;
```
| Package | Latest Version |
|:--------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| [Odin.Templating.Razor.Abstractions](https://www.nuget.org/packages/Odin.Templating.Razor.Abstractions) | [](https://www.nuget.org/packages/Odin.Templating.Razor.Abstractions)  |
| [Odin.Templating.Razor](https://www.nuget.org/packages/Odin.Templating.Razor) | [](https://www.nuget.org/packages/Odin.Templating.Razor)  |
## StringEnum
[](https://www.nuget.org/packages/Odin.System.StringEnum) 
[Odin.System.StringEnum](System/StringEnum/) provides enum-like behaviour for a set of string values via StringEnum,
as well as a useful StringEnumMemberAttribute. [Read more...](System/StringEnum)
1 - Define your string 'enum' with public string constants
```csharp
public class LoaderTypes : StringEnum
{
public const string File = "FILE";
public const string DynamicSql = "DYNAMIC-SQL";
}
```
2 - Use like an enum
```csharp
if (loaderOptions.LoaderType == LoaderTypes.DynamicSql)
```
3 - HasValue
```csharp
bool memberExists = LoaderTypes.HasValue("CUSTOM"); // returns false
```
4 - Values property
```csharp
string message = $"Valid members are: {string.Join(" | ", LoaderTypes.Values)}"
```
5 - Validation attribute
```csharp
public record LoaderEditModel : IValidatableObject
{
[Required(AllowEmptyStrings = false)]
[StringEnumMember]
public required string Loader { get; set; }
...
}
```
## Other Libraries
In various states of incubation, deprecation or neglect...
| Area | Description | Status | Version |
|:----------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------:|
| [Remote files \ SFTP \ FTPS](https://www.nuget.org/packages?q=Odin.RemoteFiles) | An abstraction of SFTP and FTPS file operations. | Needs attention | [](https://www.nuget.org/packages/Odin.RemoteFiles) |
| [Configuration - AzureBlobJson ](https://www.nuget.org/packages?q=Odin.Configuration.AzureBlobJson) | Support for json configuration source in Azure Blob Storage | | [](https://www.nuget.org/packages/Odin.Configuration.AzureBlobJson ) |
| [SQL scripts runner](https://www.nuget.org/packages?q=Odin.Data) | Useful for running database migration scripts at application deployment time. | | [](https://www.nuget.org/packages/Odin.Data.SQLScriptsRunner) |
| [Utility - Tax](https://www.nuget.org/packages?q=Odin.Utility.Tax) | Simple support for storing tax rate changes over time in application configuration, and then getting tax rates as at any date. | No docs | [](https://www.nuget.org/packages/Odin.Utility.Tax) |
| [BackgroundProcessing](https://www.nuget.org/packages?q=Odin.BackgroundProcessing) | Wrapper around Hangfire | Deprecated | [](https://www.nuget.org/packages/Odin.BackgroundProcessing) |
| [Notifications](https://www.nuget.org/packages?q=Odin.Notifications) | Messaging | Incubator | [](https://www.nuget.org/packages/Odin.Notifications) |
| [Cryptography](https://www.nuget.org/packages?q=Odin.Cryptography) | Wrapper around IDataProtector | Deprecated | [](https://www.nuget.org/packages/Odin.Cryptography) |