GithubHelp home page GithubHelp logo

skybrud / skybrud.essentials Goto Github PK

View Code? Open in Web Editor NEW
14.0 5.0 5.0 74.41 MB

Library with common purpose functionality for working with .NET.

Home Page: https://packages.skybrud.dk/skybrud.essentials/

License: MIT License

C# 99.99% Batchfile 0.01%
skybrud skybrud-essentials essentials json time xml security enums csharp dotnet

skybrud.essentials's Introduction

Skybrud.Essentials

GitHub license NuGet NuGet Skybrud.Essentials at packages.limbo.works

Skybrud.Essentials is a library with common purpose functionality for working with .NET.

The package has a strong focus on parsing various types of input. Among other things, the package can help you work with date and time beyond the standard DateTime and DateTimeOffset classes in .NET. It has better support for various date formats - such as unix time, ISO 8601, RFC 822 and RFC 2822.

It also contains utilities for converting to and from string values, changing text casing and working with both JSON and XML.



Installation

Install the package via NuGet. To install the package, you can use either .NET CLI:

dotnet add package Skybrud.Essentials

or the NuGet Package Manager:

Install-Package Skybrud.Essentials



See also

  • Skybrud.Essentials.Http
    A .NET library for making HTTP requests - including working with OAuth 1.0a and OAuth 2. This package us used in most of our integration packages.

  • Skybrud.Essentials.Maps
    A .NET package for working with maps and geospatial data, including popular formats such as GeoJSON, KML (Keyhole Markup Language) and WKT (Well Known Text).

  • Skybrud.Essentials.AspNetCore
    A .NET package for that provides various logic that makes it easier to work with different parts of ASP.NET Core - eg. parsing query strings and reading request headers.

  • Skybrud.Essentials.Umbraco
    A .NET package for that provides various logic that makes it easier to work with different parts of Umbraco.



Found a bug? Have a question?



Changelog

The releases page lists the relevant changes from each release.



Documentation

Go to the documentation at packages.skybrud.dk

skybrud.essentials's People

Contributors

abjerner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

skybrud.essentials's Issues

Introduce new `GuidUtils` class

Sometimes it may be useful to convert an integer to a GUID, but there isn't exactly a standard way of doing this.

The last bit a GUID has plenty of room to represent an integer, so the following integers would lead to the following GUIDs:

  • 1 -> 00000000-0000-0000-0000-000000000001
  • 12 -> 00000000-0000-0000-0000-000000000012
  • 123 -> 00000000-0000-0000-0000-000000000123
  • 1234 -> 00000000-0000-0000-0000-000000001234
  • 12345 -> 00000000-0000-0000-0000-000000012345
  • 123456 -> 00000000-0000-0000-0000-000000123456

An alternate method is using the BitConverter class, which fills out the GUID from the left as hexadecimal values:

  • 1 -> 00000001-0000-0000-0000-000000000000
  • 12 -> 0000000c-0000-0000-0000-000000000000
  • 123 -> 0000007b-0000-0000-0000-000000000000
  • 1234 -> 000004d2-0000-0000-0000-000000000000
  • 12345 -> 00003039-0000-0000-0000-000000000000
  • 123456 -> 0001e240-0000-0000-0000-000000000000

Not sure which one is the most correct 🤷‍♂️

Here is a quick WIP of a GuidUtils class:

using System;
using System.Linq;

// ReSharper disable CheckNamespace

namespace Skybrud.Essentials.Guids {
    
    public class GuidUtils {

        public static Guid FromInt32(int value) {
            if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
            return new Guid($"00000000-0000-0000-0000-{value:000000000000}");
        }

        public static Guid[] FromInt32Array(int[] value) {
            return value?.Select(FromInt32).ToArray();
        }

        public static Guid ToGuid(long value) {
            byte[] bytes = new byte[16];
            BitConverter.GetBytes(value).CopyTo(bytes, 0);
            return new Guid(bytes);
        }
        
        public static int ToInt32(Guid value) {
            return BitConverter.ToInt32(value.ToByteArray(), 0);
        }
        
        public static long ToInt64(Guid value) {
            return BitConverter.ToInt64(value.ToByteArray(), 0);
        }

    }

}

`EssentialsTime.Add...` methods lose time zone information

The various Add... methods in the EssentialsTime class all perform an operation on the underlying DateTimeOffset, and then returns a new EssentialsTime instance wrapping the resulting DateTimeOffset.

