Suche: ADO

Syslog-Nachrichten mit C# versenden 👍 👎

Syslog beschreibt einen Protokollierungsstandard. Dieser Beitrag soll die beispielhafte Implementierung eines C#-Clients aufzeigen. Dazu werden UDP-basierte Nachrichten im (älteren) BSD-Format nach RFC 3164 erzeugt.

In einem früheren Beitrag hatte ich bereits mein NAS von Synology erwähnt. Sofern ihr ebenfalls ein solches besitzt, könnt ihr dieses sehr einfach als Server einrichten und somit direkt für die Beispiele (und mehr) nutzen.
Syslog-Klasse implementieren
010203040506070809101112131415161718192021222324252627282930313233343536
public class Syslog : IDisposable {    private const int Facility = 1;  // user-level messages    private const int FacilityFactor = 8;
private readonly UdpClient udpClient;

public Syslog(string hostname = "localhost", int port = 514) { this.udpClient = new UdpClient(hostname, port); }
public void Send(Severity severity, string message, [CallerMemberName] string caller = null) { byte[] data = Encoding.UTF8.GetBytes(String.Format("<{0}>{1} {2} {3}", ((Syslog.Facility * Syslog.FacilityFactor) + (int) severity), DateTime.Now.ToString("MMM dd HH:mm:ss"), Dns.GetHostName(), ((!String.IsNullOrWhiteSpace(caller)) ? (caller + " ") : (String.Empty)) + message ));
this.udpClient.Send(data, data.Length); }
public void Dispose() => this.udpClient?.Dispose();

public enum Severity { Emergency, // [0] system is unusable Alert, // [1] action must be taken immediately Critical, // [2] critical conditions Error, // [3] error conditions Warning, // [4] warning conditions Notice, // [5] normal but significant condition Informational, // [6] informational messages Debug, // [7] debug-level messages } // https://tools.ietf.org/html/rfc3164}
Es handelt sich hierbei – wie üblich – um eine minimale Implementierung ohne Fehlerbehandlung, welche außerdem keine Konfiguration des Facility-Wertes ermöglicht. Die Verwendung gestaltet sich nun jedoch sehr einfach:
Syslog-Klasse verwenden
01020304
using(Syslog syslog = new Syslog("syslog.example.net")) {    syslog.Send(Severity.Notice, "Dies ist eine weniger interessante Meldung.");    syslog.Send(Severity.Warning, "Dies ist eine sehr interessante Meldung.", "MyApp");}

Melodien mit Systemtönen in C# 👍 👎

Im letzten Beitrag hatte ich Systemtöne mit C# vorgestellt. Der Übersicht wegen – und nicht ganz ernstzunehmen – möchte ich mit diesem separaten Beitrag noch ein paar Beispiele zur Umsetzung einfacher Melodien zeigen.

Dazu implementieren wir zuerst eine kleine Hilfsklasse mit einigen vordefinierten Werten:
Klasse zur Arbeit mit der Tonart C-Dur
01020304050607080910111213141516171819
public static class BeepMusic {    private static readonly Dictionary<char, int> frequencyCollection = new Dictionary<char, int>() {        ['C'] = 264, ['D'] = 297, ['E'] = 330, ['F'] = 352,        ['G'] = 396, ['A'] = 440, ['H'] = 495, ['c'] = 528    };
public static void PlayNotes(Duration duration, string noteList) { foreach(char note in noteList) { Console.Beep(frequencyCollection[note], (int) duration); } }
public enum Duration { Whole = 1600, Half = (Whole / 2), Quarter = (Half / 2), Quaver = (Quarter / 2) }}
Nun können wir diese Klasse statisch importieren und (sehr) einfache Melodien erzeugen:
Melodien
01020304050607080910111213141516171819202122232425262728293031323334353637383940
using static BeepMusic;
// TonleiterPlayNotes(Duration.Quarter, "CDEFGAHc");
// Alle meine EntchenPlayNotes(Duration.Quaver, "CDEF");PlayNotes(Duration.Quarter, "GG");PlayNotes(Duration.Quaver, "AAAA");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quaver, "AAAA");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quaver, "FFFF");PlayNotes(Duration.Quarter, "EE");PlayNotes(Duration.Quaver, "GGGG");PlayNotes(Duration.Half, "C");
// Hänschen KleinPlayNotes(Duration.Quarter, "GE");PlayNotes(Duration.Half, "E");PlayNotes(Duration.Quarter, "FD");PlayNotes(Duration.Half, "D");PlayNotes(Duration.Quarter, "CDEFGG");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quarter, "GE");PlayNotes(Duration.Half, "E");PlayNotes(Duration.Quarter, "FD");PlayNotes(Duration.Half, "D");PlayNotes(Duration.Quarter, "CEGG");PlayNotes(Duration.Whole, "C");PlayNotes(Duration.Quarter, "DDDDDE");PlayNotes(Duration.Half, "F");PlayNotes(Duration.Quarter, "EEEEEF");PlayNotes(Duration.Half, "G");PlayNotes(Duration.Quarter, "GE");PlayNotes(Duration.Half, "E");PlayNotes(Duration.Quarter, "FD");PlayNotes(Duration.Half, "D");PlayNotes(Duration.Quarter, "CEGG");PlayNotes(Duration.Whole, "C");
Interessante Effekte ergeben sich darüber hinaus bei (pseudo-)zufälligen Tonfolgen. In einem späteren Beitrag möchte ich gerne aufzeigen, wie man auch etwas ernsthafter mit diesem Thema in C# umgehen kann.

Byte-Angaben mit C# formatieren 👍 👎

Mit gewisser Regelmäßigkeit kommt man als Softwareentwickler in die Verlegenheit, Dateigrößen statt in der Form 10110 Byte in der "üblichen" Schreibweise mit größeren Einheiten (in diesem Fall 9.87 KiB) darzustellen.

Zwar gäbe es hier wieder einmal die Möglichkeit per DllImport und beispielsweise StrFormatByteSizeW zu arbeiten, jedoch möchten wir in diesem Fall die Implementierung selbst vornehmen, um die Basis (Stichwort: Binärpräfixe) konsistent bestimmen zu können. Ich stelle dazu zwei mögliche Implementierungen vor:
Hilfsklasse mit Methode zur Formatierung
0102030405060708091011121314151617
public static class Helper {    private static readonly IReadOnlyList<string> unitList = new string[] {        "Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"    };

public static string FormatBytes(double bytes) { int index = 0;
while(bytes >= 1024 && index < Helper.unitList.Count) { bytes /= 1024; index++; }
return $"{Math.Round(bytes, 2).ToString("N2")} {Helper.unitList[index]}"; }}
Alternativ lässt sich das Problem jedoch auch ohne Schleife – und daher mathematisch etwas eleganter – durch ein ähnliches Vorgehen wie bei der Ermittlung der Anzahl der Ziffern einer Zahl mittels Logarithmen lösen:
Alternativer Lösungsansatz
010203040506
public static string FormatBytes(double bytes) {    int index = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));    bytes /= Math.Pow(1024, index);
return $"{Math.Round(bytes, 2).ToString("N2")} {Helper.unitList[index]}";}
Die Verwendung gestaltet sich nun in jedem Fall recht einfach:
Formatierung verwenden
010203
string formattedBytes = Helper.FormatBytes(1024);         // 1,00 KiBformattedBytes = Helper.FormatBytes(1024 * 1024);         // 1,00 MiBformattedBytes = Helper.FormatBytes(1024 * 1024 * 1024);  // 1,00 GiB
Abschließend möchte ich auch noch kurz eine beispielhafte Implementierung zur Verwendung im Rahmen einer benutzerdefinierten Formatierung per String.Format skizzieren:
Implementierung der benutzerdefinierten Formatierung
010203040506070809
public class FileSizeFormatProvider : ICustomFormatter, IFormatProvider {    public string Format(string format, object arg, IFormatProvider formatProvider) {        return Helper.FormatBytes(Convert.ToDouble(arg));    }
public object GetFormat(Type formatType) { return this; }}
Bitte beachtet jedoch, dass diese Implementierung noch nicht vollständig ist. Weiterführende Informationen zur korrekten Implementierung finden sich im MSDN per ICustomFormatter und IFormatProvider. Verwendung:
Verwendung der benutzerdefinierten Formatierung verwenden
01
formattedBytes = String.Format(new FileSizeFormatProvider(), "{0}", (1024 * 1024));  // 1,00 MiB
Ich werde eine vollständige Implementierung vermutlich in einem späteren Beitrag nachreichen.

