mirror of
https://github.com/wagesj45/CapyKit.git
synced 2024-12-21 04:42:29 -06:00
Adding Extensions and Helpers
This commit is contained in:
parent
cbbe897d15
commit
6cdd805be4
7 changed files with 686 additions and 7 deletions
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CapyKit.Attributes;
|
||||
using CapyKit.Extensions;
|
||||
|
||||
namespace CapyKit
|
||||
|
@ -14,7 +15,7 @@ namespace CapyKit
|
|||
/// <remarks>
|
||||
/// Because consumers of CapyKit may have varied ways of handling logging, the <see cref="CapyEventReporter"/> provides
|
||||
/// a way for subscribers to recieve events for various "events" within the library. These can be thought of as
|
||||
/// a logging solution for CapyKit.
|
||||
/// a logging solution for CapyKit. Consumers are free to treat these events however they see fit.
|
||||
/// </remarks>
|
||||
public static class CapyEventReporter
|
||||
{
|
||||
|
@ -25,6 +26,10 @@ namespace CapyKit
|
|||
/// </summary>
|
||||
private static Dictionary<EventLevel, List<(CapyEventHandler Handler, string origin)>> subscribers = new Dictionary<EventLevel, List<(CapyEventHandler Handler, string origin)>>();
|
||||
|
||||
/// <summary> A hash set storing unique identifiers for events intended to only be emitted once. </summary>
|
||||
/// <seealso cref="EmitEventOnce(EventLevel, string, string, string, object[])"/>
|
||||
private static HashSet<string> uniqueIdentifiers = new HashSet<string>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
@ -73,14 +78,17 @@ namespace CapyKit
|
|||
/// <summary> Emits an event with the given severity level, message, and method name. </summary>
|
||||
/// <remarks>
|
||||
/// In order to allow for efficient calling member access via <see cref="CallerMemberNameAttribute"/>
|
||||
/// , it is suggested that <paramref name="args"/> is defined explicitly for formatted messages.
|
||||
/// ,
|
||||
/// it is suggested that <paramref name="args"/> is defined explicitly for formatted messages.
|
||||
/// </remarks>
|
||||
/// <param name="eventLevel"> The severity level of the event. </param>
|
||||
/// <param name="message"> The message describing the reason for the event. </param>
|
||||
/// <param name="method">
|
||||
/// (Optional) The name of the method where the event was raised. String formatting for <paramref name="args"/>
|
||||
/// <param name="message">
|
||||
/// The message describing the reason for the event. String formatting for <paramref name="args"/>
|
||||
/// is accepted.
|
||||
/// </param>
|
||||
/// <param name="method">
|
||||
/// (Optional) The name of the method where the event was raised.
|
||||
/// </param>
|
||||
/// <param name="args">
|
||||
/// A variable-length parameters list containing arguments for formatting the message.
|
||||
/// </param>
|
||||
|
@ -88,6 +96,7 @@ namespace CapyKit
|
|||
/// CapyEventReporter.EmitEvent(EventLevel.Error, "Could not find the description for {0}.",
|
||||
/// args: new[] { enumeration });
|
||||
/// </example>
|
||||
/// <seealso cref="CallerMemberNameAttribute"/>
|
||||
internal static void EmitEvent(EventLevel eventLevel, string message, [CallerMemberName] string method = null, params object[] args)
|
||||
{
|
||||
if (!subscribers.ContainsKey(eventLevel))
|
||||
|
@ -105,6 +114,40 @@ namespace CapyKit
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits an event with the given severity level, message, unique identifier, and method name one
|
||||
/// time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is similar to <see cref="EmitEvent(EventLevel, string, string, string, object[])"/>
|
||||
/// , but requires a unique identifier (such as a <see cref="Guid"/>) to prevent duplicate
|
||||
/// emissions.
|
||||
/// </remarks>
|
||||
/// <param name="eventLevel"> The severity level of the event. </param>
|
||||
/// <param name="message">
|
||||
/// The message describing the reason for the event. String formatting for <paramref name="args"/>
|
||||
/// is accepted.
|
||||
/// </param>
|
||||
/// <param name="uniqueIdentifier"> A unique identifier for the event emission. </param>
|
||||
/// <param name="method">
|
||||
/// (Optional) The name of the method where the event was raised.
|
||||
/// </param>
|
||||
/// <param name="args">
|
||||
/// A variable-length parameters list containing arguments for formatting the message.
|
||||
/// </param>
|
||||
/// <seealso cref="CallerMemberNameAttribute"/>
|
||||
/// <seealso cref="Guid"/>
|
||||
internal static void EmitEventOnce(EventLevel eventLevel, string message, string uniqueIdentifier, [CallerMemberName] string method = null, params object[] args)
|
||||
{
|
||||
if(uniqueIdentifiers.Contains(uniqueIdentifier))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uniqueIdentifiers.Add(uniqueIdentifier);
|
||||
EmitEvent(eventLevel, message, method: method, args: args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -162,12 +205,19 @@ namespace CapyKit
|
|||
public enum EventLevel
|
||||
{
|
||||
/// <summary> Represents a critical error that requires immediate attention. </summary>
|
||||
[EnumerationDescription("Represents a critical error that requires immediate attention.")]
|
||||
Critical = 0,
|
||||
/// <summary> Represents an error that prevents the normal execution of the application. </summary>
|
||||
[EnumerationDescription("Represents an error that prevents the normal execution of the application.")]
|
||||
Error = 1,
|
||||
/// <summary> Represents a warning indicating a non-critical issue that should be addressed. </summary>
|
||||
[EnumerationDescription("Represents a warning indicating a non-critical issue that should be addressed.")]
|
||||
Warning = 2,
|
||||
/// <summary> Represents informational messages that provide useful context to the consumer. </summary>
|
||||
Information = 2,
|
||||
[EnumerationDescription("Represents informational messages that provide useful context to the consumer.")]
|
||||
Information = 3,
|
||||
/// <summary> Represents detailed messages that are typically used for debugging purposes. </summary>
|
||||
Debug = 3
|
||||
[EnumerationDescription("Represents detailed messages that are typically used for debugging purposes.")]
|
||||
Debug = 4
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace CapyKit.Extensions
|
||||
{
|
||||
/// <summary> Provides static extentions methods for providing additional functionality for <see cref="Enum"/> types. </summary>
|
||||
public static class EnumerationExtensions
|
||||
{
|
||||
#region Methods
|
||||
|
|
260
CapyKit/Extensions/LINQExtentions.cs
Normal file
260
CapyKit/Extensions/LINQExtentions.cs
Normal file
|
@ -0,0 +1,260 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CapyKit.Extensions
|
||||
{
|
||||
/// <summary> Provides static extension methods for performing common LINQ operations on <see cref="IEnumerable{T}"/> and <see cref="IQueryable{T}"/> collections. </summary>
|
||||
public static class LINQExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Filters out items matching a <paramref name="predicate"/> from the collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="predicate"> The predicate. </param>
|
||||
/// <returns>
|
||||
/// An enumerator that allows foreach to be used to process remove in this collection.
|
||||
/// </returns>
|
||||
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, Func<T, bool> predicate)
|
||||
{
|
||||
return source.Where(item => !predicate(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters out items matching a <paramref name="predicate"/> from the collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="predicate"> The predicate. </param>
|
||||
/// <returns>
|
||||
/// An enumerator that allows foreach to be used to process remove in this collection.
|
||||
/// </returns>
|
||||
public static IQueryable<T> Filter<T>(this IQueryable<T> source, System.Linq.Expressions.Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
if(predicate.Parameters.Count > 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Warning, "More than one parameter was found in the predicate, which could result in unexpected behavior.");
|
||||
}
|
||||
|
||||
var parameter = predicate.Parameters.FirstOrDefault();
|
||||
var negatedPredicate = Expression.Not(predicate);
|
||||
var lamda = Expression.Lambda<Func<T, bool>>(negatedPredicate, parameter);
|
||||
|
||||
return source.Where(lamda);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a page of items from a collection, skipping <paramref name="pageNumber"/> pages of
|
||||
/// <paramref name="pageSize"/> items per page.
|
||||
/// </summary>
|
||||
/// <remarks> This method uses natural numbering starting at page 1. </remarks>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when <paramref name="pageNumber"/> is less than <c>1</c> or if
|
||||
/// <paramref name="pageSize"/> is less than
|
||||
/// <c>1</c>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="pageNumber"> The page number to retrieve. </param>
|
||||
/// <param name="pageSize"> Number of items per page. </param>
|
||||
/// <returns>
|
||||
/// An enumerator that allows foreach to be used to process page in this collection.
|
||||
/// </returns>
|
||||
public static IEnumerable<T> Page<T>(this IEnumerable<T> source, int pageNumber, int pageSize)
|
||||
{
|
||||
if (pageNumber < 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "pageNumber is out of range. [{0}]", args: new[] { pageNumber });
|
||||
throw new ArgumentOutOfRangeException("pageNumber");
|
||||
}
|
||||
|
||||
if (pageSize < 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "pageSize is out of range. [{0}]", args: new[] { pageSize });
|
||||
throw new ArgumentOutOfRangeException("pageSize");
|
||||
}
|
||||
|
||||
return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a page of items from a collection, skipping <paramref name="pageNumber"/> pages of
|
||||
/// <paramref name="pageSize"/> items per page.
|
||||
/// </summary>
|
||||
/// <remarks> This method uses natural numbering starting at page 1. </remarks>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when <paramref name="pageNumber"/> is less than <c>1</c> or if
|
||||
/// <paramref name="pageSize"/> is less than
|
||||
/// <c>1</c>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="pageNumber"> The page number to retrieve. </param>
|
||||
/// <param name="pageSize"> . </param>
|
||||
/// <returns>
|
||||
/// An enumerator that allows foreach to be used to process page in this collection.
|
||||
/// </returns>
|
||||
public static IQueryable<T> Page<T>(this IQueryable<T> source, int pageNumber, int pageSize)
|
||||
{
|
||||
if (pageNumber < 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "pageNumber is out of range. [{0}]", args: new[] { pageNumber });
|
||||
throw new ArgumentOutOfRangeException("pageNumber");
|
||||
}
|
||||
|
||||
if (pageSize < 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "pageSize is out of range. [{0}]", args: new[] { pageSize });
|
||||
throw new ArgumentOutOfRangeException("pageSize");
|
||||
}
|
||||
|
||||
return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of pages of <paramref name="pageSize"/> size in the given collection.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when <paramref name="pageSize"/> is less than <c>1</c>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="pageSize"> Size of the page. </param>
|
||||
/// <returns> An int. </returns>
|
||||
public static int PageCount<T>(this IEnumerable<T> source, int pageSize)
|
||||
{
|
||||
if (pageSize < 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "pageSize is out of range. [{0}]", args: new[] { pageSize });
|
||||
throw new ArgumentOutOfRangeException("pageSize");
|
||||
}
|
||||
|
||||
var ceiling = Math.Ceiling(Convert.ToDouble(source.Count()) / pageSize);
|
||||
return Convert.ToInt32(ceiling);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of pages of <paramref name="pageSize"/> size in the given collection.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when <paramref name="pageSize"/> is less than <c>1</c>.
|
||||
/// </exception>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="pageSize"> Size of the page. </param>
|
||||
/// <returns> An int. </returns>
|
||||
public static int PageCount<T>(this IQueryable<T> source, int pageSize)
|
||||
{
|
||||
if (pageSize < 1)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "pageSize is out of range. [{0}]", args: new[] { pageSize });
|
||||
throw new ArgumentOutOfRangeException("pageSize");
|
||||
}
|
||||
|
||||
var ceiling = Math.Ceiling(Convert.ToDouble(source.Count()) / pageSize);
|
||||
return Convert.ToInt32(ceiling);
|
||||
}
|
||||
|
||||
/// <summary> An IQueryable<T> extension method that left outer join. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <typeparam name="U"> Generic type parameter. </typeparam>
|
||||
/// <typeparam name="TKey"> Type of the key. </typeparam>
|
||||
/// <typeparam name="R"> Type of the r. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="inner"> The inner. </param>
|
||||
/// <param name="outerSelector"> The outer selector. </param>
|
||||
/// <param name="innerSelector"> The inner selector. </param>
|
||||
/// <param name="resultSelector"> The result selector. </param>
|
||||
/// <param name="defaultGenerator"> (Optional) The default generator. </param>
|
||||
/// <returns> An IQueryable<R> </returns>
|
||||
public static IQueryable<R> LeftOuterJoin<T, U, TKey, R>(this IQueryable<T> source, IQueryable<U> inner, Expression<Func<T, TKey>> outerSelector, Expression<Func<U, TKey>> innerSelector, Func<T, IEnumerable<U>, R> resultSelector, Func<T, U> defaultGenerator = null)
|
||||
{
|
||||
Func<T, IEnumerable<U>, R> resultOrDefaultSelector = (i, o) =>
|
||||
{
|
||||
if (defaultGenerator == null)
|
||||
{
|
||||
defaultGenerator = (t) => default(U);
|
||||
}
|
||||
|
||||
if (!o.Any())
|
||||
{
|
||||
return resultSelector(i, new[] { defaultGenerator(i) });
|
||||
}
|
||||
|
||||
return resultSelector(i, o);
|
||||
};
|
||||
|
||||
return source.LeftOuterJoin(inner, outerSelector, innerSelector, (a, b) => resultSelector(a, b));
|
||||
}
|
||||
|
||||
/// <summary> An IQueryable<T> extension method that left outer join. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <typeparam name="U"> Generic type parameter. </typeparam>
|
||||
/// <typeparam name="TKey"> Type of the key. </typeparam>
|
||||
/// <typeparam name="R"> Type of the r. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="inner"> The inner. </param>
|
||||
/// <param name="outerSelector"> The outer selector. </param>
|
||||
/// <param name="innerSelector"> The inner selector. </param>
|
||||
/// <param name="resultSelector"> The result selector. </param>
|
||||
/// <returns> An IQueryable<R> </returns>
|
||||
private static IQueryable<R> LeftOuterJoin<T, U, TKey, R>(this IQueryable<T> source, IQueryable<U> inner, Expression<Func<T, TKey>> outerSelector, Expression<Func<U, TKey>> innerSelector, Expression<Func<T, IEnumerable<U>, R>> resultSelector)
|
||||
{
|
||||
return source.GroupJoin(inner, outerSelector, innerSelector, resultSelector);
|
||||
}
|
||||
|
||||
/// <summary> An IEnumable<T> extension method that left outer join. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <typeparam name="U"> Generic type parameter. </typeparam>
|
||||
/// <typeparam name="TKey"> Type of the key. </typeparam>
|
||||
/// <typeparam name="R"> Type of the r. </typeparam>
|
||||
/// <param name="source"> The source to act on. </param>
|
||||
/// <param name="inner"> The inner. </param>
|
||||
/// <param name="outerSelector"> The outer selector. </param>
|
||||
/// <param name="innerSelector"> The inner selector. </param>
|
||||
/// <param name="resultSelector"> The result selector. </param>
|
||||
/// <param name="defaultGenerator"> (Optional) The default generator. </param>
|
||||
/// <returns>
|
||||
/// An enumerator that allows foreach to be used to process left outter join in this collection.
|
||||
/// </returns>
|
||||
public static IEnumerable<R> LeftOuterJoin<T, U, TKey, R>(this IEnumerable<T> source, IEnumerable<U> inner, Func<T, TKey> outerSelector, Func<U, TKey> innerSelector, Func<T, IEnumerable<U>, R> resultSelector, Func<T, U> defaultGenerator = null)
|
||||
{
|
||||
var combined = source.GroupJoin(inner, outerSelector, innerSelector, (i, o) => new { inner = i, outer = o });
|
||||
|
||||
if (defaultGenerator == null)
|
||||
{
|
||||
defaultGenerator = (t) => default(U);
|
||||
}
|
||||
|
||||
return combined.Select(anon =>
|
||||
{
|
||||
if (!anon.outer.Any())
|
||||
{
|
||||
return resultSelector(anon.inner, new[] { defaultGenerator(anon.inner) });
|
||||
}
|
||||
|
||||
return resultSelector(anon.inner, anon.outer);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates distinct items in this collection as defined by the key <paramref name="property"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> Generic type parameter of the parent object. </typeparam>
|
||||
/// <typeparam name="U"> Generic type parameter property value. </typeparam>
|
||||
/// <param name="items"> The items to act on. </param>
|
||||
/// <param name="property"> The property. </param>
|
||||
/// <returns>
|
||||
/// An enumerator that allows foreach to be used to process distinct items in this collection.
|
||||
/// </returns>
|
||||
public static IEnumerable<T> Distinct<T, U>(this IEnumerable<T> items, Func<T, U> property)
|
||||
{
|
||||
var propertyComparer = new PropertyComparer<T, U>(property);
|
||||
return items.Distinct(propertyComparer);
|
||||
}
|
||||
}
|
||||
}
|
56
CapyKit/Extensions/StringExtensions.cs
Normal file
56
CapyKit/Extensions/StringExtensions.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CapyKit.Extensions
|
||||
{
|
||||
/// <summary> Provides static extentions methods for providing additional functionality for <see cref="string"/> types. </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
#region Members
|
||||
|
||||
//
|
||||
|
||||
#endregion Members
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary> Replaces a null or empty string with a specified replacement string. </summary>
|
||||
/// <param name="value"> The original string. </param>
|
||||
/// <param name="replacement"> The replacement string. </param>
|
||||
/// <returns>
|
||||
/// The original string if not null or empty, otherwise the replacement string.
|
||||
/// </returns>
|
||||
/// <seealso cref="string.IsNullOrEmpty(string?)"/>
|
||||
public static string IfNullOrEmpty(this string value, string replacement)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return replacement;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary> Replaces a null or whitespace string with a specified replacement string. </summary>
|
||||
/// <param name="value"> The original string. </param>
|
||||
/// <param name="replacement"> The replacement string. </param>
|
||||
/// <returns>
|
||||
/// The original string if not null or whitespace, otherwise the replacement string.
|
||||
/// </returns>
|
||||
/// <seealso cref="string.IsNullOrWhiteSpace(string?)"/>
|
||||
public static string IfNullOrWhiteSpace(this string value, string replacement)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return replacement;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
106
CapyKit/Helpers/CompressionHelper.cs
Normal file
106
CapyKit/Helpers/CompressionHelper.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CapyKit.Helpers
|
||||
{
|
||||
/// <summary> A class that contains methods for managing data compression. </summary>
|
||||
public static class CompressionHelper
|
||||
{
|
||||
#region Members
|
||||
|
||||
//
|
||||
|
||||
#endregion Members
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary> Compresses a given object using the <c>gzip</c> algorithm. </summary>
|
||||
/// <param name="obj"> The object. </param>
|
||||
/// <returns> A byte array representing the compressed object in <c>gzip</c> format. </returns>
|
||||
public static byte[] Compress(object obj)
|
||||
{
|
||||
var bytes = SerializationHelper.SerializeToBytes(obj);
|
||||
|
||||
try
|
||||
{
|
||||
using (var inputStream = new MemoryStream(bytes))
|
||||
using (var outputStream = new MemoryStream())
|
||||
{
|
||||
using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress))
|
||||
{
|
||||
inputStream.Position = 0;
|
||||
inputStream.CopyTo(gzipStream);
|
||||
gzipStream.Flush();
|
||||
}
|
||||
return outputStream.ToArray();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "Could not compress the object.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Compresses a given object to a string using <c>base64</c> encoding of <c>gzip</c> format. </summary>
|
||||
/// <param name="obj"> The object. </param>
|
||||
/// <returns> A string in <c>base64</c> encoding. </returns>
|
||||
public static string CompressToString(object obj)
|
||||
{
|
||||
var bytes = Compress(obj);
|
||||
return Convert.ToBase64String(bytes);
|
||||
}
|
||||
|
||||
/// <summary> Decompresses a given <c>base64</c> encoded string of <c>gzip</c> format. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="encodedString"> The <c>base64</c> encoded <c>gzip</c> string. </param>
|
||||
/// <returns> A <typeparamref name="T"/> typed object. </returns>
|
||||
public static T Decompress<T>(string encodedString)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(encodedString);
|
||||
return Decompress<T>(bytes);
|
||||
}
|
||||
|
||||
/// <summary> Decompresses a given compressed <c>gzip</c> byte stream. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="byteStream"> The compressed byte stream. </param>
|
||||
/// <returns> A <typeparamref name="T"/> typed object. </returns>
|
||||
public static T Decompress<T>(byte[] byteStream)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var inputStream = new MemoryStream(byteStream))
|
||||
using (var outputStream = new MemoryStream())
|
||||
{
|
||||
using (var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress))
|
||||
{
|
||||
gzipStream.CopyTo(outputStream);
|
||||
}
|
||||
var bytes = outputStream.ToArray();
|
||||
return SerializationHelper.Deserialize<T>(bytes);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "Could not decompress the deflated object.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Decompresses the given <c>base64</c> string in <c>gzip</c> format. </summary>
|
||||
/// <param name="compressed"> The compressed string. </param>
|
||||
/// <returns> A decomressed string. </returns>
|
||||
public static string DecompressToString(string compressed)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(compressed);
|
||||
|
||||
return Decompress<string>(bytes);
|
||||
}
|
||||
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
105
CapyKit/Helpers/SerializationHelper.cs
Normal file
105
CapyKit/Helpers/SerializationHelper.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CapyKit.Helpers
|
||||
{
|
||||
public static class SerializationHelper
|
||||
{
|
||||
#region Members
|
||||
|
||||
//
|
||||
|
||||
#endregion Members
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary> Serializes an object to a byte array. </summary>
|
||||
/// <param name="obj"> The object. </param>
|
||||
/// <returns> A <c>JSON</c> encoded string. </returns>
|
||||
public static byte[] SerializeToBytes(object obj)
|
||||
{
|
||||
return JsonSerializer.SerializeToUtf8Bytes(obj);
|
||||
}
|
||||
|
||||
/// <summary> Serializes an object to a <c>JSON</c> encoded string. </summary>
|
||||
/// <param name="obj"> The object. </param>
|
||||
/// <returns> A <c>JSON</c> encoded string. </returns>
|
||||
public static string SerializeToString(object obj)
|
||||
{
|
||||
return JsonSerializer.Serialize(obj);
|
||||
}
|
||||
|
||||
/// <summary> Deserializes an object to a given <typeparamref name="T"/> type. </summary>
|
||||
/// <exception cref="FormatException"> Thrown when the format of the byte array is incorrect. </exception>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="bytes"> The byte array representing a <typeparamref name="T"/> object. </param>
|
||||
/// <returns> A <typeparamref name="T"/> object. </returns>
|
||||
public static T Deserialize<T>(byte[] bytes)
|
||||
{
|
||||
var stream = new MemoryStream(bytes);
|
||||
|
||||
return Deserialize<T>(stream);
|
||||
}
|
||||
|
||||
/// <summary> Deserializes an object to a given <typeparamref name="T"/> type. </summary>
|
||||
/// <exception cref="FormatException">
|
||||
/// Thrown when the format of an input is incorrect.
|
||||
/// </exception>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="stream"> The steam. </param>
|
||||
/// <returns> A <typeparamref name="T"/> object. </returns>
|
||||
public static T Deserialize<T>(Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
var obj = JsonSerializer.Deserialize<T>(stream);
|
||||
|
||||
if(obj == null)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "The deserialized object was null.");
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
return (T)obj;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "JSON formatting error detected during deserialization of byte array for {0}.", args: new[] { typeof(T).Name });
|
||||
throw new FormatException(string.Format("JSON formatting error detected during deserialization of byte array for {0}.", typeof(T).Name), ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "Could not deserialize object of type {0}.", args: new[] { typeof(T).Name });
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Deserializes a <c>JSON</c> encoded string to the given <typeparamref name="T"/>. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter. </typeparam>
|
||||
/// <param name="str"> The <c>JSON</c> encoded string representing a <typeparamref name="T"/> object. </param>
|
||||
/// <returns> A <typeparamref name="T"/> object. </returns>
|
||||
public static T Deserialize<T>(string str)
|
||||
{
|
||||
if (typeof(T) == typeof(string))
|
||||
{
|
||||
return (T)Convert.ChangeType(str, typeof(T));
|
||||
}
|
||||
else if(string.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
CapyEventReporter.EmitEvent(EventLevel.Error, "Could not deserialize an empty string.");
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return JsonSerializer.Deserialize<T>(str);
|
||||
}
|
||||
|
||||
#endregion Methods
|
||||
}
|
||||
}
|
101
CapyKit/PropertyComparer.cs
Normal file
101
CapyKit/PropertyComparer.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CapyKit
|
||||
{
|
||||
/// <summary>
|
||||
/// A object comparer that can accept a lambda expression to compare properties.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"> Generic type parameter of the parent object. </typeparam>
|
||||
/// <typeparam name="U"> Generic type parameter of the property value. </typeparam>
|
||||
/// <example>
|
||||
/// using System;
|
||||
/// using System.Collections.Generic;
|
||||
/// using System.Linq;
|
||||
///
|
||||
/// class Program
|
||||
/// {
|
||||
/// static void Main(string[] args)
|
||||
/// {
|
||||
/// var people = new List<Person>
|
||||
/// {
|
||||
/// new Person { Name = "Alice", Age = 30 }, new Person { Name = "Bob", Age = 30 }, new
|
||||
/// Person { Name = "Charlie", Age = 35 }
|
||||
/// };
|
||||
///
|
||||
/// var comparer = new PropertyComparer<Person, int>(p => p.Age);
|
||||
/// var distinctPeople = people.Distinct(comparer).ToList();
|
||||
///
|
||||
/// foreach (var person in distinctPeople)
|
||||
/// {
|
||||
/// Console.WriteLine($"{person.Name} - {person.Age}");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// class Person
|
||||
/// {
|
||||
/// public string Name { get; set; }
|
||||
/// public int Age { get; set; }
|
||||
/// }
|
||||
/// </example>
|
||||
public class PropertyComparer<T, U> : IEqualityComparer<T>
|
||||
{
|
||||
/// <summary> The expression to retrieve the property. </summary>
|
||||
private Func<T, U> expression;
|
||||
|
||||
/// <summary> Constructor. </summary>
|
||||
/// <param name="expression"> The expression. </param>
|
||||
public PropertyComparer(Func<T, U> expression)
|
||||
{
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/// <summary> Determines whether the specified properties are equal. </summary>
|
||||
/// <param name="x"> The first object of type <typeparamref name="T"/> to compare. </param>
|
||||
/// <param name="y"> The second object of type <typeparamref name="T" /> to compare. </param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the specified objects are equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(T x, T y)
|
||||
{
|
||||
var left = expression.Invoke(x);
|
||||
var right = expression.Invoke(y);
|
||||
|
||||
if (left == null && right == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (left == null ^ right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Returns a hash code for the specified object. </summary>
|
||||
/// <param name="obj">
|
||||
/// The <see cref="T:System.Object" /> for which a hash code is to be returned.
|
||||
/// </param>
|
||||
/// <returns> A hash code for the specified object. </returns>
|
||||
///
|
||||
/// ### <exception cref="T:System.ArgumentNullException">
|
||||
/// The type of <paramref name="obj" /> is a reference type and
|
||||
/// <paramref name="obj" /> is
|
||||
/// <see langword="null" />.
|
||||
/// </exception>
|
||||
public int GetHashCode(T obj)
|
||||
{
|
||||
var property = expression(obj);
|
||||
|
||||
return (property == null) ? 0 : property.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue