mirror of
https://github.com/jcreek/Creek.HelpfulExtensions.git
synced 2026-07-14 19:53:44 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b8acdf4d9 | |||
| d8ab65334a | |||
| a44420e81d | |||
| 3940aaf966 | |||
| aabbfcaf70 | |||
| 7b585dfc1e | |||
| eb463a0f36 | |||
| 7037e2dc5e | |||
| af60820c10 | |||
| 14530efb0f |
@@ -19,11 +19,12 @@
|
|||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<PackageTags>creek</PackageTags>
|
<PackageTags>creek</PackageTags>
|
||||||
<Version>1.1.0</Version>
|
<Version>1.4.3</Version>
|
||||||
<AssemblyVersion>1.1.0.0</AssemblyVersion>
|
<AssemblyVersion>1.4.0.3</AssemblyVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Creek.HelpfulExtensions
|
||||||
|
{
|
||||||
|
public class DisposableStopWatch : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private Stopwatch _stopWatch;
|
||||||
|
private string _message;
|
||||||
|
private bool _useConsole;
|
||||||
|
private bool _useConsoleOnly;
|
||||||
|
|
||||||
|
public DisposableStopWatch(ILogger logger, string message = null, bool useConsole = false)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_useConsoleOnly = false;
|
||||||
|
_stopWatch = new Stopwatch();
|
||||||
|
_message = message ?? string.Empty;
|
||||||
|
_useConsole = useConsole;
|
||||||
|
|
||||||
|
string startMessage = $"Start: {message}";
|
||||||
|
|
||||||
|
if (_useConsole)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(startMessage);
|
||||||
|
Console.WriteLine(startMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation(startMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stopWatch.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisposableStopWatch(string message = null)
|
||||||
|
{
|
||||||
|
_useConsoleOnly = true;
|
||||||
|
_stopWatch = new Stopwatch();
|
||||||
|
_message = message ?? string.Empty;
|
||||||
|
|
||||||
|
string startMessage = $"Start: {message}";
|
||||||
|
|
||||||
|
Console.WriteLine(startMessage);
|
||||||
|
|
||||||
|
_stopWatch.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_stopWatch.Stop();
|
||||||
|
|
||||||
|
string endMessage = $"Complete:{_message}: Elapsed: {_stopWatch.Elapsed}";
|
||||||
|
|
||||||
|
if (_useConsoleOnly)
|
||||||
|
{
|
||||||
|
Console.WriteLine(endMessage);
|
||||||
|
}
|
||||||
|
else if (_useConsole)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(endMessage);
|
||||||
|
Console.WriteLine(endMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation(endMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DisposableStopWatchExtension
|
||||||
|
{
|
||||||
|
public static DisposableStopWatch DisposableStopWatch(this ILogger logger, string message = null, bool useConsole = false) => new DisposableStopWatch(logger, message, useConsole);
|
||||||
|
|
||||||
|
public static DisposableStopWatch DisposableStopWatch(this string message) => new DisposableStopWatch(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Creek.HelpfulExtensions
|
||||||
|
{
|
||||||
|
public static class ExceptionExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a string of the message of the first/root/base exception in an exception stack.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">The Exception stack to find the first/root/base exception within.</param>
|
||||||
|
/// <returns>Returns the first/root/base exception message string.</returns>
|
||||||
|
public static string BaseExceptionMessage(this Exception ex)
|
||||||
|
{
|
||||||
|
Exception baseEx = ex.GetBaseException();
|
||||||
|
return baseEx.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a single string of the message and stack trace of the first/root/base exception in an exception stack.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">The Exception stack to find the first/root/base exception within.</param>
|
||||||
|
/// <returns>Returns the first/root/base exception message and stack trace as a single string, separated by " :: ".</returns>
|
||||||
|
public static string BaseExceptionMessageAndStackTrace(this Exception ex)
|
||||||
|
{
|
||||||
|
Exception baseEx = ex.GetBaseException();
|
||||||
|
return GenerateMessageAndStackTraceString(baseEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of the exception messages of each nested exception in an exception stack.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">The Exception stack.</param>
|
||||||
|
/// <returns>Returns a list of the exception messages of each nested exception in an exception stack.</returns>
|
||||||
|
public static List<string> AllInnerExceptionMessages(this Exception ex)
|
||||||
|
{
|
||||||
|
return GetAllInnerExceptionMessagesAndStackTraces(ex, includeStackTraces: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of single strings of the message and stack trace of each nested exception in an exception stack.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">The Exception stack.</param>
|
||||||
|
/// <returns>Returns a list with a string for each nested exception in an exception stack containing the exception message and stack trace as a single string, separated by " :: ".</returns>
|
||||||
|
public static List<string> AllInnerExceptionMessagesAndStackTraces(this Exception ex)
|
||||||
|
{
|
||||||
|
return GetAllInnerExceptionMessagesAndStackTraces(ex, includeStackTraces: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> GetAllInnerExceptionMessagesAndStackTraces(Exception ex, bool includeStackTraces)
|
||||||
|
{
|
||||||
|
List<string> exceptionMessagesAndStackTraces = new List<string>();
|
||||||
|
|
||||||
|
Exception inner = ex.InnerException;
|
||||||
|
|
||||||
|
exceptionMessagesAndStackTraces.Add(
|
||||||
|
includeStackTraces ? GenerateMessageAndStackTraceString(ex)
|
||||||
|
: ex.Message);
|
||||||
|
|
||||||
|
while (inner != null)
|
||||||
|
{
|
||||||
|
exceptionMessagesAndStackTraces.Add(
|
||||||
|
includeStackTraces ? GenerateMessageAndStackTraceString(inner)
|
||||||
|
: inner.Message);
|
||||||
|
inner = inner.InnerException;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've now reached the end of the exceptions, so can return the error messages
|
||||||
|
return exceptionMessagesAndStackTraces;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateMessageAndStackTraceString(Exception ex)
|
||||||
|
{
|
||||||
|
return $"{ex.Message} :: {ex.StackTrace}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Creek.HelpfulExtensions
|
||||||
|
{
|
||||||
|
public static class SystemTime
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This method exposes DateTime.Now as an instance of a function, that can be replaced in tests.
|
||||||
|
/// </summary>
|
||||||
|
#pragma warning disable S1104 // Fields should not have public accessibility
|
||||||
|
#pragma warning disable S2223 // Non-constant static fields should not be visible
|
||||||
|
public static Func<DateTime> Now = () => DateTime.Now;
|
||||||
|
#pragma warning restore S2223 // Non-constant static fields should not be visible
|
||||||
|
#pragma warning restore S1104 // Fields should not have public accessibility
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,3 +40,130 @@ foreach (var (item, index) in list.WithIndex())
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## DateTime Extensions
|
||||||
|
|
||||||
|
### SystemTime.Now
|
||||||
|
|
||||||
|
This is technically not an extension, but fits well in this package. It's taken from [this blog](https://lostechies.com/jimmybogard/2008/11/09/systemtime-versus-isystemclock-dependencies-revisited/). This method exposes DateTime.Now as an instance of a function, that can be replaced in tests.
|
||||||
|
|
||||||
|
If you want to replace SystemTime.Now in a test you can do it like this:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[Test]
|
||||||
|
public void Should_test_something_here()
|
||||||
|
{
|
||||||
|
var systemTimeDate = new DateTime(2021, 9, 2);
|
||||||
|
SystemTime.Now = () => new DateTime(2021, 9, 2);
|
||||||
|
|
||||||
|
DateTime dateTimeToBeTested = systemTimeDate;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also set SystemTime.Now to any function that returns a DateTime:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var startDate = new DateTime(2021, 9, 2);
|
||||||
|
SystemTime.Now = () => startDate.AddDays(2);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exception Extensions
|
||||||
|
|
||||||
|
### BaseExceptionMessage
|
||||||
|
|
||||||
|
Returns a string of the message of the first/root/base exception in an exception stack.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
string baseExceptionMessage = ex.BaseExceptionMessage();
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### BaseExceptionMessageAndStackTrace
|
||||||
|
|
||||||
|
Returns a single string of the message and stack trace of the first/root/base exception in an exception stack.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
string baseExceptionMessageAndStackTrace = ex.BaseExceptionMessageAndStackTrace();
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AllInnerExceptionMessages
|
||||||
|
|
||||||
|
Returns a list of the exception messages of each nested exception in an exception stack.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
List<string> allInnerExceptionMessages = ex.AllInnerExceptionMessages();
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### AllInnerExceptionMessagesAndStackTraces
|
||||||
|
|
||||||
|
Returns a list of single strings of the message and stack trace of each nested exception in an exception stack.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
List<string> allInnerExceptionMessagesAndStackTraces = ex.AllInnerExceptionMessagesAndStackTraces();
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging Extensions
|
||||||
|
|
||||||
|
### DisposableStopWatchExtension
|
||||||
|
|
||||||
|
This extends `ILogger` with a timer that can wrap code with a using statement. It was heavily inspired by James Curran's [blog post](https://honestillusion.com/2021/12/14/Simple-timings.html). With this extension you can quickly, easily and cleanly stick `StopWatch` objects around code that you wish to time.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using (_logger.DisposableStopWatch())
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It takes in additional parameters for a message and whether or not to also write to a Console for quick and dirty iterating.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using (_logger.DisposableStopWatch("A message", true))
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also make use of it without a logger for very barebones console applications.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using ("A message".DisposableStopWatch())
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You get a `Start:` log, with the message appended if you've passed it, and once your code within the using block has run you get `Complete:` with the messaged appended if you've passed it, then on the same line `Elapsed:` and the time elapsed in HH:MM:SS:MS..... format.
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Creek.HelpfulExtensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace UnitTests
|
||||||
|
{
|
||||||
|
public class ExceptionExtensionTests
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldReturnBaseExceptionMessage()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This function calls another that forces a
|
||||||
|
// division by 0.
|
||||||
|
Rethrow();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
string baseExceptionMessage = ex.BaseExceptionMessage();
|
||||||
|
Assert.AreEqual("Attempted to divide by zero.", baseExceptionMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldReturnBaseExceptionMessageAndStackTrace()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This function calls another that forces a
|
||||||
|
// division by 0.
|
||||||
|
Rethrow();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
string baseExceptionMessageAndStackTrace = ex.BaseExceptionMessageAndStackTrace();
|
||||||
|
StringAssert.StartsWith("Attempted to divide by zero.", baseExceptionMessageAndStackTrace);
|
||||||
|
Assert.AreNotEqual("Attempted to divide by zero.", baseExceptionMessageAndStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldReturnAllInnerExceptionMessages()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This function calls another that forces a
|
||||||
|
// division by 0.
|
||||||
|
Rethrow();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
List<string> allInnerExceptionMessages = ex.AllInnerExceptionMessages();
|
||||||
|
|
||||||
|
List<string> expected = new List<string>()
|
||||||
|
{
|
||||||
|
"Caught the second exception and threw a third in response.",
|
||||||
|
"Forced a division by 0 and threw a second exception.",
|
||||||
|
"Attempted to divide by zero.",
|
||||||
|
};
|
||||||
|
|
||||||
|
CollectionAssert.AreEqual(expected, allInnerExceptionMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldReturnAllInnerExceptionMessagesAndStackTraces()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This function calls another that forces a
|
||||||
|
// division by 0.
|
||||||
|
Rethrow();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
List<string> allInnerExceptionMessagesAndStackTraces = ex.AllInnerExceptionMessagesAndStackTraces();
|
||||||
|
|
||||||
|
List<string> expected = new List<string>()
|
||||||
|
{
|
||||||
|
"Caught the second exception and threw a third in response.",
|
||||||
|
"Forced a division by 0 and threw a second exception.",
|
||||||
|
"Attempted to divide by zero.",
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var (baseExceptionMessageAndStackTrace, index) in allInnerExceptionMessagesAndStackTraces.WithIndex())
|
||||||
|
{
|
||||||
|
StringAssert.StartsWith(expected[index], baseExceptionMessageAndStackTrace);
|
||||||
|
Assert.AreNotEqual(expected[index], baseExceptionMessageAndStackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function catches the exception from the called
|
||||||
|
// function DivideBy0( ) and throws another in response.
|
||||||
|
private void Rethrow()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DivideBy0();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new ThirdLevelException("Caught the second exception and threw a third in response.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function forces a division by 0 and throws a second
|
||||||
|
// exception.
|
||||||
|
private void DivideBy0()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int zero = 0;
|
||||||
|
int ecks = 1 / zero;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new SecondLevelException("Forced a division by 0 and threw a second exception.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define two derived exceptions to demonstrate nested exceptions.
|
||||||
|
private class SecondLevelException : Exception
|
||||||
|
{
|
||||||
|
public SecondLevelException(string message, Exception inner)
|
||||||
|
: base(message, inner)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ThirdLevelException : Exception
|
||||||
|
{
|
||||||
|
public ThirdLevelException(string message, Exception inner)
|
||||||
|
: base(message, inner)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user