Lang ist’s her

Moin moin und Hallo!
Fast ein Jahr ist seit meinem letzten Eintrag vergangen... aber was soll man tun.
Viel ist nicht passiert in dieser Zeit. Arbeit, Arbeit und dies und jenes und im Sommer 2012 eine Woche Urlaub auf Fuerteventura.
Der ganz normale Wahnsinn eben.
Aber es gibt Neuigkeiten:
Ich habe es endlich mal geschafft mich dazu durchzuringen, die in die Jahre gekommene Pulse-Bibliothek umzuschreiben.
Bisher habe ich die Woche damit verbracht, den alten Code zu überführen.
Dabei fällt der ganze Schrott wie "Überall return-Werte" und "alle möglichen Exceptions abfangen" weg.
Die neue Version wird als auf ExceptionThrowing basieren. Daher muss noch einiges umgeschrieben werden, da ich eher eigene Exception-Typen benutzten möchte.
Des Weiteren werden die .Net-Empfehlung der Namenskonvention durchgesetzt.
Ein paar neue Klassen werden natürlich auch nicht fehlen, da sich in der Zeit einiges angesammelt hat, was ich natürlich gern in die Pulse mit aufnehme.
Was am meistens Zeit kosten wird, ist die komplette Durchsicht des Codes und dann natürlich noch das Testen.
Also müsst ihr euch wohl noch etwas gedulden.
Bis dahin euch erstmal einen schönen Rest-Sonntag und so weiter.
Reingehauen und wiederschauen!
.NET-Snippets wird 5!

Aus aktuellem Anlass möchte ich hiermit der Community .Net-Snippets zum 5. Geburtstag gratulieren, welche sich in dieser Zeit zu einem sehr nützlichen Werkzeug im Programmierer-Alltag entwickelt hat. Die dort veröffentlichen Snippets sind ziemlich oft hilfreich, bieten für jeden Erfahrungsgrad etwas und ich bin froh dort mitwirken zu dürfen. Ihr findet mich dort übrigens unter Legion
Summa Summarum: Eine tolle Community und weiter so!
"Happy Birthday"
Einführung in Erweitungsmethoden