The value of the TimeZone property of the input EssentialsTime should be carried over to the output EssentialsTime. This doesn't happen now 🐛

This can be considered fixed when this test class succeeds:

https://github.com/skybrud/Skybrud.Essentials/blob/878b381324d069e9a56d8aa7696e64ac52dad4d4/src/UnitTestProject1/Time/EssentialsTimeTests.cs

Added CurrentUnixTimeSeconds and CurrentUnixTimeMilliseconds properties to EssentialsTime

Similar as we have:

https://github.com/skybrud/Skybrud.Essentials/blob/v1.1.29/src/Skybrud.Essentials/Time/UnixTime/UnixTimeUtils.cs#L13

https://github.com/skybrud/Skybrud.Essentials/blob/v1.1.29/src/Skybrud.Essentials/Time/UnixTime/UnixTimeUtils.cs#L18

This will basically do exactly the same as the two current properties, but it may sometimes be more favorable to access via the EssentialsTime class instead.

Add more method overloads to the `RegexUtils` class

If the regex pattern is specified at compile time, the type of each group in a match should also be known at compile time. To improve the developer experience a bit, we could introduced some additional IsMatch method overloads to the RegexUtils class - eg.:

if (RegexUtils.IsMatch("http://omgbacon.dk/", "^([a-z]+)://([a-z0-9\\._-]+)/", out string scheme, out string host)) {
    Console.WriteLine($"Scheme: {scheme}");
    Console.WriteLine($"Host: {host}");
}

or:

if (RegexUtils.IsMatch("2022-04-30", "^([0-9]{4})-([0-9]{2})-([0-9]{2})$", out int year, out int month, out int day)) {
    Console.WriteLine($"Year: {year}");
    Console.WriteLine($"Month: {month}");
    Console.WriteLine($"Day: {day}");
}

Lots of functionality is missing for .NET 7 in the 1.1.53 release

For the v1.1.53, I added .NET 7 as an additional target framework, but I forgot to update the target framework check that enables some of the compile constants used by the package. As a result of this, all the functionality enabled by these constants are not part of the .NET 7 DLL in the 1.1.53 release.

Add useful IEnumerable<T> extension methods

  • DistinctBy
    Umbraco versions prior to v10 have had a similar extension method, and now there is a DistinctBy extension method in .NET 6 (but not in .NET Standard). It would be useful to have this extension method when not working with either Umbraco or .NET. And per .NET being licensed under MIT, it should be fine to borrow the code.

  • InGroupsOf
    Umbraco extension method for splitting a collection into groups (Umbraco is licensed under MIT).

  • WhereNotNull
    Umbraco extension method for filtering out all null values in a collection (Umbraco is licensed under MIT).

Add methods for parsing into nullable types

.NET already contains TryParse methods for various types - eg. int, long etc., but no methods for converting into int?, long? etc.

As having these methods could help making the code that uses them simpler, it would be a good idea to have them in Skybrud.Essentials.

Usage could be something like:

if (StringUtils.TryParseInt32("123", out int? result)) {

    // Do something with the result

}

Ideally Skybrud.Essentials should have the following methods (some already exist):

Utils

  • StringUtils.TryParseBoolean(string, out bool)
  • StringUtils.TryParseBoolean(string, out bool?)
  • StringUtils.TryParseInt32(string, out int)
  • StringUtils.TryParseInt32(string, out int?)
  • StringUtils.TryParseInt64(string, out long)
  • StringUtils.TryParseInt64(string, out long?)
  • StringUtils.TryParseFloat(string, out float)
  • StringUtils.TryParseFloat(string, out float?)
  • StringUtils.TryParseDouble(string, out double)
  • StringUtils.TryParseDouble(string, out double?)
  • StringUtils.TryParseGuid(string, out Guid)
  • StringUtils.TryParseGuid(string, out Guid?)

Extension methods

  • StringExtensions.IsBoolean(string)
  • StringExtensions.IsBoolean(string, out bool)
  • StringExtensions.IsBoolean(string, out bool?)
  • StringExtensions.IsInt32(string)
  • StringExtensions.IsInt32(string, out int)
  • StringExtensions.IsInt32(string, out int?)
  • StringExtensions.IsInt64(string)
  • StringExtensions.IsInt64(string, out long)
  • StringExtensions.IsInt64(string, out long?)
  • StringExtensions.IsFloat(string)
  • StringExtensions.IsFloat(string, out float)
  • StringExtensions.IsFloat(string, out float?)
  • StringExtensions.IsDouble(string)
  • StringExtensions.IsDouble(string, out double)
  • StringExtensions.IsDouble(string, out double?)
  • StringExtensions.IsGuid(string)
  • StringExtensions.IsGuid(string, out Guid)
  • StringExtensions.IsGuid(string, out Guid?)

