lib12 – my helper library
After several years of developing various IT projects I’ve collected a set of various helper methods and classes. Sometimes I also have written a more generic solution that could be used across projects, like a fluent SQL query generator to support micro-orms. Most of the times I’ve just copied useful code between projects. After doing it several times I’ve started thinking about wrapping it all in one library and copying reference to NuGet package instead. So I did exactly that – some time ago I’ve created lib12. It was still mostly for personal use and I didn’t advert it much, but I think that after some time it’s mature enough to say little more about it, so it could be useful for someone else besides me. Also with the advent of .NET Standard 2.0 I’ve created a new version, so this post will be about what actual lib12 consist of and how you can create your own .NET Standard 2.0 package.
What is .NET Standard?
After Microsoft created the new version of .NET in the form of .NET Core problem arose – how do you use libraries from .NET Framework in the .NET Core? This problem actually existed even before – different platform had different constraints and creating one totally generic platform wasn’t really possible, that’s why we had a different version of .NET in the form PCL for embedded devices, that’s why we had a slightly different version of .NET for Silverlight.
.NET Standard is the solution to handle such issues, but critical thing to understand about it is that it isn’t another .NET platform it’s more like contract/interface – set of APIs that need to be supported by platform in order to run given library. For example, I need to use API x in my library, so I need to choose the specific version of .NET Standard, and also based on that I see which platforms will support my library.
There are two resources that can help you better understand supported platforms and APIs. First one is the compatibility table where you can see what version of .NET Standard supports your platform, which can be found here. The second resource is this gist containing C# code that I think will speak to every developer and let you better understand what .NET Standard actually is.
Creating an empty package
Starting with your own library targeting .NET Standard isn’t complicated, you just create a correct project type in Visual Studio:
After the project is created open its properties and the Package tab:
First check Generate NuGet package on build which I believe is self-explanatory – *.nupkg package file will be created in [project]/bin/[build type]/ directory. All package properties like package id, author, package version can be set on the same tab.
When you have your package file you can open nuget.org, create an account there and upload your first library. This process is straightforward so I won’t describe it in details here, but at this moment your package is available to the world!
Continuous Integration/Continuous Deployment with Travis CI
After some time manually publishing NuGet packages become tedious so I used CI/CD to make my life little easier. I used popular choice among Github projects – Travis. This service allows me to build, run tests and deploy a new package version to NuGet. After registering (which is pretty straightforward – you sign up using your Github account) and choosing to create new deployment pipeline from one of your Github repositories the most important thing is to create valid deployment script, here is mine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
language: csharp mono: none dotnet: 2.0.0 dist: trusty script: - dotnet build -c Release - dotnet test lib12.Tests - dotnet pack -c Release deploy: skip_cleanup: true provider: script script: dotnet nuget push lib12/bin/Release/lib12.*.nupkg --source https://api.nuget.org/v3/index.json --api-key $NUGET_API_KEY on: branch: master tags: true |
This script is saved in the main directory of the project in the format of YAML file. It is used to provide basic configuration, to describe language and platform which is used to build the project. Under script, there are three tasks that need to be done – building project, running unit tests from lib12.Tests project and packing it to a NuGet package. Under deploy, we have information on how to deploy a package to the NuGet repository. The $NUGET_API_KEY is the NuGet key, that you can generate on a NuGet website and then set in Travis settings as a variable, so you don’t store it in open text in the repository. Last important thing here is the on statement which describes the condition when deploying to the NuGet repository is actually done – in this case, we need it to be on branch master and we need to set the tag in Git. When these conditions aren’t met – on any other branch or during pull request – we just conduct build and run unit tests. Finally, one small feat that Travis provide is an ability to have badge-indicator on the GitHub page with build status information, here is information on how to do that. The second budge I use is a version budge that connects to NuGet and display version of the latest package – I use https://badge.fury.io/ service for that.
What lib12 contains?
Ok, but how exactly lib12 can help you? What functionalities does it provide? Some highlights:
Fluent SQL query
When working with micro-orms I often encountered a problem with writing SQL queries – I needed to write code to join strings, it wasn’t the prettiest solution. So instead I wrote a fluent query generator and with that writing SQL queries is much easier:
1 2 3 4 5 6 7 8 9 |
var select = SqlBuilder.Select.Fields(fields).From("products", "p") .Join("groups", "g", "p.group_id", "g.id") .Join("stores", "s", "g.id", "s.group_id", JoinType.Left) .OpenBracket() .Where("price", Compare.GreaterThan, 100).And.Where("price", Compare.LessOrEquals, 1000) .CloseBracket() .Or.Where("code", Compare.Like, "a%") .GroupBy("product_group").Having("avg(price)>100") .OrderByDesc("price").Build(); |
SqlBuilder class apart from SELECT handles also INSERT (with batch), UPDATE and DELETE
Random data
lib12.Data.Random contains static Rand class that encapsulates .NET Random class providing much more than basic random functions, i.e. functions to generate bools, strings, enums and more complex things like names, addresses, countries. What’s more Rand allows generating even whole classes and collections of them, i.e.:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Account { public string Name { get; set; } public string Surname { get; set; } public string Email { get; set; } public string Address { get; set; } public string Country { get; set; } public string Company { get; set; } public string Info { get; set; } public double Number { get; set; } public DateTime Created { get; set; } } var generated = Rand.NextArrayOf<Account>(10); |
Also with built-in constraints you have control over how exactly data will be generated:
1 2 3 4 |
var constrains = ConstrainFactory.For<Account>() .AddNumericConstrain(x => x.Number, 50, 100) .Build(); var generated = Rand.NextArrayOf<Account>(CollectionSize, constrains); |
Mathematical formulas evaluation
Formula class parses mathematical expressions using Reverse Polish Notation and evaluates them:
1 2 |
var formula = new Formula("-12*3 + (5-3)*6 + 9/(4-1)"); var result = formula.Evaluate(); |
Variables are also supported:
1 2 |
var formula = new Formula("a*(5-b)"); formula.Evaluate(new { a = 10, b = 3 }); |
Set of extensions for collections
Writing code you often work with collections. .NET, in my opinion, has great support for working with them in the form of LINQ, but it doesn’t mean that it couldn’t be better. For example Recover() method is an implementation of null pattern object – checks if a collection is null and returns an empty collection instead, so one if less to write. IsNullOrEmpty() and IsNotNullAndNotEmpty() are pretty straightforward and further simplify conditions in code. MaxBy and MinBy allow to search collections for maximum and minimum by given property and returns object from collection instead of maximum/minimum data. In my opinion, very useful and interesting methods are ExceptBy and IntersectBy, which can simplify the task of getting a subset of two collections based on a non-standard comparison, from something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Item { public int Value { get; set; } } public class AnotherItem { public int AnotherValue { get; set; } } var list1 = new List<Item>(){...} var list2 = new List<AnotherItem>(){...} var result = list1 .Where(x => !list.Any(y => x.Value == y.AnotherValue)) |
to something like this:
1 |
var result = list1.ExceptBy(list2, x => x.Value, x => x.AnotherValue); |
which is much easier to read and understand.
Set of extensions for working with standard classes like string or DateTime
To easy do string comparison you can use EqualsIgnoreCase() and EqualsMatchCase() functions, you can remove diacritics, count number of occurrences of given substring, use Recover(), IsNullOrEmpty(), IsNotNullAndNotEmpty() functions similar to the those I described in collections part. For DateTime, you can convert it to Unix timestamp in both ways and easy check if given DateTime contains default value.
Utility classes
lib12.Utility namespace contains various utility classes like Range to handle working with numerical and date time ranges, Logger to have the ability to quickly log something without heavy configuration or PerformanceCheck to benchmark source code.
These all are just some highlights, more in-depth description can be found in the lib12 readme on GitHub
Summary
I hope now you understand better how NuGet packages are created, why most people choose .NET Standard as a target for their nugets, how to create your own package and why lib12 could be useful for you. If you want to check out my library you just need to enter Install-Package lib12 in Package Manager or use NuGet explorer in Visual Studio. I hope it will be useful to you!
3 thoughts on “lib12 – my helper library”
Finally, some content! I’ve been waiting almost a year!
Thank you for patience, MD! 🙂