Hallo ihr da draußen! Ich bin gestern auf ein Snippet bei dotnet-snippets gestoßen, in dem ein Element an ein bestehendes Array angehangen werden soll. Nachdem ich dort eine einfachere Methode vorgeschlagen habe, ist mir noch ein viel elegantere Art und Weise eingefallen, wie man dies Bewerkstelligen könnte. Und zwar über Erweiterungsmethoden! Dabei bleibt das Vorgehen das selbe. Bei Erweiterungsmethoden handelt es sich um Erweitungen zu bestehenden Funktionalitäten von Klassen/Typen. In unserem Fall des Arrays. Wir bauen uns also zwei Erweitungen, eine für das Ändern der Größe und eine zum Anhängen des Elementes. Was brauchen wir dafür? Als allererstes benötigen wir eine als static deklarierte Klasse, diese nennen wir ArrayExtensions.
/// <summary>
/// Klasse für Array-Erweiterungen
/// </summary>
public static class ArrayExtensions
{
}
als nächstes fangen wir an uns den Funktionsrumpf für die Resize-Funktion anzusehen
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <param name="NewSize"></param>
public static T[] Resize<T>(this T[] Source, int NewSize)
{
}
Wir ihr sehen könnt, benötigen wir in dem Fall einen Rückgabewert der Funktion. Warum? Bei einem Array handelt es sich um einen Value Type, daher kann dies nicht direkt in der Erweiterungsmethode geändert werden. Es ist leider auch nicht möglich, die Funktion so zu deklarieren, dass das Array als ref übergeben wird. Wenn jemand dennoch eine Lösung gefunden hat, bitte melden!. Bei Referenz-Typen könnt ihr nach eigenem Ermessen in der Erweiterungsmethode an dem Objekt rumpfuschen, ohne dass ihr die Änderung zurückgeben müsst.
Aus diesem Grund gilt: Wenn in der Erweiterungsmethode zu einem ValueType gehört, und dort das ValueType-Object geändert werden soll, muss das geänderte Objekt zurückgegeben und zugewiesen werden. Der Aufruf würde dann so aussehen:
object[] myArray = new object[] { 1, 2, 3 };
myArray = myArray.Resize(4);
Der Parameter NewSize ist, denk ich mal, selbsterklärend. Des Weiteren müssen Erweiterungsmethoden immer als static deklariert sein. Sie werden jedoch per Instanzmethodensyntax aufgerufen. Also wie in obrigen Beispiel.
Das Interessantere ist jedoch der erste Parameter der Funktion. Dieser beginnt, wie ihr sehen könnt mit this T[]. Darüber wird signalisiert, dass es sich um eine Erweiterungsmethode für den Typ T[] handelt. Also eine Erweiterung für ein Array von irgendeinem Typ. Die fertige Version sieht dann so aus:
/// <summary>
/// Klasse für Array-Erweiterungen
/// </summary>
public static class ArrayExtensions
{
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <param name="NewSize"></param>
public static T[] Resize<T>(this T[] Source, int NewSize)
{
Array.Resize(ref Source, Source.Length + 1);
return Source;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <param name="NewElement"></param>
public static T[] Append<T>(this T[] Source, T NewElement)
{
Array.Resize(ref Source, Source.Length + 1);
Source[Source.Length - 1] = NewElement;
return Source;
}
}
Datenbankverbindung herstellen

Codierte Grüße an euch Entwickler! Heute gibt es ein Snippet, was für die angehenden Entwickler unter euch ist. Und zwar wie man recht einfach die Connectivität zu einer Datenbank herstellt. Dabei benutze ich eine Variante, die sich simpel abändern lässt, um andere Datenbanken anzusprechen, wie z.b. Access und MySql. In meinem Beispiel beziehe ich mich vorerst auf Sql.
Wie man in dem unteren Beispiel sehen kann, benutze ich System.Data.SqlClient.SqlConnection und System.Data.SqlClient.SqlCommand für die Instanzierung.
Für eine Access-Verbindung bräuchtet ihr System.Data.Odbc.OdbcConnection und System.Data.Odbc.OdbcCommand. Des Weiteren müsstet ihr bei Access den Datenbank-Treiber angeben, welcher auch auf dem System installiert sein muss!. Bei der 2003er-Version war das glaub ich
DbConnectionStringBuilder["Driver"] = "Microsoft Access Driver (*.mdb)";
Wenn ihr eine MySql-Verbindung herstellen möchtet, benötigt ihr den MySql-Connector für .Net. Den Link dazu findet ihr im Download-Bereich. Die Instanzierung erfolgt dann über MySql.Data.MySqlClient.MySqlConnection und MySql.Data.MySqlClient.MySqlCommand.
//Wir benutzen eine Common.DbConnection als Object und instanzieren dies als SqlConnection
//Hier erfolgt die Definition, welche Datenbankverbindung wir nutzen, also ob Access,MySql,Sql, usw.
//Anmerkung: Man könnte DbConnection auch mit der using-Direktive benutzen!
System.Data.Common.DbConnection DbConnection = new System.Data.SqlClient.SqlConnection();
//Der DbCommand dient zum Absenden der Queries, hier auch wieder als SqlCommand instanziert
System.Data.Common.DbCommand DbCommand = new System.Data.SqlClient.SqlCommand();
//Anschließend weisen mir dem Command die entsprechende Verbindung zu
DbCommand.Connection = DbConnection;
//DbConnectionStringBuilder dient zur einfachen Erstellung von ConnectionStrings, man kann auch ohne Arbeiten,
//und den ConnectionString direkt beim Construktur der SqlConnection übergeben.
//Für die Verbindungszeichenfolgen empfehle ich: http://www.connectionstrings.com/
//In unserem Fall verbinden wir uns zu einem SqlServer2005
System.Data.Common.DbConnectionStringBuilder DbConnectionStringBuilder = new System.Data.Common.DbConnectionStringBuilder();
//Anwählen des Datenbank Servers
DbConnectionStringBuilder["Server"] = "localhost";
//Anwählen der Datenbank, diese kann später entweder über DbConnection.ChangeDatabase oder per Query geändert werden
DbConnectionStringBuilder["Database"] = "Northwind";
//Integrated Security, bedeutet, dass in diesem Fall die Windows-Authentifizierung genutzt wird
//Ansonsten, werden die Parameter: "User ID" und "Password" zur Authentifizierung genutzt
DbConnectionStringBuilder["Integrated Security"] = "SSPI";
//Nun übergeben wir den ConnectionString an die DbConnection
DbConnection.ConnectionString = DbConnectionStringBuilder.ConnectionString;
try
{
//Öffnen der Verbindung
DbConnection.Open();
//Wir überprüfen sicherheitshalber, nochmal den Status der Verbindung, ob diese auch wirklich geöffnet ist,
//bevor wir einen Query absenden
if (DbConnection.State == System.Data.ConnectionState.Open)
{
int Return = 0;
//Wir haben eine Tabelle TEST mit den Spalten ID und Wert, in welche wir einen neuen Datensatz einfügen
DbCommand.CommandText = "INSERT INTO dbo.TEST (ID, Value) VALUES (3,'This is a test');";
//ExecuteNonQuery gibt die Anzahl der betroffenen Zeilen zurück. Bei Selects immer -1;
Return = DbCommand.ExecuteNonQuery();
//Nun könnten wir noch den Rückgabewert überprüfen, dieser dürfte 1 sein
}
//Schließen der Verbindung, welches man auch weglassen könnte, da die Dispose-Methode dies auch übernimmt
//Jedoch sinnvoll, wenn ohne Dispose gearbeitet wird
DbConnection.Close();
}
//Verwerfen der Verbindung
finally
{
DbConnection.Dispose();
}
Viel Spaß mit dem Snippet und auf bald!
Einfache Ini-Klasse

Ich melde mich mal wiedr zurück. Heute habe ich für euch eine kleine aber feine Klasse für Ini-Dateien basiend auf Dictionaries, welche ich vor ein paar Minuten soweit erstmal fertiggestellt habe. Grundlegendem Fehlerabfang ist soweit implementiert, diesmal per Exceptions. Ein paar Erweiterungen werden sich dafür aber bestimmt noch finden lassen, wie z.b. Kommentierte Zeilen mitnehmen (natürlich mit der korrekten Position in der Inidatei), oder Export/Import per Xml.
Ich gedenke auch die alte Ini-Klasse der Pulse-Bibliothek mit dieser Version zu ersetzen, wenn ich mich endlich mal entschieden habe, wie die Umstrukturierung erfolgen soll. Verbesserungsvorschläge und so weiter, sind wie immer willkommen.
Bis dahin euch erstmal einen guten Start ins Wochenende!
/// <summary>
///
/// </summary>
public sealed class Ini
{
/// <summary>
///
/// </summary>
public Ini()
{
this._Categories = new Dictionary<String,IniCategory>();
this._General = new IniCategory(String.Empty);
}
/// <summary>
///
/// </summary>
/// <param name="File"></param>
public Ini(String File) : this()
{
this.Read(File);
}
/// <summary>
///
/// </summary>
public String Name
{
get
{
return this._Name;
}
set
{
this._Name = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="File"></param>
public void Read(String File)
{
if (!System.IO.File.Exists(File)) throw new System.IO.FileNotFoundException("The specified path doesn't exist");
this._Name = System.IO.Path.GetFileNameWithoutExtension(File);
using (System.IO.StreamReader Input = new System.IO.StreamReader(File))
{
String Line = String.Empty;
String Temp = String.Empty;
IniCategory TCategory = this._General;
while (!Input.EndOfStream)
{
Line = Input.ReadLine();
//Skip comment lines and empty lines
if (Line.StartsWith(";") || String.IsNullOrEmpty(Line)) continue;
//Detect categories
if (Line.StartsWith("[") && Line.EndsWith("]"))
{
TCategory = this.CreateCategory(Line.Substring(1, Line.Length - 2));
continue;
}
//Detect invalid entries
if (!Line.Contains('=')) continue;
Temp = Line.Split('=')[0];
//Detect duplicate entries
if (TCategory._Entries.ContainsKey(Temp))
{
//Something to do
}
else
{
TCategory.CreateEntry(Temp, Line.Substring(Temp.Length + 1));
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="File"></param>
public void Save(String File)
{
using (System.IO.StreamWriter Output = new System.IO.StreamWriter(File, false))
{
foreach (IniEntry Entry in this._General._Entries.Values)
{
Output.WriteLine(Entry.Name + "=" + Entry.Value);
}
foreach (IniCategory Category in this._Categories.Values)
{
Output.WriteLine("[" + Category.Name + "]");
foreach (IniEntry Entry in Category._Entries.Values)
{
Output.WriteLine(Entry.Name + "=" + Entry.Value);
}
}
}
}
/// <summary>
///
/// </summary>
public IniCategory[] Categories
{
get
{
return this._Categories.Values.ToArray();
}
}
/// <summary>
///
/// </summary>
public IniEntry[] Entries
{
get
{
List<IniEntry> TEntries = new List<IniEntry>();
TEntries.AddRange(this._General.Entries);
foreach (IniCategory TCategory in this._Categories.Values)
{
TEntries.AddRange(TCategory.Entries);
}
return TEntries.ToArray();
}
}
/// <summary>
///
/// </summary>
public IniCategory General
{
get
{
return this._General;
}
}
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <returns></returns>
public IniCategory this[String Category]
{
get
{
if (!this._Categories.ContainsKey(Category)) throw new InvalidOperationException("The category doesn't exists");
else return this._Categories[Category];
}
}
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <param name="Entry"></param>
/// <returns></returns>
public IniEntry this[String Category, String Entry]
{
get
{
try
{
if (!this._Categories.ContainsKey(Category)) throw new InvalidOperationException("The category doesn't exists");
else if (!this._Categories[Category]._Entries.ContainsKey(Entry)) throw new InvalidOperationException("The entry doesn't exists");
else return this._Categories[Category]._Entries[Entry];
}
catch
{
return null;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
public IniCategory CreateCategory(String Name)
{
if (String.IsNullOrEmpty(Name)) throw new InvalidOperationException("The name of a category cant be empty");
else if (this._Categories.ContainsKey(Name)) throw new InvalidOperationException("The category you tried to create already exists");
return this._Categories[Name] = new IniCategory(Name);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
public IniEntry CreateEntry(String Name)
{
if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
if (this._General._Entries.ContainsKey(Name)) throw new InvalidOperationException("The entry you tried to create already exists");
return this._General._Entries[Name] = new IniEntry(Name, String.Empty);
}
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <param name="Name"></param>
public IniEntry CreateEntry(String Category, String Name)
{
return this.CreateEntry(Category, Name, String.Empty);
}
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <param name="Name"></param>
/// <param name="Value"></param>
public IniEntry CreateEntry(String Category, String Name, String Value)
{
if (!this._Categories.ContainsKey(Category)) throw new InvalidOperationException("The category doesn't exists");
else if (this._Categories[Category]._Entries.ContainsKey(Name)) throw new InvalidOperationException("The entry you tried to create already exists");
else if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
else return this._Categories[Category]._Entries[Name] = new IniEntry(Name, Value);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <param name="Value"></param>
public void SetEntry(String Name, String Value)
{
if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
if (this._General._Entries.ContainsKey(Name)) this._General._Entries[Name].Value = Value;
else this._General._Entries[Name] = new IniEntry(Name, Value);
}
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <param name="Name"></param>
/// <param name="Value"></param>
public void SetEntry(String Category, String Name, String Value)
{
if (String.IsNullOrEmpty(Category)) throw new InvalidOperationException("The name of a category can't be empty");
else if (String.IsNullOrEmpty(Name)) throw new InvalidOperationException("The name of an entry can't be empty");
else if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
IniCategory TCategory = null;
if (!this._Categories.ContainsKey(Category)) TCategory = this._Categories[Category] = new IniCategory(Category);
else TCategory = this._Categories[Category];
if (!TCategory._Entries.ContainsKey(Name)) TCategory._Entries[Name] = new IniEntry(Name, Value);
else TCategory._Entries[Name].Value = Value;
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <returns></returns>
public bool CategoryExists(String Name)
{
return this._Categories.ContainsKey(Name);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <returns></returns>
public bool EntryExists(String Name)
{
return this._General._Entries.ContainsKey(Name);
}
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <param name="Name"></param>
/// <returns></returns>
public bool EntryExists(String Category, String Name)
{
return this._Categories[Category]._Entries.ContainsKey(Name);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
public void RemoveCategorie(String Name)
{
if (String.IsNullOrEmpty(Name)) throw new InvalidOperationException("The name of a category can't be empty");
else this._Categories.Remove(Name);
}
/// <summary>
/// Removes an entries von the categorized-entries
/// </summary>
/// <param name="Category"></param>
/// <param name="Name"></param>
public void RemoveEntry(String Category, String Name)
{
if (this._Categories.ContainsKey(Category)) this._Categories[Category]._Entries.Remove(Name);
}
/// <summary>
/// Removes an entry from the uncategorized-entries
/// </summary>
/// <param name="Name"></param>
public void RemoveEntry(String Name)
{
this._General.RemoveEntry(Name);
}
/// <summary>
/// Represent the categories
/// </summary>
internal Dictionary<String, IniCategory> _Categories;
/// <summary>
/// Represent the Entries without category
/// </summary>
internal IniCategory _General;
/// <summary>
///
/// </summary>
private String _Name;
}
/// <summary>
///
/// </summary>
public sealed class IniCategory
{
/// <summary>
///
/// </summary>
/// <param name="Ini"></param>
/// <param name="Name"></param>
/// <returns></returns>
public static IniCategory Create(Ini Ini, String Name)
{
if (Ini == null) throw new ArgumentNullException("The ini object can't be null");
else if (Ini._Categories.ContainsKey(Name)) throw new InvalidOperationException("The category you tried to create already exists");
else if (String.IsNullOrEmpty(Name)) throw new InvalidOperationException("The name of a category can't be empty");
return Ini.CreateCategory(Name);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
internal IniCategory(String Name)
{
this._Name = Name;
this._Entries = new Dictionary<String, IniEntry>();
}
/// <summary>
///
/// </summary>
public String Name
{
get
{
return this._Name;
}
}
/// <summary>
///
/// </summary>
/// <param name="Entry"></param>
/// <returns></returns>
public IniEntry this[String Entry]
{
get
{
try
{
return this._Entries[Entry];
}
catch
{
return null;
}
}
}
/// <summary>
///
/// </summary>
public IniEntry[] Entries
{
get
{
return this._Entries.Values.ToArray();
}
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
public IniEntry CreateEntry(String Name)
{
return this.CreateEntry(Name, String.Empty);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <param name="Value"></param>
public IniEntry CreateEntry(String Name, String Value)
{
if(this._Entries.ContainsKey(Name)) throw new InvalidOperationException("The entry you tried to create already exists");
else if (String.IsNullOrEmpty(Name)) throw new InvalidOperationException("The name of an entry can't be empty");
else if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
return (this._Entries[Name] = new IniEntry(Name, Value));
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
public void RemoveEntry(String Name)
{
this._Entries.Remove(Name);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <param name="Value"></param>
public void SetEntry(String Name, String Value)
{
if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
if(!this._Entries.ContainsKey(Name)) this._Entries[Name] = new IniEntry(Name, Value);
else this._Entries[Name].Value = Value;
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <returns></returns>
public bool EntryExists(String Name)
{
return this._Entries.ContainsKey(Name);
}
/// <summary>
///
/// </summary>
public bool HasEntries
{
get
{
return (this._Entries.Count > 0);
}
}
/// <summary>
///
/// </summary>
private String _Name;
/// <summary>
///
/// </summary>
internal Dictionary<String, IniEntry> _Entries;
}
/// <summary>
///
/// </summary>
public sealed class IniEntry
{
/// <summary>
///
/// </summary>
/// <param name="Category"></param>
/// <param name="Name"></param>
/// <param name="Value"></param>
public static IniEntry Create (IniCategory Category, String Name, String Value)
{
if (Category == null) throw new ArgumentNullException("The category object can't be null");
else if (Category._Entries.ContainsKey(Name)) throw new InvalidOperationException("The entry you tried to create already exists");
else if (String.IsNullOrEmpty(Name)) throw new InvalidOperationException("The name of an entry can't be empty");
else if (Name.Contains("=")) throw new InvalidOperationException("The name can't contain =");
return Category.CreateEntry(Name, Value);
}
/// <summary>
///
/// </summary>
/// <param name="Name"></param>
/// <param name="Value"></param>
internal IniEntry(String Name, String Value)
{
this._Name = String.Intern(Name);
this._Value = String.Intern(Value);
}
/// <summary>
///
/// </summary>
public String Name
{
get
{
return this._Name;
}
}
/// <summary>
///
/// </summary>
public String Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
/// <summary>
///
/// </summary>
public bool HasValue
{
get
{
return !String.IsNullOrEmpty(this._Value);
}
}
/// <summary>
///
/// </summary>
private String _Name;
/// <summary>
///
/// </summary>
private String _Value;
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
return this._Value;
}
}