RandomNumberGenerator-Implementierung in C# für random.org 👍 👎

In einem früheren Beitrag zum Thema (Pseudo-)Zufallszahlen in C# hatte ich bereits die Klassen Random und RNGCryptoServiceProvider vorgestellt. Letztere leitet dabei von der abstrakten Klasse RandomNumberGenerator ab, welche wir im Folgenden ebenfalls als Basis für eine Klasse verwenden werden.

Unsere Klasse soll die Werte jedoch nicht selbst erzeugen, sondern vom Anbieter random.org über dessen Schnittstelle auf Basis von JSON-RPC beziehen. Der Dienst ist – zumindest derzeit und in gewissen Grenzen – kostenlos, wobei ein API-Schlüssel benötigt wird, den ihr dem Konstruktor der Klasse übergeben müsst.

Hinweis: Ich möchte vor der eigentlichen Implementierung noch darauf hinweisen, dass dieser Beitrag weder die Güte der darüber ermittelten (Pseudo-)Zufallszahlen noch die Vertrauenswürdigkeit des Anbieters bewerten soll. Dieser Beitrag soll lediglich eine sehr einfache und beispielhafte Implementierung für den Fall aufzeigen, dass ihr euch eben für diese Nutzung (beispielsweise für weniger kritische Daten oder aus reinem Interesse) entschieden habt.
Klasse implementieren
010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445
public class RandomOrg : RandomNumberGenerator {    private const string ApiMethod = "generateIntegers";    private const string ApiUrl = "https://api.random.org/json-rpc/1/invoke";    private const string ApiVersion = "2.0";
private readonly string apiKey;

public RandomOrg(string apiKey) { this.apiKey = apiKey; }
public override void GetBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length), data, data.Length); }
public override void GetNonZeroBytes(byte[] data) { Array.Copy(this.SendRequest(data.Length, 1), data, data.Length); }