`EssentialsTime.ToString` and `Iso8601Utils.ToString` methods lack precision as milliseconds are omitted

Currently the Iso8601Utils.ToString uses the Iso8601Constants.DateTimeSeconds (yyyy-MM-ddTHH:mm:ssK), which doesn't include the milliseconds of the timestamp:

The Iso8601Utils.ToString method should instead use Iso8601Constants.DateTimeMilliseconds so the milliseconds are preserved when converting to a string.

🅱️ Changing this could potentially be a breaking change a breaking change if someone does checks against Iso8601Constants.DateTimeSeconds, but not other valid ISO 8601 formats 😱


If the timestamp is in UTC, either format will prepend +00:00 to the output string. For UTC timestamps specifically, it would be a plus to replace the offset with Z (Zulu time) instead, thus resulting in a slightly shorter/simpler output string.


EssentialsTime.ToString calls Iso8601Utils.ToString, so fixing Iso8601Utils.ToString will fix them both 😎

Add ToStringJsonConverter

Implemented this class in another project, but with the intent to add it in this package ... but then forgot about it:

using System;
using System.Globalization;
using Newtonsoft.Json;

// ReSharper disable CheckNamespace

namespace Skybrud.Essentials.Json.Converters {

    public class ToStringJsonConverter : JsonConverter {

        public override bool CanRead => false;

        public override bool CanConvert(Type objectType) {
            return true;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
            if (value == null) {
                writer.WriteNull();
            } else {
                writer.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0}", value));
            }
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
            throw new NotImplementedException();
        }

    }

}

Add extension methods for sorting collections

Something like:

namespace Skybrud.Essentials.Collections {

    public enum SortOrder {
        Ascending,
        Descending
    }

}
using System;
using System.Collections.Generic;
using System.Linq;

namespace Skybrud.Essentials.Collections.Extensions {
    
    public static class EnumerableExtensions {

        public static IOrderedEnumerable<T> OrderBy<T, TKey>(this IEnumerable<T> collection, Func<T, TKey> func, bool reverse) {
            return reverse ? collection.OrderByDescending(func) : collection.OrderBy(func);
        }

        public static IOrderedEnumerable<T> OrderBy<T, TKey>(this IEnumerable<T> collection, Func<T, TKey> func, SortOrder order) {
            return order == SortOrder.Descending ? collection.OrderByDescending(func) : collection.OrderBy(func);
        }

    }

}

Implement new extension methods for JSON.net taking a `propertyName` parameter instead of a `path` parameter

The existing extension methods in the JObjectExtensions class take a path parameter, which is then evaluated as a JPath value. This causes a bit of overhead if you only really need to get the value of a property on a given JObject instance.

Changing the existing methods will be a breaking change, so ideally we should create a new extension class with new extension methods based on a propertyName rather than a path.

New extension methods could also be implemented using a ByPath suffix - eg. GetStringByPath.

`XmlUtils.ToString(XDocument)` method should use the encoding of the document's XML declaration if present

XmlUtils.ToString(XDocument) internally uses a StringWriter to save the document to. But StringWriter uses a UTF-16 encoding, so when the XML declaration specifies UTF-8, the generated XML declaration says UTF-16.

I'm marking this as a type/bug as the generated string should return the correct encoding.


The StringWriter.Encoding property doesn't have a setter, but it's virtual, so we can create our our custom class and override the property - eg. something like:

public class StringWriterWithEncoding : StringWriter
    {
        public StringWriterWithEncoding(StringBuilder sb, Encoding encoding) : base (sb)
        {
			Encoding = encoding;
        }
        public override Encoding Encoding { get; }
    }

When using the XDocument.Save(string,SaveOptions) method, .NET will try to detect the encoding from the document's XML declration:

https://source.dot.net/#System.Private.Xml.Linq/System/Xml/Linq/XDocument.cs,78b935e9fc4e9a4a,references

Ideally Skybrud.Essentials should then do something similar, and then set the encoding of our custom StringWriterWithEncoding based on this.

Annoations of the `StringUtils.FirstWithValue` methods don't support individual null values

See:

/// <summary>
/// Returns the first item in <paramref name="values"/> that has a value (validated using
/// <see cref="string.IsNullOrWhiteSpace"/>), or <see cref="string.Empty"/> if no value is found.
/// </summary>
/// <param name="values">Array of string values.</param>
/// <returns>The first value or <see cref="string.Empty"/>.</returns>
public static string FirstWithValue(params string[]? values) {
return values?.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)) ?? string.Empty;
}
/// <summary>
/// Returns the first item in <paramref name="values"/> that has a value (validated using
/// <see cref="string.IsNullOrWhiteSpace"/>), or <see cref="string.Empty"/> if no value is found.
/// </summary>
/// <param name="values">Collection of string values.</param>
/// <returns>The first value or <see cref="string.Empty"/>.</returns>
public static string FirstWithValue(IEnumerable<string>? values) {
return values?.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)) ?? string.Empty;
}

The two methods currently only support string[]? and IEnumerable<string?> respectively. This allows the overall collections to be null, but not the individual string values. Since these may also be null, the annotations should reflect this.

error after upgrading from 1.1.18 -> 1.1.20

After upgrade i get an error when trying to do this:

CreateDate = new EssentialsTime(dto.CreateDate, TimeZoneInfo.Local);

dto.CreateData is a valid date. Below i have the stacktrace

Stack trace ved Skybrud.Umbraco.Spa.Api.SpaControllerBase.GetResponse(SpaRequest request) ved VejleKommune.Controllers.Api.Spa.SpaController.GetData() i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Controllers\Api\Spa\SpaController.cs:linje 44 Exception Type System.ArgumentException Message UTC-forskydningen for DateTime-forekomster skal være 0. Parameternavn: offset Stack trace ved System.DateTimeOffset..ctor(DateTime dateTime, TimeSpan offset) ved Skybrud.Essentials.Time.EssentialsTime..ctor(DateTime dateTime, TimeZoneInfo timeZone) ved VejleKommune.Models.Intranet.Employees.Messages.EmployeeMessage..ctor(EmployeeMessageDto dto) i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Employees\Messages\EmployeeMessage.cs:linje 116 ved VejleKommune.Models.Intranet.Employees.Messages.EmployeeMessagesService.<>c.b__7_0(EmployeeMessageDto x) i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Employees\Messages\EmployeeMessagesService.cs:linje 111 ved System.Linq.Enumerable.WhereSelectListIterator2.MoveNext()
ved System.Linq.Buffer1..ctor(IEnumerable1 source)
ved System.Linq.Enumerable.ToArray[TSource](IEnumerable1 source) ved VejleKommune.Models.Intranet.Employees.Messages.EmployeeMessagesService.GetAllMessages() i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Employees\Messages\EmployeeMessagesService.cs:linje 110 ved VejleKommune.Models.Intranet.Employees.Messages.EmployeeMessagesCache.b__8_0() i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Employees\Messages\EmployeeMessagesCache.cs:linje 96 ved Umbraco.Core.Cache.FastDictionaryAppCacheBase.<>c__DisplayClass21_0.b__0() i D:\a\1\s\src\Umbraco.Core\Cache\FastDictionaryAppCacheBase.cs:linje 285 --- Afslutningen på staksporingen fra den tidligere placering, hvor undtagelsen blev udløst --- ved System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ved Umbraco.Core.Cache.WebCachingAppCache.Get(String key, Func1 factory, Nullable1 timeout, Boolean isSliding, CacheItemPriority priority, CacheItemRemovedCallback removedCallback, CacheDependency dependency) i D:\a\1\s\src\Umbraco.Core\Cache\WebCachingAppCache.cs:linje 180 ved Umbraco.Core.Cache.WebCachingAppCache.Get(String key, Func1 factory, Nullable1 timeout, Boolean isSliding, CacheItemPriority priority, CacheItemRemovedCallback removedCallback, String[] dependentFiles) i D:\a\1\s\src\Umbraco.Core\Cache\WebCachingAppCache.cs:linje 45 ved Umbraco.Core.Cache.DeepCloneAppCache.Get(String key, Func1 factory, Nullable1 timeout, Boolean isSliding, CacheItemPriority priority, CacheItemRemovedCallback removedCallback, String[] dependentFiles) ved VejleKommune.Models.Intranet.Employees.Messages.EmployeeMessagesCache.GetMessagesFromCache() i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Employees\Messages\EmployeeMessagesCache.cs:linje 95 ved VejleKommune.Models.Intranet.Employees.Messages.EmployeeMessagesCache.GetMessages(MemberProfileItem member) i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Employees\Messages\EmployeeMessagesCache.cs:linje 48 ved VejleKommune.Models.Intranet.Pages.VkFrontPage..ctor(VkSiteModel site, IPublishedContent content) i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Models\Intranet\Pages\VkFrontPage.cs:linje 81 ved VejleKommune.Controllers.Api.Spa.SpaController.InitContentModel(SpaRequest request, IPublishedContent content) i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Controllers\Api\Spa\SpaController.cs:linje 162 ved VejleKommune.Controllers.Api.Spa.SpaController.InitContentModel(SpaRequest request) i C:\Work\Projects\7179 Vejle Kommune - Intranet - Umbraco 8\dev\code\Controllers\Api\Spa\SpaController.cs:linje 148 ved Skybrud.Umbraco.Spa.Api.SpaControllerBase.GetResponse(SpaRequest request)

