Rash thoughts about .NET, C#, F# and Dynamics NAV.


"Every solution will only lead to new problems."

Saturday, 4. November 2006


Eine überwachte Kopierfabrik (5)

Filed under: C#,Tools — Steffen Forkmann at 11:22 Uhr

Im fünften Teil meiner Serie möchte ich auf erweiterte Möglichkeiten der Konfiguration des Dienstes eingehen. Über die ApplicationSettings ist es in Visual Studio ganz einfach möglich Strings, Integer usw. als globale Anwendungseinstellungen (web.config bei ASP.NET – sonst app.config) abzuspeichern:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="userSettings" type="..."> <section name="DBCopyService.Properties.Settings" type="..." allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> </configSections> <userSettings> <DBCopyService.Properties.Settings> <setting name="ServerName" serializeAs="String"> <value>serverName</value> </setting> </DBCopyService.Properties.Settings> </userSettings> </configuration>

Der Zugriff auf die Einstellung “ServerName” erfolgt dann ganz einfach über Properties.Settings.Default.ServerName. Man kann sogar zur Laufzeit die Settings ändern und wieder abspeichern.

Wenn man jedoch komplexere Strukturen in den Einstellungen ablegen möchte, kommt man um etwas Code nicht mehr herum. Im folgenden Beispiel lege ich die neue Sektion “entrances” an, um dort dann beliebig viele Eingangsverzeichnisse überwachen zu lassen:

Eintrag in der app.config:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="DBCopyService"> <section name="entrances" type="DBCopyService.EntranceSection,DBCopyService" /> </sectionGroup> </configSections> <DBCopyService> <entrances> <entrance name="entrance1" path="C:\Eingang\" mailAddress="mail@server.de" /> <entrance name="entrance2" path="C:\Eingang2\" mailAddress="mail2@server.de" /> </entrances> </DBCopyService> </configuration>

Zugriff über Ableitung von ConfigurationElement, ConfigurationElementCollection und ConfigurationSection:

using System; using System.Collections.Generic; using System.Text; using System.Configuration; namespace DBCopyService { public sealed class EntranceElement : ConfigurationElement { /// <summary> /// Gets or sets the name. /// </summary> /// <value>The name.</value> [ConfigurationProperty("name", IsKey = true, IsRequired = true)] public string Name { get { return (string)base["name"]; } set { base["name"] = value; } } /// <summary> /// Gets or sets the mail address. /// </summary> /// <value>The mail address.</value> [ConfigurationProperty("mailAddress", IsRequired = true)] public string MailAddress { get { return (string)base["mailAddress"]; } set { base["mailAddress"] = value; } } /// <summary> /// Gets or sets the path. /// </summary> /// <value>The path.</value> [ConfigurationProperty("path", IsRequired = true)] public string Path { get { return (string)base["path"]; } set { base["path"] = value; } } } }
using System; using System.Collections.Generic; using System.Text; using System.Configuration; namespace DBCopyService { public sealed class EntranceCollection : ConfigurationElementCollection { protected override ConfigurationElement CreateNewElement() { return new EntranceElement(); } protected override object GetElementKey( ConfigurationElement element) { return ((EntranceElement)element).Name; } public override ConfigurationElementCollectionType CollectionType { get { return ConfigurationElementCollectionType.BasicMap; } } protected override string ElementName { get{ return "entrance"; } } } }
using System; using System.Collections.Generic; using System.Text; using System.Configuration; namespace DBCopyService { public sealed class EntranceSection : ConfigurationSection { [ConfigurationProperty("", IsDefaultCollection = true)] public EntranceCollection Entrances { get { return (EntranceCollection)base[""]; } } } }

Mit Hilfe dieser Klassenstruktur (Element, Collection, Section) kann man nun bequem auf die Section zugreifen:

EntranceSection entranceSection = ConfigurationManager.GetSection("DBCopyService/entrances") as EntranceSection; foreach (EntranceElement element in entranceSection.Entrances) Console.WriteLine( string.Format("Path {0}, Mail {1}", element.Path, element.MailAddress));

Eine überwachte Kopierfabrik (4)

Filed under: C#,Tools — Steffen Forkmann at 11:00 Uhr

Im vierten Teil meiner kleinen Serie zur überwachten Kopierfabrik, soll es nun darum gehen den Kopiervorgang in einen anderen Thread auszulagern. Das Problem ist nämlich, dass der lang anhaltende Kopiervorgang sonst das Erkennen weiterer Dateisystem-Ereignisse verhindert und der Dienst für die Zeit des Kopierens “eingefroren” ist. Um nun den Kopiervorgang “parallel” zu starten, kann man in C# 2.0 den sogenannten BackgroundWorker benutzen. Dieser bietet auf ganz einfache Weise die Möglichkeit ein lang andauernden Vorgang in einen Hintergrundthread zu verlagern und über Events sogar Fortschrittsmeldungen nach Außen zu geben. Im speziellen Fall des Kopierens könnte man also einfach von BackgroundWorker ableiten:

using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.IO; using System.Diagnostics; namespace DBCopyService { /// <summary> /// This BackgroundWorker copies the file async /// from source to target /// </summary> public class BackgroundCopyWorker : BackgroundWorker { private string sourceFile; private string targetFile; public BackgroundCopyWorker (string sourceFile, string targetFile) : base() { this.sourceFile = sourceFile; this.targetFile = targetFile; DoWork += new DoWorkEventHandler(bcw_DoWork); RunWorkerCompleted += new RunWorkerCompletedEventHandler (bcw_RunWorkerCompleted); RunWorkerAsync(); // Start new Thread } void bcw_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { Trace.WriteLine(string.Format("{0} copied to {1}", sourceFile, targetFileName)); } void bcw_DoWork(object sender, DoWorkEventArgs e) { try { Trace.WriteLine(string.Format("Copy {0} to {1}", sourceFile, targetFile)); FileInfo SourceFile = new FileInfo(sourceFile); FileInfo TargetFile = new FileInfo(targetFile); SourceFile.CopyTo(TargetFile.FullName, true); } catch (Exception ex) { Trace.TraceError(ex.Message); } } } }

Das Starten eines asyncronen Kopiervorgangs erfolgt dann ganz einfach über:

new BackgroundCopyWorker(originalFileName, targetFileName);