private byte[] SendRequest(int n, byte min = 0) { using(WebClient webClient = new WebClient()) { JavaScriptSerializer js = new JavaScriptSerializer();
dynamic data = js.Deserialize<dynamic>(webClient.UploadString(ApiUrl, js.Serialize(new { jsonrpc = ApiVersion, method = ApiMethod, @params = new { apiKey = this.apiKey, n = n, min = min, max = 255 }, id = DateTime.Now.Ticks })));
if(data.ContainsKey("error")) { throw new Exception(data["error"]["message"]); }
return Array.ConvertAll((object[]) data["result"]["random"]["data"], Convert.ToByte); } }}
Die eigentliche Verwendung gestaltet sich nun kompatibel zur Basisklasse RandomNumberGenerator und entsprechend einfach. Um beispielsweise ein byte-Feld mit fünf Zahlen zu befüllen, kann folgender Aufruf dienen:
Klasse verwenden
01020304
using(RandomOrg numberGenerator = new RandomOrg("@ToDo")) {  // API-Schlüssel eintragen    byte[] randomNumbers = new byte[5];    numberGenerator.GetBytes(randomNumbers);}

Aufzählungswert per Zeichenkette ermitteln 👍 👎

Vor sehr langer Zeit habe ich bereits einen Artikel zu Enumerationen in C# geschrieben, an welchen ich indirekt anknüpfen möchte. Oftmals steht man im Bereich der Softwareentwicklung vor dem Problem der Interoperabilität, da mehrere – bisweilen äußerst unterschiedliche – Systeme zusammenarbeiten müssen.

Ein klassischer Fall im Bereich der Webentwicklung ist die Zusammenarbeit aus Anwendung und Datenbank, beispielsweise hier im Blog zwischen der Programmiersprache C# mit ASP.NET MVC-Framework und MySQL-Datenbank. Etwas konkreter soll es darum gehen, C#-Aufzählungstypen zusammen mit MySQL-ENUMs zu verwenden, welche grundsätzlich erst einmal als bloße Zeichenketten zu behandeln sind. Für das folgende Beispiel nehmen wir an, dass eine Tabelle user existiert, die eine Spalte state vom Typ ENUM('ACTIVE','INACTIVE','LOCKED') enthält.

Unter Verwendung des ADO.NET Entity Framework würde beim Database First-Ansatz eine entsprechende Klasse user mit der Eigenschaft state vom Typ string erzeugt. Da es sich um eine partielle Klasse handelt, erweitern wir diese um eine neue Eigenschaft State. Diese soll anschließend die automatische Konvertierung für state übernehmen und ermöglicht uns im Programmcode einen typsicheren Zugriff auf den Status des Benutzers:
Aufzählungstyp UserState anlegen
0102030405
public enum UserState {    Active,    Inactive,    Locked}
Erweiterung der Klasse user
010203040506070809
public partial class user {    public UserState State {        get {            return (UserState) Enum.Parse(typeof(UserState), this.state, true);        }        set {            this.state = value.ToString().ToUpper();        }    }
Die Verwendung gestaltet sich nun – mit beiden Eigenschaften – wie gewohnt komfortabel:
Verwendung
0102030405060708091011121314151617
user dbUser = new user() {    State = UserState.Active};  // üblicherweise aus Datenbank ermittelt

string state = dbUser.state; // "ACTIVE"UserState userState = dbUser.State; // Active
dbUser.State = UserState.Inactive;

state = dbUser.state; // "INACTIVE"userState = dbUser.State; // Inactive
dbUser.state = "LOCKED";

state = dbUser.state; // "LOCKED"userState = dbUser.State; // Locked
Weitere Informationen zu den Enum.Parse-Methoden finden sich wie üblich im MSDN. Es lohnt sich außerdem ein Blick auf die Enum.TryParse(…)-Methoden mit generischem Typ-Parameter.

12

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche