⭐ Please star this project if you find it useful!
SWAN stands for Stuff We All Need
Repeating code and reinventing the wheel is generally considered bad practice. At Unosquare we are committed to beautiful code and great software. Swan is a collection of classes and extension methods that we and other good developers have developed and evolved over the years. We found ourselves copying and pasting the same code for every project every time we started it. We decide to kill that cycle once and for all. This is the result of that idea. Our philosophy is that SWAN should have no external dependencies, it should be cross-platform, and it should be useful.
PM> Install-Package Unosquare.Swan
In this section, we present the different components that are available in the SWAN library. Please keep in mind that everything in the library is opt-in. SWAN won't force you to use any of its components, classes or methods.
Runtime
provides properties and methods that provide information about the application environment (including Assemblies and OS) and access to singleton instance of other components inside Swan as ObjectMapper
.
Many times, we find ourselves implementing Console
output code as some NLog or Log4Net logger or adapter, especially
when writing console applications, daemons and windows services. We also tend to write Console
code for reading user
input because it can't be some logger or adapter. And then you have the System.Diagnostics.Debug
class to write
to the debugger output. And finally, all your Console
user interaction looks primitive and unprofessional. In other
words, you end up with 3 things you are unsure of how to put together in the different configurations and runtime environments:
Console
, Debug
and some logging mechanism. In return you have placed unintended logging code, Console
code, and Debug
code everywhere in your application and it makes it look silly, bloated and written by an amateur.
The Swan Terminal
is all of the following:
- Console Standard Output Writer
- Console Standard Error Writer
- Debug Writer
- Console Standard Input Reader
- Log message forwarder
It is also very easy to use, it's thread-safe, and it does not require you to learn anything new. In fact, it simplifies logging
messages and displaying Console
messages by providing string
extension methods.
This only writes messages out to the TerminalWriters
if they are available. In practice, we typically DO NOT use
the Write
and WriteLine
methods but they are provided for convenience, extensibility and customization. Please note
that these methods do not forward messages as logging events and therefore whatever is written via these methods
will not show up in you logging subsystem.
// The simplest way of writing a line of text -- equivalent to `Console.WriteLine`:
Terminal.WriteLine($"Hello, today is {DateTime.Today}");
// A slightly better way using extension methods:
$"Hello, today is {DateTime.Today}".WriteLine();
// Now, let's add some color:
$"Hello, today is {DateTime.Today}".WriteLine(ConsoleColor.Green);
// Write it out to the debugger as well!
$"Hello, today is {DateTime.Today}".WriteLine(ConsoleColor.Green, TerminalWriters.StandardOutput | TerminalWriters.Diagnostics);
// You could have also set the color argument to null and just use the configured default
$"Hello, today is {DateTime.Today}".WriteLine(null, TerminalWriters.StandardOutput | TerminalWriters.Diagnostics);
This is where Terminal
really shines. Instead of using the Write
and WriteLine
methods, you can use the
methods that are intended for logging. These methods have different purposes and distinct functionality. Please
refer to the example below and its comments.
$"Hello, today is {DateTime.Today}".Info();
Suppose you have various calls to Terminal
's logging methods such as Info()
, Warn()
, Error()
, Trace()
and Debug()
. You wish to forward those messages to a logging subsystem in addition to using the Console
's
standard output and standard error, and the built-in diagnostics output. All you have to do is subscribe to the
Terminal's OnLogMessageReceived
event. The event arguments of this event provide useful properties that you
can piece together to send your logging messages directly to the Logging subsystem in your application.
Swan's Terminal
provides both, flexibility and consistency for all of its output. While it will pick the most
common defaults for a given build or runtime scenario, you are able to modify such defaults and adjust them to your
liking. You can change the output colors,
The Swan Terminal
would not be complete without a way to read user input. The good news is
that Terminal
can create decent-looking user prompts if a very convenient way.
// Reads a line of text from the console.
var lineResult = Terminal.ReadLine();
// Reads a number from the input. If unable to parse, it returns the default number, in this case (default 0).
var numberResult = Terminal.ReadNumber("Read Number", 0);
// Creates a table prompt where the user can enter an option based on the options dictionary provided.
var promptResult = Terminal.ReadPrompt("Read Promp", options, "A");
// Reads a key from the terminal preventing the key from being echoed.
var keyResult = Terminal.ReadKey("Read Key");
Swan's Terminal
also provides additional methods to accomplish very specific tasks. Given the fact that Terminal
is an asynchronous, thread-safe output queue, we might under certain situations require all of the output queue to be written
out to the Console
before the program exits. For example, when we write a console application that requires its usage
to be fully printed out before the process is terminated. In these scenarios, we use Terminal.Flush
which blocks
the current thread until the entire output queue becomes empty.
You can serialize and deserialize strings and objects using Swan's Json
Formatter. It's a great way to transform objects to JSON format and vice versa. For example, you need to send information as JSON format to other point of your application and when arrives it's necessary to get back to the object that is going to be used, and thanks to JSON format the data can interchange in a lightweight way.
Serializes the specified object into a JSON string
.
// The object to be serialize
var basicObject = new { One = "One", Two = "Two", Three = "Three" };
// Serializes the specified object into a JSON string.
var data = Json.Serialize(basicObject);
Serializes the specified object only including the specified property names.
// The object to be serialize
var basicObject = new { One = "One", Two = "Two", Three = "Three" };
// The included names
var includedNames = new[] { "Two", "Three" };
// Serialization Only.
var data = Json.SerializeOnly(basicObject, true, includedNames);
Serializes the specified object excluding the specified property names.
// The object to be serialize
var basicObject = new { One = "One", Two = "Two", Three = "Three" };
// The excluded names
var excludeNames = new[] { "Two", "Three" };
// Serialization Excluding
var data = Json.SerializeExcluding(basicObject, true, excludeNames);
Deserializes the specified JSON string
as either a Dictionary<string, object>
or as a List<object>
depending on the syntax of the JSON string
.
// The json to be deserialize
var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
// Deserializes the specified json into Dictionary<string, object>.
var data = Json.Deserialize(basicJson);
Deserializes the specified json string
and converts it to the specified object type. Non-public constructors and property setters are ignored.
// The json Type BasicJson to be deserialize
var basicJson = "{\"One\":\"One\",\"Two\":\"Two\",\"Three\":\"Three\"}";
// Deserializes the specified string in a new instance of the type BasicJson.
var data = Json.Deserialize<BasicJson>(basicJson);
Many projects require the use of CSV files to export the information, with CsvWriter
you can easily write objects and data to CSV format, also gives a useful way to save the information into a file.
This is the way to write a list of objects into a CSV format.
// The list of objects to be written as CSV
var basicObj = new List<BasicJson>();
using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(basicObj.ToString())))
{
// The writer of the CSV
var reader = new CsvWriter(stream);
};
You also can write the object into a file or a temporal file.
// The list of objects to be written as CSV
var basicObj = new List<BasicJson>();
// This is where the object is save into a file
CsvWriter.SaveRecords<BasicJson>(basicObj, "C:/Users/user/Documents/CsvFile");
When you use, and manage the information through CSV files you need to have an easy way to read and load the data into lists and information usable by the application. Swan makes use of CsvReader
to read and load CSV files.
This is a way to read CSV format data.
// The data to be readed
var data = @"Company,OpenPositions,MainTechnology,Revenue
Co,2,""C#, MySQL, JavaScript, HTML5 and CSS3"","500"
Ca,2,""C#, MySQL, JavaScript, HTML5 and CSS3"","600";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(data)))
{
// The reader of the CSV
var reader = new CsvReader(stream, true, Encoding.UTF8);
};
From a CSV file, you can read and load the information to a generic list.
// The list of object to be written as CSV
var basicObj = new List<BasicJson>();
// This is where the object is save into a file
CsvWriter.SaveRecords<BasicJson>(basicObj, "C:/Users/user/Documents/CsvFile");
// This is how you can load the records of the CSV file
var loadedRecords = CsvReader.LoadRecords<BasicJson>("C:/Users/user/Documents/CsvFile");
Represents a wrapper HttpClient
with extended methods to use with JSON payloads and bearer tokens authentication.
You can Authentication into your application.
// The Authenticate
var data = JsonClient.Authenticate("https://mywebsite.com/api/token", "admin", "password");
Easy way to make a HTTP GET using JsonClient
.
// The GET
var data = JsonClient.Get<BasicJson>("https://mywebsite.com/api/data");
Easy way to make a POST using JsonClient
.
// The POST
var data = JsonClient.Post<BasicJson>("https://mywebsite.com/api/data", new { filter = true });
Easy way to make a PUT using JsonClient
.
// The PUT
var data = JsonClient.Put<BasicJson>("https://mywebsite.com/api/data", new { filter = true });
It's a Swan's basic SMTP client that can submit messages to an SMTP server. It's very easy to manage and provide a very handy way to make use of SMTP in your application to send mails to any registered user.
The mails are sent asynchronously.
// Sending mails async
await client.SendMailAsync(new MailMessage());
// Or sent the mail based on the Smtp session state
await client.SendMailAsync(new SmtpSessionState());
It's a very handy component of Swan that maps objects. You can access a default instance of ObjectMapper
by Runtime
class.
The conversion generates a map automatically between the properties in base of the properties names.
// Here is mapping the specific user to a destination
var destination = Runtime.ObjectMapper.Map<UserDto>(user);
With CreateMap
you generate a new map and you can map one custom property with MapProperty
.
// Creating an Object Mapper
var mapper = new ObjectMapper();
// Creating the map and mapping the property
mapper.CreateMap<User, UserDto>().MapProperty(d => d.Role, s => s.Role.Name);
// Then you map the custom map to a destination
var destination = mapper.Map<UserDto>(user);
To remove a custom property, you also use CreateMap
and then remove the custome property of the mapping.
// Create an Object Mapper
var mapper = new ObjectMapper();
// Creating a map and removing a property
mapper.CreateMap<User, UserDto>().RemoveMapProperty(t => t.Name);
// Then you map the custom map to a destination
var destination = mapper.Map<UserDto>(user);
When you are working with projects related to network or you want to extend your application to use some network functionality the Swan's Network
provides miscellaneous network utilities such as a Public IP finder, a DNS client to query DNS records of any kind, and an NTP client.
It's always useful to have a tool that gives you access to your adapters information and your IP address local and public and use it in your application.
// Gets the active IPv4 interfaces.
var interfaces = Network.GetIPv4Interfaces();
// Retrieves the local IP addresses.
var address = Network.GetIPv4Addresses();
// Gets the public IP address using ipify.org.
var publicAddress = Network.GetPublicIPAddress();
Also, you can use the Network
utility to access the IPs of the DNS servers and the UTC from the NTP servers.
// Gets the configured IPv4 DNS servers for the active network interfaces.
var dnsServers = Network.GetIPv4DnsServers();
// Gets the DNS host entry (a list of IP addresses) for the domain name.
var dnsAddresses = Network.GetDnsHostEntry("google-public-dns-a.google.com");
// Gets the reverse lookup FQDN of the given IP Address.
var dnsPointer = Network.GetDnsPointerEntry(IPAddress.Parse("8.8.8.8"));
// Queries the DNS server for the specified record type.
var mxRecord = Network.QueryDns("google-public-dns-a.google.com", DnsRecordType.MX);
// Gets the UTC time by querying from an NTP server
var dateTime = Network.GetNetworkTimeUtc();
Many times, you need to compare the values inside of an object, array, struct or enum, to do so you need to implement your on code or iterate to find if the values are equals. With ObjectComparer
you easily compare the properties. It represents a quick object comparer using the public properties of an object or the public members in a structure.
// Compare if two variables of the same type are equal.
ObjectComparer.AreEqual(first, second)
// Compare if two objects of the same type are equal.
ObjectComparer.AreObjectsEqual(first, second);
// Compare if two structures of the same type are equal.
ObjectComparer.AreStructsEqual(first, second)
// Compare if two enumerables are equal.
ObjectComparer.AreEnumsEqual(first, second)
It's an easy to use IoC Inversion of Control Container of your classes and interfaces, you can register and associate your class with the interface that is going to be use and then when you finish working with that you can unregister them. You can access a default instance of DependencyContainer
called Container
by Runtime
class.
// Initialize a new instance of DependencyContainer
var container = new DependencyContainer();
// Creates/replaces a named container class registration with a given implementation and default options.
container.Register<IAnimal, Cat>();
// Attempts to resolve a type using specified options.
var resolve = container.Resolve<IAnimal>();
// Remove a named container class registration.
container.Unregister<IAnimal>();
It's a easy usage of the Runtime
class to access the Container
to quickly use the DependencyContainer
.
// Creates/replaces a named container class registration with a given implementation and default options.
Runtime.Container.Register<IAnimal, Dog>();
// Attempts to resolve a type using specified options.
var resolve = Runtime.Container.Resolve<IAnimal>();
// Remove a named container class registration.
Runtime.Container.Unregister<IAnimal>();
A very handy method to determine if a type can be resolve.
// Using CanResolve to check if type can be resolve
if (Runtime.Container.CanResolve<IAnimal>())
{
// Attempts to resolve a type using specified options.
Runtime.Container.Resolve<IAnimal>();
}