Wenn man aus einem Pool von x Zeilen, die ein Select-Statement zurückliefert, eine zufällige Auswahl an Zeilen habe möchte geht das ganz einfach so: SELECT TOP 5 NEWID() AS randomNumber, * FROM [MyTable] ORDER BY 1
In diesem SQL-Statement wird einfach eine Guid erzeugt und das Resultset nach dieser zusätzlichen Spalte sortiert. Durch die Anweisung TOP 5 werden dann auch nur die ersten fünf Zeilen im Resultset aufgeführt.
Schon ist es vollbracht!
Alternativ - und wie ich auch gerade festgestellt habe der von Microsoft vorgeschlagene Weg - kann man auch folgenden Befehl nutzen
SELECT TOP 5 * FROM [MyTable] ORDER BY NEWID()
Vorteil: Die zusätzliche Spalte randomNumber wird nicht mit in das Resultset aufgenommen und ich persönlich finde es auch etwas eleganter :)
Gerade habe ich auf GotDotNet mal wieder eine wirklich coole Komponente gefunden, mit der man die Sidebar von Outlook 2003 abbilden kann.
Zu finden gibt es dieses .NET Komponenten - die übrigens in C# implementiert wurde - inkl. dem kompletten Sourcecode und einer Beispielanwendung, wie man die Komponente einsetzt, genau hier (klickst Du).
Letzte Woche Donnerstag war ich auf dem diesjährigen NRW Sumit veranstaltet von JustCommunity e.V.. Es war eine wirklich klasse Veranstaltung, auf der man eine ganze Menge geboten bekommen hat. Es gab sehr viele interessante Vorträge und die eine besser Auswahl an Dozenten kann man wohl kaum treffen. Nur schade war das Christian Weyer absagen musste und damit der Vortrag über die WCF ausgefallen ist. Es war eine gute Gelegenheit sich mit Kollegen auszutauschen und ein paar Anregungen mitzunehmen.
Es wurden aber nicht nur Vorträge zum Thema "Mircosoft" angeboten auch die PHP- und Linuxwelt waren mit Referenten vertreten. Also für jeden Geschmack gab es das passende Angebot.
Besonders gespant war ich auf den letzten Vortrag über "Indigo & SOA in the real World", in dem Daniel Fisher und Michael "Security" Willers aus einem Projekt für das BSI berichteten. Ziel war es wohl Indigo (WCF) auf Sicherheitsmerkmale zu überprüfen. Nun und auf der ersten Folie stand dann plötzlich Proseware :) ... die Anwendung lebt also doch noch. Ich bin leider nicht dazu gekommen nachzufragen ob die Anwendung komplett neugeschrieben wurde oder aber ob es die Basis von Clemens ist. Leider hat Proseware ja nie eine Freigabe von Microsoft erhalten :( aber diese Version wird wohl zum Ende des Jahres erhältlich sein ... ich denke auf diese Anwendung darf man sich sehr freuen!
Leider konnte ich nicht alle Vorträge besuchen da immer vier Tracks parallel liefen und das von 10:00Uhr Uhr bis Abends 20:30Uhr (!). Aufgrund der Länge und des doch sehr warmen Wetters waren wohl alle am Abend ziemlich platt und müde, was bei der anschließende Party mit 50 Liter Freibier aber keinen größeren Schaden anrichtete.
Ein großes Dankeschön geht an dieser Stelle an die beiden Hauptorganisatoren Daniel Fisher und Stephan Oetzel ... ich freue mich sehr auf den NRW Summit 07 :)
Die letzten Monate war es um DotNetNuke ja doch recht ruhig geworden. Mittlerweile kommt aber wieder Leben in den Releasezyklus :) Vor wenigen Tagen wurde die Version 3.3.3 veröffentlich, die als Final Release bezeichnet wird.
Hier ist ein Auszug aus den Änderungen (die Orginalfassung findet man unter dotnenuke.com):
Membership Management CAPTCHA - eine bekannte Technologie, die mehr Sicherheit bei der Anmeldung verspricht. In einem Bild werden Zahlen und oder Buchstaben angezeigt, die der Benutzer bei der Anmeldung eingegben muss. Durch die Darstellung als Bild es es dann nicht mehr so ohne weiteres möglich eine brute force Attacke durchzuführen. Login Redirect - Nach der Anmeldung kann man die User zu einer definierten Seite weiterleiten Password Generation - Als Administrator kann man nun automatisier ein Passwort für einen Anwender verwegeben. Public Registration - Beim diesem Anmeldetyp wird nun auch eine E-Mail an den Benutzer geschickt - damit kann man den Mißbrauch von Daten vorbeugen. Profile Change Notification - Anwender werde über Änderungen in Ihrem Profil per E-Mail informiert. User Account Creation Notification - Wenn ein Administrator ein Benutzerkonto anlegt, dann können nun die Daten an den entsprechenden Benutzer verschickt werden. Force Profile Update - Der Benutzer kann dazu "gezwungen" werden, nach einem Login sein Profil u verändern. Force Password Change - Bietet die Möglichkeit das der Anwender sein Passwort erneuern muss. Password Complexity - Der Aufbau und die Komplexität der Passwörter lassen sich definieren. Preserve Login Parameters - Wenn ein Benutzer zur Anmeldung geleitet wird, dann wird nun die Herkunftsurl ausgelesen und gespeichert, damit anschließend dahin zurück geleitet werden kann. Automated Verified Registration URL - Der Benutzer bekommt per E-Mail eine URL und sobald er diese aufruft wird das Konto freigeschaltet. User Lockout Notification - Wen ein Benutzerkonto gesperrt wird (durch die falsche Eingabe vom Passwort), bekommt der Administrator darüber eine E-Mail.
Role Management Effective Date - Ab wann soll eine Rolle gültig sein. Role Groups - Rollen können nun zu Gruppen zusammengefasst werden.
Usability Rich Text Editor - Nun können Verlinkungen zu Seiten im Poral direkt angelegt werden. Newsletter - Die Abenderadresse kann definiert werden.
Framework AccessDeniedURL - Hierfür kan nu eine Seite definiert werden.
Performance Module Settings - Seiten und Moduleinstellungen werden nun gecached :).
Module Definitions/Installer Module Definitions - Die Moduledefinition kann nun direkt durch eine *.dnn Datei angelegt werden, ohne eine Zip-Datei hochzuladen.
Other SMTP Settings - Die Portnumber kann angepasst werden New Modules - Store, Forums, Blogs, Gallery sind nun Bestandteil der aktuellen Version.
Eigentlich bin ich von den Controls des .NET Frameworks 2.0 sehr zufrieden und der Umfang der enthaltenen Controls ist durchaus brauchbar. Aber gerade bin ich doch ein wenig entäuscht, da es die Jungs wohl nicht geschafft, übersehen bzw. nicht an eine sehr simple doch oft gebraucht Funktionalität zu denken.
Ich habe eine Windowsforms-Anwendung auf der sich ein Multiline-Textfeld befindet. Jeder (oder sagen wir lieber viele) Benutzer sind es gewohnt den gesamte Inhalt eines solchen Textfeldes mit Strg-A zu markieren... doch was ist das? Nichts rührt sich, nichts wird markiert :(
Nach einer recherche im Web musste ich wohl feststellen, dass ich nicht zu blöd bin, sondern dieses Problem wohl auch bei anderen existiert. Also beleibt einem nichts übrig als diese Enterprise-Funktionalität selber zu implementieren... das würde dann so aussehen:
if ( (e.Control) && (e.KeyCode==Keys.A)) { this.myTextbox.SelectAll(); }
Mahlzeit!
Manchmal ist erwünscht, dass der Benutzer nur eine Instanze der Anwendung starten kann. Hier ein Code-Beispiel wie man dieses Verhalten über ein Mutex erreichen kann:
[STAThread] static void Main() { Application.EnableVisualStyles(); Application.DoEvents(); // ensure one instance running if(!MainForm.InstanceExists) { Application.Run(new MainForm()); } } static Mutex mutex; static string AppGuid = "4B3DF7C2-1BD3-7d54-AFF0-A3D4656C5EDH"; static bool InstanceExists { get { bool notExists; mutex = new Mutex( false, "Local\\" + AppGuid, out notExists ); return !notExists; } }
Durch die Verwendung einer Guid wird hier die "Einmaligkeit" gewährleistet. Sollte die Anwendung auf einem Terminalserver laufen dann muss "Local\\" gegen "Global\\" ersetzt werden....
In .NET 2.0 ist das Arbeiten mit Threads dank des Backgroundworkers deutlich einfacher geworden. Heute Abend habe ich mich mal damit beschäftigt in einem Backgroundworker einen Treeview zu füllen. Der Zugriff auf ein Control in der Methode DoWork() ist nämlich nicht möglich, aber es gibt einen Trick, dieses zu lösen.
Zunächst einmal die Basics um einen Backgroundworker bereitzustellen:
this.backgroundworker = new BackgroundWorker(); this.backgroundworker.WorkerReportsProgress = true; this.backgroundworker.DoWork += new DoWorkEventHandler(backgroundworker_DoWork); this.backgroundworker.ProgressChanged += new ProgressChangedEventHandler(backgroundworker_ProgressChanged); this.backgroundworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundworker_RunWorkerCompleted);
Wichtig dabei ist lediglich das Event ProgressChanged zu implementieren und die Property WorkerReportsProgress auf "true" zu setzen. Bis hier noch nicht wirklich aufregend....auch der Auruf mit
this.backgroundworker.RunWorkerAsync();
ist wie gewohnt.
In der Methode DoWork() kommt dann aber der Trick...
void backgroundworker_DoWork(object sender, DoWorkEventArgs e) {
TreeNode treenode= new TreeNode(); treenode.Text = 'name'; this.backgroundworker.ReportProgress(0, treenode); }
Hier jetzt einfach mal so viel Vorstellungskraft aufbringen als würde DoWork() eine Schleife, Datenbankabfrage oder ähnliches durchführen, die viel Zeit in Anspruch nimmt. Die Möglichkeit eine Fortschritt zu Reporten kann man nun nutzen um ein Object an eine Form weiterzugeben. Die Implementierung der Methode/Event ProgessChanged könnte dann so aussehen:
void backgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.tvNavigator.BeginUpdate(); this.tvNavigator.Nodes.Clear(); this.tvNavigator.Nodes.Add((TreeNode)e.UserState); this.tvNavigator.EndUpdate(); }
Mit diesem Trick ist es nun möglich einen TreeView asyncron zu füllen....
In der Mittagspause noch mit einem Bekannte über DNN diskutiert und das es in der letzten Zeit doch recht still geworden ist .... musste ich gerade feststellen das das Core-Team von DotNetNuke heute das neue Release 3.3.0 zum Download frei gegeben hat.
Leider komme ich in den nächsten nicht dazu diese Version zu testen aber in den Foren gibt es dazu bestimmt jede Menge zu lesen - oder auch nicht, wenn alles ohne Probleme verläuft. Es bleibt aber dabei: Zuerst die Installationsanweisung von DotNetNuke lesen und auf jeden Fall eine komplette Datensicherung von Datenbank und Dateisystem durchführen.
Die deutsche Übersetzung gibt es wie immer unter Sebastian Leupold.
Eine wirklich sehr coole Vorstellung der WCF (Windows Communication Foundation) gibt es derzeit als Folge von dotnetPro-TV. Zusammen mit Ralf Westphal gibt Christian Weyer einen super Überblick für Einsteiger und Neulinge in diesem Bereich.
Derzeit gibt es das Video sogar für die Öffentlichkeit zugänglich auf der Seite von DotNetPro, der Link zum Video ist in der orangefarbenen Kästchen.
Viel Spaß :)
Nachdem es für ASP.NET, Windowsforms und Windows Communication Foundation Community und Informatonsseiten im Netz existieren ist seit kurzem auch eine Seite für den IIS (Internet Information Server) online. Wie bei all den anderen Communityseiten gibt es auch hier ein Forum, einen Downloadbereich mit Tools und jede Menge Tips & Tricks.
www.iis.net
Habe gerade eine OpenSource Library/Komponente entdeckt, die den Zugrif auf die Messanagerplattform von MSN ermöglicht. In manchen Anwendungsszenarien macht es durchaus Sinn, einen Messanger als Kommunikationsweg einzusetzen z.B. bei kritischen Fehlermeldungen, Meldungen das ein asyncroner langer Prozess beendet ist, usw.
Wer dafür den MSN Messanger nutzen möchte aber die Anbindung nicht selber programmieren mag, sollte sich folgende Library anschauen: http://www.xihsolutions.net/dotmsn/index.html
Der SQL Server 2005 unterstützt nun eine deutlich bessere Fehlerbehandlung unter T-SQL - ein Try/catch Block erlaubt ein Errorhandling. Die Handhabung ist eigentlich wie bei allen Try/catch-Blöchen die man z.B. aus C#, VB.NET oder Delphi kennt.
Hier ein klassisches Beispeil - divison durch 0:
BEGIN TRY DECLARE @X INT -- Divide by zero to generate Error SET @X = 1/0 END TRY BEGIN CATCH
SELECT ERROR_NUMBER() ERNumber, ERROR_SEVERITY() Error_Severity, ERROR_STATE() Error_State, ERROR_PROCEDURE() Error_Procedure, ERROR_LINE() Error_Line, ERROR_MESSAGE() Error_Message END CATCH
Als Ergebnis bekommt man:
Error Detected Err_Num Err_Sev Err_State Err_Proc Err_Line Err_Msg ------- ------- --------- -------------------- --------- -------------------------------- 8134 16 1 NULL 4 Divide by zero error encountered.
Ziemlich cool und bei complexen SQL-Statements kann ddas sehr hilfreich sein!
Gerade habe ich eine doch sehr cooles .NET Opensource Projekt auf sourcefroge.net gefunden. Dabei handelt es sich um eine in C# geschriebene Komponenten die eine Dockingfunktionalität bietet. Vorlage war dabei wohl das Visual Studio.net 2005.
Für so manche .NET Windows Anwendung dürfte das eine wirkliche Bereichung sein. Hier geht es zu der Komponente: Link
Wer mal "eben" ein Regex-Pattern erstellen möchte und dieses auch direkt testen will, kann ein kleines Online-Tool dafür nutzen. Hier der Link
Ich kann es ja kaum glauben aber bei dem mächtigen Framework von .Net wird es echt kompliziert eine DateTime Wert von GMT in PST zu konvertieren. Habe gerade ein paar lustige Stunden damit verbraucht eine dieses einigermaßen sauber zu implementieren und hier meine Lösung:
Basis für meine Lösung ist eine Klasse von Clemens Vasters und seiner Blogengine dasblog.net. Dort gibt es im Projekt "newtelligence.DasBlog.Util.csproj" die Klasse "TimeZones.cs". Die nötigen Informationen für eine Konvertierung wird hier allerdings in einer XML Datei gespeichert. Da ich derzeit allerdings nur eine konvertierung nach PST brauch habe ich die XML Datei ignoriert und setze die Daten manuell in einer Klasse.
/// <summary> /// Liefert verschiedne Zeiten auf der Welt zurücl /// </summary> public class TimeZoneConverter {
/// <summary> /// (GMT-08:00) Pacific Time (US & Canada); Tijuana /// Pacific Daylight Time /// </summary> /// <returns>DateTime of Pacific Daylight Time</returns> public static DateTime GetCurrentPST() { return DateTime2PST(DateTime.Now) ; }
/// <summary> /// (GMT-08:00) Pacific Time (US & Canada); Tijuana /// Pacific Daylight Time /// </summary> /// <returns>DateTime of Pacific Daylight Time</returns> public static DateTime DateTime2PST(DateTime dtDate) {
WindowsTZI pstTZI = new WindowsTZI(); pstTZI.bias = 480; pstTZI.daylightBias = -60; pstTZI.standardBias = 0; WindowsSystemTime standardDate = new WindowsSystemTime(); standardDate.year = 0; standardDate.month = 10; standardDate.day = 5; standardDate.dayOfWeek = 0; standardDate.hour = 2; standardDate.minute = 0; standardDate.second = 0; standardDate.milliseconds = 0;
WindowsSystemTime daylightDate = new WindowsSystemTime(); daylightDate.year = 0; daylightDate.month = 4; daylightDate.day = 1; daylightDate.dayOfWeek = 0; daylightDate.hour = 2; daylightDate.minute = 0; daylightDate.second = 0; daylightDate.milliseconds = 0;
pstTZI.standardDate = standardDate; pstTZI.daylightDate = daylightDate;
WindowsTimeZone tz = new WindowsTimeZone(); tz.WinTZI = pstTZI; return tz.ToLocalTime ( dtDate.ToUniversalTime() ) ;
}
}
Wer das ganze etwas dynamischer haben möchte schaut sich am beste die Klasse vom Clemens an ...
Ich brauchte diese Funktion für die Google-Adwords-API, diese erwatet nämlich das Datum im PST Format.
Gerade habe ich einen interessanten Artikel gefunden der zeigt, wie man einen Parameter aus der Querystringauflistung entfernen kann.
Public Sub RemoveQueryString(ByVal Req As HttpRequest, ByVal strKeyToDel As String, ByVal http_Context As HttpContext) Dim nvc As System.Collections.Specialized.NameValueCollection = New System.Collections.Specialized.NameValueCollection(Req.QueryString) Dim sPage As String = Req.ServerVariables("SERVER_NAME") & Req.ServerVariables("SCRIPT_NAME") nvc.Remove(strKeyToDel)
Dim strNewURL As String = Req.Path Dim sSeparator As String = "?" Dim sKey As String For Each sKey In nvc If sKey <> Nothing Then Dim sValues As String() = nvc.GetValues(sKey) Dim sValue As String For Each sValue In sValues If sValue <> Nothing Then strNewURL &= sSeparator strNewURL &= sKey & "=" & sValue sSeparator = "&" End If Next End If Next If nvc.Keys.Count < 1 Then strNewURL &= "?" End If http_Context.Current.RewritePath(strNewURL) End Sub
Gefunden habe ich den Code unter http://www.codeproject.com
Eines meiner größten Probleme - oder sagen wir lieber Sorgen - war bisher, dass DNN keine Transaktionen unterstützt. Es gibt doch immer wieder mal Business-Cases wo man transaktionsicher Daten verarbeiten möchte. Ein typischens Beispiel wäre z.B. eine Online-Shop, wenn es um die Bestellung und gleichzeitige Zahlungsabwicklung geht.
Da DotNetNuke in vermutlich in vielen Fällen auf einem Windows 2003 Server betrieben wird, ist die komplette Umgebung schon vorhanden um die Abläufe transaktionssicher zu gestalten. Das Stichwort ist hier "Services without Components". Der Begriff "Services without Components" steht für die Nutzung von COM+-Dienste in .NET-Anwendung ohne Registrierung als Serviced Component (COM+-Anwendung).
Klassen: .ServiceConfig and System.EnterpriseServices.ServiceDomain
Das folgende Beispiel zeigt eine ganz einfache Implementierung innerhalb einer Methode:
//Hier wird mit einem Transaktionskontext gearbeitet, //damit das Handling einfacher wird ServiceConfig sc = new ServiceConfig(); sc.Transaction = TransactionOption.RequiresNew ; ServiceDomain.Enter ( sc ) ;
try { //mach was .... ContextUtil.SetComplete() ;
} catch (Exception exc) { ContextUtil.SetAbort() ; throw exc ; } finally { ServiceDomain.Leave(); }
Seit einigen Tagen existiert ein eigenes Informations-Portal für IT-Architekten (www.architectsconnection.de). Dort stehen sehr viele architekturrelevanten Ressourcen – Fachartikel, Kommentare, Web- und PodCasts, Vorträge, Event-Termine u.v.m zur Verfügung und soll bei den täglichen Aufgaben helfen und unterstützen.
Der Gedanke des Portals ist "Service von Architekten für Architekten". Schauen wir mal wie sich dieses Microsoft Portal entwickelt.
Die ersten Erfahrungen mit dem .NET Framework 2.0 waren sehr erfreulich. Als eines der ersten Features habe ich mir die "Anwendungseinstellungen" vorgenommen und war total begeistert. Endlich wird einem diese lästige Aufgabe komplett abgenommen auch das Binden von Einstellunge auf Eigenschaften eines Controls finde ich super! Nur noch ein Load() und Save() in den Code einbauen und fertig. So dachte ich zumindest aber der Frust kam relativ schnell:
Das Speichern von benutzerabhängigen Einstellungen wird unter c:\Dokumente und Einstellungen\benutzername\.... vorgenommen. So weit kein Problem nur das die Versionsnummer mit im Pfad stehen muss finde ich richtig übel. So bald sich die Versionnummer ändert ist es mit den alten Einstellungen essig ... es gibt zwar eine Upgrade() Methode aber bei der Verwendung von SettingKey funktioniert das nicht mehr :(
Also habe ich versucht den Pfad anzupassen und habe gegoogelt .. siehe da: Es geht nicht .... super gemacht, toll durchdacht aber dann trotzdem leider auf den letzten Metern versaut. Immerhin hat man die Möglichkeit einen eigenenen Provider zu schreiben, um die Einstellungen abzuspeichern. Das wiederum ist klasse gelöst und mehr als sinnvoll!
Nun hier ist eine Provider, der die Einstellungen im XML-Format unter dem Anwendungsverzeichnis speichert:
using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Configuration; using System.Windows.Forms; using System.Xml; using System.Collections.Specialized; using Microsoft.Win32;
namespace GaliNeo.Framework.GaliNeoSettings { class GaliNeoSettingsProvider : SettingsProvider { XmlDocument doc = null; XmlNode root = null; XmlNode userSettings = null; XmlNode appSettings = null;
public GaliNeoSettingsProvider() { doc = new XmlDocument(); LoadSettings(); }
public override string ApplicationName { get { return Application.ProductName; } set { } }
public override void Initialize(string name, NameValueCollection col) { base.Initialize(this.ApplicationName, col); }
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals) {
foreach (SettingsPropertyValue propval in propvals) {
SettingsProperty prop = propval.Property;
setValue(prop, propval, context);
}
this.WriteSettings(); }
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection props) {
// Create new collection of values SettingsPropertyValueCollection values = new SettingsPropertyValueCollection(); string sectionName = this.GetSectionName(context); string settingKey = (string)context["SettingsKey"];
// Iterate through the settings to be retrieved foreach (SettingsProperty setting in props) { SettingsPropertyValue value = new SettingsPropertyValue(setting); value.IsDirty = false; value.SerializedValue = getValue(setting, context );
values.Add(value); } return values;
}
private XmlNode SerializeToXmlElement(SettingsProperty setting, SettingsPropertyValue value) {
XmlElement element = doc.CreateElement("value"); string text1 = value.SerializedValue as string; if ((text1 == null) && (setting.SerializeAs == SettingsSerializeAs.Binary)) { byte[] buffer1 = value.SerializedValue as byte[]; if (buffer1 != null) { text1 = Convert.ToBase64String(buffer1); } } if (text1 == null) { text1 = string.Empty; } if (setting.SerializeAs == SettingsSerializeAs.String) { //text1 = this.Escaper.Escape(text1); }
element.InnerXml = text1; XmlNode node1 = null; foreach (XmlNode node2 in element.ChildNodes) { if (node2.NodeType == XmlNodeType.XmlDeclaration) { node1 = node2; break; } } if (node1 != null) { element.RemoveChild(node1); } return element; }
private string GetSectionName(SettingsContext context) { string groupName = (string)context["GroupName"]; string settingKey = (string)context["SettingsKey"]; string section = groupName; if (!string.IsNullOrEmpty(settingKey)) { object[] arrName = new object[] { section, settingKey }; section = string.Format("{0}.{1}", arrName); } return XmlConvert.EncodeLocalName(section); }
#region DAN
private string getXPathQueryProperty(SettingsProperty setting, SettingsContext context) { string xPathQuery = getXPathQuerySection(setting, context);
if (setting.Name != "") { xPathQuery += "/" + setting.Name; } //if (propName != "")
return xPathQuery; }
private string getXPathQuerySection(SettingsProperty setting, SettingsContext context) { string xPathQueryGroup = doc.DocumentElement.LocalName ;
if (this.IsUserScoped(setting)) { xPathQueryGroup += "/userSettings"; } else { xPathQueryGroup += "/appSettings"; } xPathQueryGroup += "/" + GetSectionName(context);
return xPathQueryGroup;
}
private void CreatePropNode(SettingsProperty setting, SettingsPropertyValue value, SettingsContext context) { string xPathQuery = getXPathQuerySection(setting, context);
XmlNode groupNode = doc.SelectSingleNode(xPathQuery);
if (groupNode == null) { groupNode = doc.CreateElement(GetSectionName(context));
if (this.IsUserScoped(setting)) { userSettings.AppendChild(groupNode); } else { appSettings.AppendChild(groupNode); }
} //if (node == null)
XmlNode nodeProp = doc.CreateElement(setting.Name);
nodeProp.AppendChild (this.SerializeToXmlElement(setting,value) );
groupNode.AppendChild(nodeProp);
}
private void setValue(SettingsProperty setting, SettingsPropertyValue value, SettingsContext context) //string propName, string groupName, object value, object defaultvalue) {
string xPathQuery = getXPathQueryProperty(setting,context);
XmlNode node = doc.SelectSingleNode(xPathQuery); if (node != null) { XmlNode valueNode = SerializeToXmlElement(setting, value); node.RemoveAll(); node.AppendChild(valueNode); } else {
CreatePropNode(setting, value, context);
} //if (node != null)
}
private object getValue(SettingsProperty setting, SettingsContext context) { string xPathQuery = getXPathQueryProperty(setting, context);
XmlNode node = doc.SelectSingleNode(xPathQuery + "/value");
if (node != null) { return node.InnerXml; } else if (setting.DefaultValue != null) { return setting.DefaultValue; } else { return null; }
}
// Helper method: walks the "attribute bag" for a given property // to determine if it is user-scoped or not. // Note that this provider does not enforce other rules, such as // - unknown attributes // - improper attribute combinations (e.g. both user and app - this implementation // would say true for user-scoped regardless of existence of app-scoped) private bool IsUserScoped(SettingsProperty prop) { foreach (DictionaryEntry d in prop.Attributes) { Attribute a = (Attribute)d.Value; if (a.GetType() == typeof(UserScopedSettingAttribute)) return true; } return false; }
#endregion
private void LoadSettings() { string configFile = getPath() + "user.config";
if (System.IO.File.Exists(configFile) ) { doc.Load(configFile); root = doc.DocumentElement; userSettings = doc.DocumentElement.SelectSingleNode("./userSettings"); appSettings = doc.DocumentElement.SelectSingleNode("./appSettings"); ;
} else {
try { root = doc.CreateElement("configuration");
userSettings = doc.CreateElement("userSettings"); root.AppendChild(userSettings); appSettings = doc.CreateElement("appSettings"); root.AppendChild(appSettings);
doc.AppendChild(root); } catch {
} }
}
private string getPath() { return Application.StartupPath + @"\App_Data\" ; }
private void WriteSettings() { if (!( System.IO.Directory.Exists(getPath()))) { System.IO.Directory.CreateDirectory(getPath()); } string configFile = getPath() + "user.config";
doc.Save(configFile);
}
} }
Nun das ist noch nicht 100% best practice Entwicklung (musste schnell gehen) aber es funktioniert ....:)
|