Implement `ArrayUtils.Empty<T>` utility method

Newer versions of .NET features an Array.Empty<T> method for returning an empty array without unnecessary memory allocations. But as Skybrud.Essentials also targets older .NET versions, it might be relevant to add similar functionality to Skybrud.Essentials.

// ReSharper disable UseArrayEmptyMethod

namespace Skybrud.Essentials.Arrays {
    
    internal static class ArrayUtils {
        
        private static class EmptyArray<T> {
            internal static readonly T[] Value = new T[0];
        }

        public static T[] Empty<T>() {
            return EmptyArray<T>.Value;
        }

    }

}

Add `Parse{Type}List` and `To{Type}List` methods

The StringUtils class already contains static utility methods like ParseInt32Array, ParseInt64Array and so forth, but the internal conversion to an array involves an extra iteration, and an array might not always be the desired output type.

So it would be ideal to have similar ParseInt32List and ParseInt64List methods where the return types are List<int> and List<long>. The same goes for the other types that already have array versions, and for the To{Type}Array extension methods in the StringExtensions class.

Introduce `RandomOrDefault` extension method

Eg:

/// <summary>
/// Returns a random item from the specified <paramref name="collection"/>.
/// </summary>
/// <typeparam name="T">The type the items.</typeparam>
/// <param name="collection">The collection.</param>
/// <returns>A random item of type <typeparamref name="T"/> from <paramref name="collection"/> if not empty; otherwise, the default value of <typeparamref name="T"/>.</returns>
public static T RandomOrDefault<T>(this IEnumerable<T> collection) {
    return collection.OrderBy(x => Guid.NewGuid()).FirstOrDefault();
}

Trying to use in .NET Core

Hi, I'm trying to use this in .NET Core but I get the following error:

Package Skybrud.Essentials 1.0.9 is not compatible with netcoreapp1.1 (.NETCoreApp,Version=v1.1). Package Skybrud.Essentials 1.0.9 supports: net45 (.NETFramework,Version=v4.5)
One or more packages are incompatible with .NETCoreApp,Version=v1.1.

I wanted to use the pre-release package of Skybrud.Social.Core but I needed the above dependency. Will it be compatible in the future? Sorry for the bother.

Introduce method for formatting file size

Since formatting a file size (byte count) is something we do over and over again, it's a good candiate for adding to Skybrud.Essentials.

It's worth noticing that there are both a decimal format (1000 bytes is 1 KB), and a binary format (1024 bytes is 1 KiB), but also that KB is commonly used instead of KiB to refer to 1024 bytes.

Usage could be something like:

Implicit (binary, but with decimal units - technically incorrect, but commonly used)

// Returns "1 KB"
string formatted = StringUtils.FormatFileSize(1024);

Default (binary, but with decimal units - technically incorrect, but commonly used)

// Returns "1 KB"
string formatted = StringUtils.FormatFileSize(1024, FileSizeFormat.Default);

Binary

// Returns "1 KiB"
string formatted = StringUtils.FormatFileSize(1024, FileSizeFormat.Binary);

or:

// Returns "1 KiB"
string formatted = StringUtils.FormatFileSize(1024, FileSizeFormat.Kibi);

Decimal

// Returns "1 KB"
string formatted = StringUtils.FormatFileSize(1000, FileSizeFormat.Decimal);

or:

// Returns "1 KB"
string formatted = StringUtils.FormatFileSize(1000, FileSizeFormat.Kilo);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.