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 ....:)
Bevor ich aber mit .NET 2.0 so richtig loslege hier noch ein kleiner Codebeitrag. Man hat ja immer wieder die Anforderung Daten in CSV zu exportieren (im Zeitalter von XML für mich unverständlich aber gut ). Dafür veröffentliche ich jetzt das fünftausenste Code-Beispiel.
Zunächst habe ich eine allgemeine Basisklasse geschrieben, die auch für weiter Exportformate genutzt werden kann:
using System; using System.Data; using System.Web; using System.Text; using System.IO;
namespace GaliNeo.Framework { /// <summary> /// Zusammenfassung für BaseConverter. /// </summary> public abstract class BaseConverter { public BaseConverter() { }
/// <summary> /// Export DataTable to http stream /// </summary> /// <param name="oDataTable"></param> /// <param name="response"></param> /// <param name="sFileName"></param> public void ExportToResponseStream(DataTable oDataTable, System.Web.HttpResponse response, string sFileName) { response.Clear() ; response.ClearHeaders(); response.ClearContent(); response.Buffer = true; //Very importent because "ü", "ä", usw....
response.ContentEncoding = System.Text.Encoding.Default ; response.ContentType = GetHTTPContentType() ; response.AppendHeader("content-disposition", "attachment; filename=" + HttpUtility.UrlEncode(sFileName, System.Text.Encoding.UTF8)) ;
response.Write( ConvertDataTable(oDataTable).ToString() ) ; //close http response response.End();
}
/// <summary> /// Export datatable to file /// </summary> /// <param name="oDataTable"></param> /// <param name="sFileName"></param> /// <returns></returns> public bool ExportToFile(DataTable oDataTable, string sFileName ) { try { string fullpath = System.IO.Path.GetFullPath( sFileName ) ; if (System.IO.Directory.Exists ( fullpath )) { System.IO.Directory.CreateDirectory( fullpath ) ; }
StreamWriter SW; SW=File.CreateText(sFileName); SW.WriteLine(ConvertDataTable(oDataTable).ToString()); SW.Close(); return true ; } catch { return false ; }
}
protected abstract StringBuilder ConvertDataTable(DataTable oDataTable) ; protected abstract string GetHTTPContentType() ;
} }
Die Klasse ermöglicht es eine Stream entweder auf Festplatte zu schreiben oder aber in den HTTP Responsestream.
Die passende CSV-Klasse ist relativ simple und kein wirkliches Geheimnis:
using System; using System.Data; using System.IO; using System.Text;
namespace GaliNeo.Framework { /// <summary> /// Summary description for CSVConvertor. /// </summary> public class CSVConverter : BaseConverter {
protected override string GetHTTPContentType() { return "text/csv"; }
/// <summary> /// To generate CSV file. /// </summary> /// <param name="oDataTable"></param> /// <param name="directoryPath"></param> /// <param name="fileName"></param> /// <returns></returns> protected override StringBuilder ConvertDataTable(DataTable oDataTable) {
StringBuilder oStringBuilder = new StringBuilder();
// Start, Creating column header
foreach(DataColumn oDataColumn in oDataTable.Columns) { oStringBuilder.Append(oDataColumn.ColumnName + ","); oStringBuilder.Append( System.Environment.NewLine ) ; }
//End, Creating column header //Start, Creating rows
foreach(DataRow oDataRow in oDataTable.Rows) { foreach(DataColumn oDataColumn in oDataTable.Columns) { oStringBuilder.Append(oDataRow[oDataColumn.ColumnName] + ","); }
oStringBuilder.Append( System.Environment.NewLine ) ; }
//End, Creating rows
return oStringBuilder;
} } }
Fertig!
Ab Heute wird die Thematik dieses Blogs ein wenig erweitert. Nun ist es endlich so weit und di Entwicklung mit .NET 2.0 geht bei mir endlich los. In der nächsten Zeit wird der Schwerpunkt mehr auf .NET Winforms und der Google-API liegen.
Aber keine Angst ich werde DotNetNuke nicht wirklich vernachlässigen.
Auch wenn ich es nie gedachte hätte, habe ich heute eine Spammail bekommen die ich doch recht interesannt oder viel mehr sinvoll fand.
Inhalt war eigentlich die Seite http://www.rekruter.de/ eine Seite mit Stellenangebote der Arbeitsargentur. Ob der Staat jetzt auch schon Spammails gegen die Arbeitslosigkeit einsetzt
Heute hat das Coreteam von DotNetNuke ein weiters Modul frei gegeben. Diesmal handelt es sich um ein Modul das eine Shopfunktionalität abbildet. Bei der Installation sollte man darauf achten das man nicht das Module "Categories" von efficionconsulting installiert hat, denn sonst kommt es zu Konflikten bei der Namensgebung. Diese Hürde einmal genommen ist die weiter Installation kein Problem.
Die Funktionalität ist ziemlich Low-Level und an einige Stellen gibt es noch Fehlermeldungen (z.B. bei den Einstellungen). Es gibt aber ein paar ganz gute Ansätze und ich denke das im Laufe der Zeit dieses Modul noch an Funktionsumfang zunimmt. In den nächsten Tagen werde ich wohl auch noch mal in den Source reinschauen ... so einfach aus neugier.
Für mich selber ist dieses Modul eher uninteressant, da mein Shop so langsam die Version 1.0 ereicht hat; auch wenn er laut Zeitplanung schon längst fertig sein sollte .. na ja es kommt halt immer anders als man denkt. Mittlerweile ist die Featureliste schon sehr lang - erspare mir diese aber jetzt. Eines der wichtigsten Merkmale ist aber wohl, dass alle öffentlichen Bereiche über Templates pro Portal dynamisch anpassbar sind. Ebenso ist der Bestellprozess über eine XML Workflowbeschreibung komplett frei definierbar. Anpassbarkeit und Konfigurierbakeit ist bei der Implementierung sehr wichtig gewesen. Das erste Projekt wird vermutlich nächste Woche live gehen aber dann nur für eine bestimme Gruppe von Personen. Wer trotzdem mal mehr darüber wissen möchte, kann sich ja bei mir melden. Was mit dem Shop passiert ist noch nicht ganz klar.
Microsoft stellt im Downloadbereich das IIS Diagnostic Toolkit zur Verfügung. Das ist eine Sammlung von Werkzeugen, die bei der Fehlersuche rund um den IIS (Internet Information Sevices) helfen sollen. Das Toolkit enthält folgende sieben Werkzeuge: Authentication and Access Control Diagnostics 1.0, SSL Diagnostics 1.1, SMTP Diagnostics 1.0, Log Parser 2.2, Trace Diagnostics 1.0, WFetch 1.4 und Debug Diagnostics 1.0.
Hier geht es zum Download
Eines der doch besten Argumente für die Linux- bzw. PHP-Welt im Webumfeld waren bis vor einiger Zeit immer noch die doch sehr teuren Hostingpakete von Windowsbetriebssystemen. Mittlerweile fallen die Preise ständig und sind auf einem Niveau, wie es auch im Linuxumfeld anzutreffen ist.
Das ist besonders für Freunde und Fans von DotNetNuke interessant! Es gibt zwar auch einige Anbieter die ein DotNetNuke Hosting für z.B. 10€ anbiete aber wer gerne Kontrolle über seine System hat sollte sich überlegen einen eigenen Server zu mieten.
Nicht nur das man dort der eigene Administrator ist .. man ist bei vielen Punkten nicht auf den Support seinen Providers angewiesen.
Ganz davon abgesehen, dass man auch weiter Anwendungen auf seinem Server unterbringen kann.
Ich habe nun schon seit über einem Jahr einen Server bei Strato stehen und bin mit dem Service sehr zufrieden. Wer also ein paar Euro mehr ausgibt bekommt dafür auch deutlich mehr geboten. Ich persönlich möchte das nicht mehr missen. Immerhin läuft auch mein Block darüber und ein paar weitere Anwendungen.
Weil ich das Angebot sehr gut finde habe ich oben mal einen Banner eingebaut - wer also einen Buchen will .. einfach klicken :)
|