Lokale Daten mit Hilfe von Symbolic Links über Dropbox synchronisieren (OSX)

Ich möchte zuerst kurz schildern warum ich überhaupt auf diese Idee gekommen bin, bzw. was mich dazu bewegt hat mir über eine solche Lösung Gedanken zu machen.

Ich nutze Lync auf meinen Mac’s um mit einem meiner größeren Kunden täglich in Kontakt bleiben zu können.

Lync für den Mac kann die Chatprotokolle speichern, dies geschieht jedoch in einem fest mit Lync verdrahteten Verzeichnis und kann nicht geändert werden.
Die Protokollierung findet einfach in Textdateien statt die in einer festen Verzeichnisstruktur abgelegt werden.

Mein Anforderung besteht darin, dass ich die Chat Protokolle ohne davon abhängig zu sein auf welchem Rechne der Chat stattgefunden hat immer im Zugriff auf allen Rechner vorhanden sein soll (ich setze ingesamt 3 verschieden Mac Rechner ein).

Eigenlich ist das der klassiche Fall für Tools wie Dropbox, OneDrive oder ähnliches.

Ich hätte übrigens auch eine Lösung einfach so einsetzen können, „Sugarsync“, denn Sugarsync ermöglicht als einziges mir bekanntes Produkt dieser Art die Synchronisation von beliebigen lokalen Verzeichnissen und nutzt nicht wie Dropbox oder OneDrive ein eigenes Dediziertes Verzeichnis zur Synchronisation.

Da ich mich aber vor einigen Monaten dazu entschlossen habe, Dropbox als mein primäres Tool einzusetzen musste ein Lösung her die mit Dropbox funktioniert.

Nun aber genug der Erklärung, kommen wir zur Lösung.

1) Zuerst gilt es von einem Rechner auf dem ich Lync installiert und die Protokollierung angeschaltet habe einen Symbolic Link in Dropbox anzulegen. Dadurch werden Dateien die im Sync History Verzeichnis erstellt werden automatisch in Dropbox gesichert.

Lync legt die Daten im folgenden User Verzeichnis ab:

/Users/{Username}/Documents/Microsoft-Benutzerdaten/Microsoft Lync History/{mail@domain.de}

Um einen Symbolic Link anzulegen öffnet man ein Terminal Fenster. Navigiert sich mit cd in das Dropbox Verzeichnis um dann dann den Symbolic link durch eingabe des nachfolgenden Befehls anlegt:

ln -s {vollständiger Pfad zu den Chat Protokollen} {Name des Link}

Konkretes Beispiel:
ln -s /Users/hp/Documents/Microsoft-Benutzerdaten/Microsoft Lync History/hp@kundendomain.de lyncchatlink

Nun haben wir schon mal erreicht dass die lokal gespeicherten Chat Protokolle in einem Dropbox Ordern landen.

Auf dem Rechner, auf dem ich das eingerichtet habe, liegt nun tatsächlich im lokalen Dropbox Verzeichnis ein Symbolic Link der auf das lokale History Verzeichnis verweist.

Da Dropox aber Symbolic Links nicht kennt, ist das für Dropbox einfach nur ein Verzeichnis.

Wenn ich nun auf einem andern Rechner diesen Ordner mit dem Symbolic Link synchronisiere wird dort einfach ein Verzeichnis angelegt (und auch die darin enthaltenten Chat Protokolle).

Damit habe ich aber noch nicht erreicht, dass Chat Protokolle die mit diesem zweiten Rechner erzeugt werden auch auf dem ersten Rechner im History Verzeichnis von Lync landen und auch nicht umgekehrt.

Eigentlich müsste man nun auf dem zweiten Rechner den gleichen Symbolik link anlegen damit dieser auch auf das Dropbox Verzeichnis zeigt.

Das Problem dabei ist nur.
Ein Symbolic link kann nur angelegt werden wenn es noch keinen Eintrag mit dem gleichen Namen gibt.

Ich kann also auf dem zweiten Rechner keinen Symbolik link anlegen da ja bereits ein Verzeichnis mit dem Namen lyncchatlink vorhanden ist.

Aber es gibt einfache Abhilfe:
Dazu schalten wir auf dem zweiten Rechner einfach die Dropbox Synchronsation aus.

Dann benennen wir auf diesem Rechner das Verzeichnis (eigentlich den Symbolik Link von Rechner 1) in Dropbox von lyncchatlink zum in lyncchatlink_old (oder jeden anderen Namen) um.
Nun erstellen wir auf diesem zweitren Rechner den gleichen Symbolik Link wie vorher auf dem ersten Rechner.

Danach verschieben (oder kopieren) wir alle Daten aus dem Verzeichnis lyncchatlink_old in das neue Verzeichnis lyncchatlink und löschen anschließend das Verzeichnis lyncchatlink_old.

Nun aktivieren wir die Synchrosiation von Dropbox, fertig.

Das funktioniert natürlich nicht nur bei den Chatprotokollen von Lync sondern bei jeder Art von Dateien die man über Dropbox zwischen verschiedenen Rechner austauschen will.

Und nun viel Spaß mit dieser Lösung.

Fritz!Box – Wenn mehr drinsteckt als außen drauf steht (LTE Fallback inklusive)

Letzte Woche suchte ich nach einer Lösung, um in einem Geschäft die Internet Versorung, auch dann zu gewährleisten, wenn der DSL Anschluß ausfällt.

Das war zwingend notwendig geworden, da es sich bei dem Geschäft um ein Cafe handelt, dass auf meine Empfehlung hin, nun mit einer, in der Cloud gehosteten Software sein Tagesgeschäft erledigt.
Der Ausfall der DSL Leitung würde bedeuten, mann müsste die Blöcke und Stifte raus nehmen und wieder alles per Hand machen, was, wenn der Laden richtig voll ist (was sehr häufig so ist), eigentlich nicht zu schaffen ist,

Nun aber genug des geschwafels, jetzt geht es an die Erläuterung der technisches Umsetzung.

Vorab muss ich aber noch erwähnen, das wir uns als „Endlösung“ für einen Hybrid-Anschluß der Telekom entschieden haben, der uns genau die Hohe Verfügbarkeit für unseren Internet Zugang „out of the box“ liefert die wir hier benötigen.

Aber, um die Internet Versorung sicherzustellen, bis der Hybrid-Anschluß funktioniert und vor allem auch an dem Tag, an dem der Hybrid Anschluß geschaltet wird und wir an dem Tag ganz sicher keinen funktionierenden DSL Anschluß haben werden, musste erst einmal eine Lösung her.

Ich hatte meine Kollegen über Twitter gefragt ob Sie irgendewelche Lösungen für mich hätten und habe auch einige Ideen genannt bekommen.

Unter anderem war da auch der Dualband Wireless-AC1900 Gigabit-Router als eine Lösung genannt worden.

Nachdem nun aber feststand dass wir uns für die Hybrid Lösung der Telekom und damit einhergehend sowieso einen neuen Router bekommen würden kam ein weiterer neuer Router für kurze Zeit eigentlich nicht in Frage.

Aktuell wurde dort eine Fritz!Box 7390 betrieben.

Also habe ich mich umgeschaut ob es nicht auch eine Lösung mit der vorhandenen Fritz!Box gibt.

Und siehe da, die Fritz!Box bietet tatsächlich auch eine Lösung an, man sieht diese Option allerdings erst dann, wenn mann auch einen Mobilen Stick per USB an die Fritz!Box anschließt.

In meinem Fall habe ich einen Telekom Speedstick LTE verwendet.

Einfach den Stick (der natürlich mit einer entsprechenden SIM Karte bestückt sein und konfiguriert sein muss) in die Fritz!Box stecken und schon sieht das Menü der Fritz!Box, wie im nachfolgenden Bild dargestellt, um den Menüpunkt „Mobilfunk“ erweitert aus.

Nun wird man noch aufgefordert den PIN der SIM Karte einzugeben und damit ist die Konfiguration eigentlich auch schon abgeschlossen.
(Wer möchte und sich damit sicherer fühlt: Eine detailierte Einrichtungs-Anweisung findet man direkt hier bei AVM )

Anschließend muss man nur noch angeben wann und ob die Mobilfunkverbindung aktiv sein soll und fertig,

Technik und Sicherheit, kann doch so einfach sein.

JetBrains 0xDBE verbindet sich nicht mit SQLExpress

Wer versucht sich auf einer Standardinstallation eines SqlExpress Server mit dem Datenbank Tool 0xDBE von Jetbrains zu verbinden wird leider nicht sofort Erfolg haben.
Es gibt 2 kleine Hürden die man zuerst überwinden muss, die ich hier in diesem Artikel beschreiben möchte.

TCP/IP Protokoll aktivieren

Bei der Standardinstallation eines SQLExpress ist das TCP/IP Protokoll nicht aktiviert, um mit 0xDBE arbeiten zu können muss das TCP/IP Protokoll aktiviert werden.
Hierzu öffnen wir den "Sql Server Configuration Manager" (danach nicht schließen wir brauchen ihn noch einmal), wählen auf der linken Seite den SQL Server aus, welchen wir konfigurieren wollen und aktivieren auf der rechtern Seite das TCP/IP Protokoll (siehe nachfolgende Abbildung).


Wenn die Sache mit dem TCP/IP Protokoll sicherlich noch von vielen als selbstverständlich angesehen wird und bereits aktiviert wurde ist der zweite Schritt sicherlich der entscheidende und nicht so offensichtliche.

SQL Server Browser

Bei einer Standardinstallation des SQLExpress Server wird der Dienst "SQL Server Browser“ installiert, aber weder ativiert noch gestartet.
Um mit 0xDBE eine Verbindung mit einem SQLExpress Server herzustellen ist es aber notwendig dass dieser Dienst läuft.
Hierzu gehen wir wie folgt vor:
Wir öffnen wieder (oder besser noch wir haben ihn noch auf) den "Sql Server Configuration Manager".


Wählen auf der linken Seite die "SQL Server Services" aus und doppelklicken auf der rechten Seite auf den "SQL Server Browser"


Dort wählen wir den Karteireiter „Service" aus und ändern den "Start Mode" von „Disabled" auf „Automatic".
Abschließend starten wir noch den Dienst "SQL Server Browser".

Nun klappt es auch mit 0xDBE und dem SQLExpress!

Google Music Manager – Couldn’t connect to Google Play

Seit einigen Wochen hat der Google Music Manager auf meinem Windows 7 Rechner seinen Betrieb eingestellt.

Da das synchronisieren der Musik in die Google Music Cloud nun nicht zu meinem Haupt Business gehört, war die Priorität zur Lösung des Problems auch nicht ganz so hoch angesiedelt wie das bei Problemen ist, die mich beim erledigen meiner eigentlichen Tätigkeit behindern.

Aber da mich das Problem doch so langsam genervt hat,  habe ich mich auf die Fehlersuche gemacht und bin auch fündig geworden.

Hier zuerst noch einmal kurz das Problem, das von Heute auf Morgen aufgetreten ist:

Beim Start des Music Manager konnte, wie unten zu sehen ist, dieser sich nicht mit Google Music verbinden.

SNAGHTMLf6c2a7

Deinstallieren und neu Installieren bracht auch keine Besserung.

Eine genauere Recherche hat dann ergeben, dass beim Versuch sich bei der Google Music Cloud anzumelden, ein Problem mit den in Windows installierten Root Zertifikaten eingestellt hat.

Ein einfaches Update dieser Zertifikate bringt schon Abhilfe und der Google Music Manager läuft wieder einwandfrei.

Hier der Link zum Update der Zertifikate (Für Windows ):

http://download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/rootsupd.exe

Einfache herunterladen und ausführen.

Und nicht wundern, das Update erfolgt ohne weitere Ausgaben auf dem Bildschirm

Einlesen von “delimited with” Daten – Leicht gemacht mit Hilfe von Custom Attribut

So lange es EDV gibt und wohl auch, so lange es EDV geben wird ist der Austausch von Daten immer wieder ein Thema.

Um Daten zwischen Verschiedenen Systemen auszutauschen sind die Möglichkeiten fast unerschöpflich.

Eine der Möglichkeiten besteht darin Daten in eine Zeichenkennte zu schreiben und die Daten mit einem sogenannten Trennzeichen (Delimiter) zwischen den einzelnen Feldinformationen zu trennen (Auch noch im Zeitalter von Serialisierung und anderen tollen Dingen).

Die meisten Programme können Daten in diesem Format exportieren.

Viel interessanter wird es, wenn man diese Daten in seine eigenen Programmen verarbeiten möchte.

Ich möchte hier eine einfache Möglichkeit zeigen, wie man sich mit ein paar Zeilen Code eine schicke kleine Funktion und ein Custom Attribut schreiben kann die einem dabei behilflich sind, solche Daten einzulesen und diese Daten in Eigenschaften seiner eigenen Klasse zu schreiben.

Hier zuerst das Custom Attribute.  Das bietet schon ein wenig mehr Möglichkeiten als es hier in diesem Beispiel zum Einsatz kommt, aber der Beitrag soll auch nicht eine fertige Lösung darstellen, sondern nur behilflich sein, einen Weg zu zeigen:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ImportFieldAttribute : Attribute
{
	/// <summary>
	/// Feldtype der zu importierenden Daten
	/// </summary>
	private readonly ImportFieldType fieldType;

	/// <summary>
	/// Position im Segment
	/// </summary>
	private readonly int position;

	/// <summary>
	/// Initializes a new instance of the <see cref="ImportFieldAttribute"/> class.
	/// Für Import von Sätzen mit Trennzeichen
	/// </summary>
	/// <param name="fieldType">Type of the field.</param>
	/// <param name="position">The position.</param>
	/// <param name="length">The length.</param>
	public ImportFieldAttribute(ImportFieldType fieldType, int position)
	{
		this.fieldType = fieldType;
		this.position = position;
	}

	/// <summary>
	/// Feldtype der zu importierenden Daten
	/// </summary>
	public ImportFieldType FieldType
	{
		get
		{
			return this.fieldType;
		}
	}

	/// <summary>
	/// Position im Segment
	/// </summary>
	public int Position
	{
		get
		{
			return this.position;
		}
	}
}

Schauen wir uns nun den Code an der sich mit Hilfe des Custom Attribute an die Daten ran macht.

public static void ImportClassFieldsFrom(object target, string buf, char delimiter)
{
	var fields = buf.Split(delimiter);

	const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
	var targetProperties = target.GetType().GetProperties(Flags);
	try
	{
		foreach (PropertyInfo propertyInfo in targetProperties)
		{
			var attribs =
				(ImportFieldAttribute[])propertyInfo.GetCustomAttributes(typeof(ImportFieldAttribute), true);

			if (attribs.Length > 0)
			{
				ImportFieldAttribute attrib = attribs[0];
				if (attrib.Position <= fields.Length)
				{
					var temp = fields[attrib.Position];
					propertyInfo.SetValue(target, temp.TrimEnd(), null);
				}
			}
		}
	}
	catch (Exception ex)
	{
		Exceptions.Log.LogError(buf, ex);
	}

}

Und so verwendet man diese Methode und das Custom Attribute:

Bei der Definition der Klasse, welche die Informationen aus dem “Delimited width” Zeichenkette aufnehmen soll, wende ich nun das Custom Attribute wie folgt an:

public class Palettes
{
	/// <summary>
	/// Gets or sets GrossWeight.
	/// </summary>
	[ImportField(ImportFieldType.Char, 1)]
	public string GrossWeight { get; set; }

	/// <summary>
	/// Gets or sets Height.
	/// </summary>
	[ImportField(ImportFieldType.Char, 2)]
	public string Height { get; set; }

	/// <summary>
	/// Gets or sets Lenght.
	/// </summary>
	[ImportField(ImportFieldType.Char, 3)]
	public string Lenght { get; set; }
}

Um die Klasseneigenschaften meiner Klasse mit den Werten aus der “Delimited width” Zeichenkette zu bekommen wird folgender Methodenaufruf durchgeführt:

var palette = new Palettes());
AttributHelper.ImportClassFieldsFrom(palette, "12,00~15,01~23,00", '~');

Fragen, Anregungen und Kritik wie immer, einfach als Kommentar

Fritzbox 7390 – Nortel VPN Client – VPN tunnel is disconnected due to routing table change

Nachdem ich vor einigen Wochen in meinem Homeoffice einen DSL Speedport von der Telekom gegen eine Fritzbox 7390 ausgetauscht habe, kam es bei der Verwendung der Nortel VPN Client Software zu ständigen Verbindungsabbrüchen.

imageDa ich dieses Verhalten kurz nach dem Einsatz der Fritzbox festgestellt habe, lag die Vermutung, dass es mit der Fritzbox zusammenhängt, natürlich mehr als nah.

Um die Vermutung zu verfestigen oder zu wiederlegen, habe ich mit der gleichen Hardware in verschiedenen Netzwerkumgebungen getestet, ob das Problem nicht doch an meinem Rechner oder der Konfiguration liegen könnte.

Das war aber nicht der Fall, das Problem trat nur in meinem Homeoffice auf.

Was lag also näher als den Support von AMV anzuschreiben um ihm meine Probleme zu schildern.

Gesagt getan und folgendes E-Mail an den Support gesendet:

>>>=================  START MAIL vom 03.05.2012 ================>>>

Hallo Support Team,

Ich verwende den Nortel VPN Client Version 10.04.016 um eine VPN Verbindung zu einem Firmennetzwerk herzustellen.

Ich kann die Verbindung herstellen, nach kurzer Zeit (nach einigen Sekunden bis Minuten) wird die Verbindung getrennt und es erscheint folgende

Fehlermeldung:

„VPN tunnel is disconnected due to routing table change“.

Wenn ich den gleichen Rechner in meinem Büronetzwerk verwende in dem ich einen Router von Vodafone einsetze funktioniert der VPN Zugang zum selben Firmennetzwerk den ganzen Tag ohne Unterbrechung.

Auf das VPN des Firmennetzwerk habe ich keinen Einfluss (das ist aber auch obsolet, da es für über 100000 Mitarbeiter weltweit funktioniert.

Für mich auch, wenn ich es nicht mit der FritzBox 7390 aus dem Homeoffice heraus verwende.

Der 7390 ist an einem VDSL 50000 Anschluss angeschlossen und funktioniert bis auf dieses VPN Problem ohne weitere Einschränkung. Leider kann ich ohne dieses VPN mein Homeoffice nicht nutzen 🙁 .

Ich hoffe Sie haben Lösungsansätze für mich

>>>================= ENDE MAIL vom 03.05.2012 ================>>>

Nicht gut aber schnell, nämlich am gleichen Tag habe ich dann eine erste Antwort erhalten

<<<================= START MAIL vom 03.05.2012 ===================

Guten Tag Herr Schelian,

vielen Dank für Ihre Anfrage an den AVM-Support.

Tipps und Hinweise zu VPN-Lösungen anderer Hersteller finden Sie in folgendem

Link:

http://service.avm.de/support/de/SKB/FRITZ-Box-7390/243:VPN-Loesung-eines-andere

n-Herstellers-mit-FRITZ-Box-verbinden

Über die Tipps in unserer FAQ hinaus leisten wir keinen Support für lokale oder individuelle VPN-Szenarien mit der Software anderer Hersteller.

<<<================= ENDE MAIL vom 03.05.2012 ===================

Außer einem Link zu einer furchtbaren FAQ die so überhaupt nichts mit dem von mir geschilderten Problem zu tun hat, nur “Schön dass Sie uns geschrieben haben”.

Aber ich wäre nicht Ich, wenn ich mich damit zufrieden gegeben hätte.

Also habe ich 2 Tage später folgendes Mail an den Support zurückgeschrieben:

==================== START MAIL vom 05.05.2012 ================>>>

Hallo Support Team,

leider hat mir Ihr Hinweis nicht geholfen.

Natürlich hatte ich bevor ich Sie kontaktiert habe die Q&A auf Ihrer Webseite angeschaut.

Ich nehme an, dass Sie mein Problem nicht richtig verstanden haben, ich versuche nicht mit irgendeiner VPN Lösung auf die Fritzbox zuzugreifen, sondern aus dem Netz welches über die Fritzbox über das Internet hergestellt hat auf ein Weltweites VPN Firmennetzwerk zuzugreifen.

Wie ich bereits in meiner ersten Nachricht geschrieben habe, kann ich mit dem gleichen Notebook aus meinem Firmennetz an dem ein Vodafone Router die Verbindung ins Internet herstellt, ohne Problem auf das externe VPN Netzwerk zugreifen.

Über einen Mobilen WLAN Hotspot funktioniert der Zugriff ebenfalls.

Eben nur nicht sobald ich über den Internetzugang gehe der über die Fritzbox hergestellt wird.

Ich hoffe dass Sie mir weiterhelfen können.

==================== ENDE MAIL vom 08.05.2012 ================>>>

Und tatsächlich erhalte ich nach nur 3 Tagen wieder eine Antwort

<<<================= START MAIL vom 05.05.2012 ===================

Guten Tag Herr Schelian,

vielen Dank für Ihre Rückmeldung.

Es tut mir leid, aber wir supporten wie erwähnt keine individuellen lokalen

VPN-Szenarien.

Freundliche Grüße aus Berlin

Jon Doe (AVM Support) (Name wurde auf Wunsch von AVM geändert)

<<<================= ENDE MAIL vom 08.05.2012 ===================

Es tut mir auch leid, ich konnte mir nicht verkneifen noch einmal zurück zu schreiben

==================== START MAIL vom 10.05.2012 ================>>>

Hallo Herr Doe, (Name wurde auf Wunsch von AVM geändert)

wenn das die Meinung von AVM ist!

Ich finde zwar nicht dass es sich bei meinem Problem im ein VPN Problem sondern um ein Router bzw. Firewall Problem Ihres Routers handelt. Aus irgend einem Grund ändert der Router die Routing Table und stört damit die bestendende Verbindung zu meinem VPN Server.

Vor allem wo dies mit verschiedener Firmware mal besser mal schlechter wird.

Mit der letzten Aktualisierung gerade mal wieder schlechter, bzw. mal wieder gar nicht benutzbar.

Dann werde ich wohl keine Fritz Router mehr einsetzen können.

==================== ENDE MAIL vom 10.05.2012 ================>>>

Aber wer denkt, der AVM Support würde aufgeben, der täuscht, ich erhalte also noch einmal eine Mail vom AVM Support

<<<================ START MAIL vom 11.05.2012 ===================

Guten Tag Herr Schelian,

vielen Dank für Ihre Rückmeldung.

Mein Kollege hat hier völlig recht. Es gibt hier keinen kausalen Zusammenhang mit der FRITZ!Box. Lesen Sie sich bitte dies hier in Ruhe

durch:

http://blog.michaelfmcnamara.com/2010/03/nortel-vpn-client-release-10-04-016-for-windows-7/#comment-3602

Dort ist exakt die Fehlermeldung aufgegriffen und auch die Ursache und eine Lösung beschrieben.

Freundliche Grüße aus Berlin

Max Mustermann (AVM Support) (Name wurde auf Wunsch von AVM geändert)

<<<================= ENDE MAIL vom 11.05.2012 ===================

Ich habe dort zwar keine Lösung für mein Problem gefunden, aber immerhin einen Workaround und konnte damit tatsächlich ermitteln, dass das Problem mit dem 7390 nur mit WLAN auftritt und nicht wenn ich den Rechner per Kabel angeschlossen habe.

Eigentlich hätte man die Sache damit bewenden lassen können, aber ich wäre nicht Ich wenn ich das Problem nicht an der Wurzel packen wollte.

Und so habe ich mich auf die Suche nach den Unterschieden in den verschiedenen W-LAN Netzen gemacht.

Um das ganze hier nicht ausufern zu lassen möchte ich nun aber zur Lösung des Problem kommen.

Hallo AVM Support hört gut zu, es gibt eine Fritz7390 Lösung für mein VPN spezifisches Problem.

Unter WLAN–>Funkkanal gibt es eine Einstellung:

image

Für 300 Mbit/s optimierte Kanäle verwenden.

Und diese Option ist im Default gesetzt (Und muss ausgeschaltet werden).

Das einschalten dieser Option führt dazu, dass die Fritzbox anstelle eines einzelnen Kanals einen Doppelkanal zum Beispiel Kanal 8 und Kanal 12 verwendet, und in diesem Zusammenhang kommt es zu wohl irgendwie zum internen Umschreiben der Routing Table oder irgend einem anderen Fritzbox internen Problem, dass zu dem in diesem Artikel beschriebenen Abbruch der VPN Verbindung kommt.

Übrigens konnte ich noch einen Zusammenhang mit dieser Einstellung feststellen.

image

Bei der Musik Übertagung vom iPhone/iPad via AirPlay auf einen Verstärker kam es immer wieder zu kurzen Unterbrechungen (Aussetzern)!

Diese sind nun auch verschwunden.

Hallo AVM Support Team, es war mir eine Freude behilflich sein zu können.

Ich schicke euch den Link per Mail, dann könnt Ihr eure FAQ ergänzen

DotNetNuke – Nach Upgrade auf DNN 6.1.X – HTML Modul speichert keine Änderungen mehr

Heute Vormittag habe ich den Anruf eines Kunden erhalten der mir folgendes Problem geschildert hat.

Nach der Aktualisierung eines DotNetNuke Portals von DNN 5.6.X auf DNN 6.1.1 werden Änderungen die man an Texten im HMTL Modul vornimmt nicht gespeichert.

Keine Fehlermeldung, kein Eintrag im Ereignisprotokoll, einfach nichts!

Nachdem ich mir die Konfiguration genauer angeschaut hatte, konnte ich einen Fehler in der Konfiguration des System (IIS und App Pool) ausschließen.

Das Problem war aber trotzdem relativ schnell gefunden.

Mit dem “früher” als Standardeditor eingesetzten FCK Editor gibt es mit der aktuellen Version (Ich glaube schon seit Version 5.6.X) von DotNetNuke an einigen Stellen Probleme, unter anderem eben auch im Text/HMTL Modul.

Irgendwelche JavaScripte des Editors kollidieren mit anderen JavaScript aufrufen in irgend einer der vielen verwendeten JavaScript Frameworks.

JavaScript eben Smiley

Ich weiß schon warum sich meine Begeisterung für dieses (Java)Script Gedöns in Grenzen hält.

OK, aber lassen wir das.

Ob es einen aktualisierte Version des FCK Editors gibt, die mit DNN Version 6.1 und höher läuft, weiß ich nicht, sollte jemand näheres dazu wissen, würde ich mich über einen entsprechenden Kommentar freuen.

Um das Problem mit den Bordmitteln von DNN zu lösen kann man aber einfach den Texteditor umstellen und anstelle des FCK Editors den Telerik Texteditor, der aktuell als Standardeditor von DotNetNuke verwendet wird.

Hierzu öffnet man die web.config und nimmt folgende Änderung vor:

Den defaultProvider von “FckHtmlEditorProvider” (siehe Vorher) auf “TelerikEditorProvider” (siehe Nachher) ändern

Vorher:

<htmlEditor defaultProvider="FckHtmlEditorProvider">
  <providers>
	<clear />
	<add name="TelerikEditorProvider" type="DotNetNuke.HtmlEditor.TelerikEditorProvider.EditorProvider, DotNetNuke.HtmlEditor.TelerikEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Telerik/" toolsFile="~/Providers/HtmlEditorProviders/Telerik/Config/ToolsDefault.xml" configFile="~/Providers/HtmlEditorProviders/Telerik/Config/ConfigDefault.xml" FilterHostExtensions="True" />
	<add name="FckHtmlEditorProvider" type="DotNetNuke.HtmlEditor.FckHtmlEditorProvider.FckHtmlEditorProvider, DotNetNuke.FckHtmlEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Fck/" CustomConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfig.js" EnhancedSecurityDefault="false" SecureConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfigSecure.js" ImageGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageAllowedFileTypes="gif,png,bmp,jpg" FlashGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashAllowedFileTypes="fla,swf" LinksGalleryPath="~/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx" DynamicStylesGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKStyles.aspx" DynamicStylesCaseSensitive="true" DynamicStylesGeneratorFilter="controlpanel|filemanager|mainmenu|wizard" StaticStylesFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/fckstyles.xml" StylesDefaultMode="dynamic" DynamicCSSGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKCSS.aspx" StaticCSSFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/editor/css/fck_editorarea.css" CSSDefaultMode="dynamic" spellCheck="ieSpell" AvailableToolbarSkins="Office2003,Silver" DefaultToolbarSkin="Office2003" AvailableToolBarSets="DNNDefault,Default,NoGallery,Basic" DefaultToolbarSet="DNNDefault" DefaultImageGallerySkin="Default" DefaultFlashGallerySkin="Default" DefaultLinksGallerySkin="Default" FCKDebugMode="false" UseFCKSource="false" OptionsOpenMode="ShowModalDialog" ShowModuleType="true" FixOldDNNPostback="false" CustomOptionsDialog="Admin" />
  </providers>
</htmlEditor>

Nachher:

<htmlEditor defaultProvider="TelerikEditorProvider">
  <providers>
	<clear />
	<add name="TelerikEditorProvider" type="DotNetNuke.HtmlEditor.TelerikEditorProvider.EditorProvider, DotNetNuke.HtmlEditor.TelerikEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Telerik/" toolsFile="~/Providers/HtmlEditorProviders/Telerik/Config/ToolsDefault.xml" configFile="~/Providers/HtmlEditorProviders/Telerik/Config/ConfigDefault.xml" FilterHostExtensions="True" />
	<add name="FckHtmlEditorProvider" type="DotNetNuke.HtmlEditor.FckHtmlEditorProvider.FckHtmlEditorProvider, DotNetNuke.FckHtmlEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Fck/" CustomConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfig.js" EnhancedSecurityDefault="false" SecureConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfigSecure.js" ImageGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageAllowedFileTypes="gif,png,bmp,jpg" FlashGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashAllowedFileTypes="fla,swf" LinksGalleryPath="~/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx" DynamicStylesGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKStyles.aspx" DynamicStylesCaseSensitive="true" DynamicStylesGeneratorFilter="controlpanel|filemanager|mainmenu|wizard" StaticStylesFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/fckstyles.xml" StylesDefaultMode="dynamic" DynamicCSSGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKCSS.aspx" StaticCSSFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/editor/css/fck_editorarea.css" CSSDefaultMode="dynamic" spellCheck="ieSpell" AvailableToolbarSkins="Office2003,Silver" DefaultToolbarSkin="Office2003" AvailableToolBarSets="DNNDefault,Default,NoGallery,Basic" DefaultToolbarSet="DNNDefault" DefaultImageGallerySkin="Default" DefaultFlashGallerySkin="Default" DefaultLinksGallerySkin="Default" FCKDebugMode="false" UseFCKSource="false" OptionsOpenMode="ShowModalDialog" ShowModuleType="true" FixOldDNNPostback="false" CustomOptionsDialog="Admin" />
  </providers>
</htmlEditor>

Generische Methode zur Überschreibung der ToString Methode eigener komplexer Klassen – C#

Im Zuge eines Projektes musste eine komplexe ineinander verschachtelte Klassenstrukturen aufgebaut werden, um eine ebenfalls komplexe Interfacestruktur einer externen Anwendung abzubilden.

Um bei Fehlermeldungen und beim Debuggen (ja ich oute mich, ich debugge auch Code) ordentliche Daten, anstelle des reinen Objektnamen über die ToString() Methode dargestellt zu bekommen, überschreibt man in der jeweiligen Klasse die ToString() Methode und gibt darin an, welche Daten ausgegeben werden sollen.

So in etwas sieht es im Debugger aus, wenn man ToString() nicht überschreibt:

image

Für, ich will es mal “Nicht so komplexe Klassen” nennen, kann man das auch im folgenden Stil machen.

        public override string ToString()
        {
            return "Ausschreibungsstatus= " + this.Ausschreibungsstatus + 
                " " + "Ausschreibungstext= " + this.Ausschreibungstext;
        }

Das ist schon besser und sieht in etwa so aussehen:

image

Schon besser, aber ….

Ich würde gerne ohne großen Schreibaufwand in jeder Klasse die Public Eigenschaften mit den enthaltenen Werten angezeigt bekommen.

Also musste ich mir hierfür etwas mehr generisches ausdenken.

Und wieder einmal lag die Lösung meines Problems in der Verwendung von Reflection.

Hier nun meine “generische” Methode zur Bildung einer für mich “sinnvollen” ToString() Ausgabe für meine komplexen Klassen.

public static string ClassPropertiesToString(object obj)
{
    string retVal = obj.GetType().ToString() + "\n";
    const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;
    var sourceProperties = obj.GetType().GetProperties(Flags);

    foreach (PropertyInfo pi in sourceProperties)
    {
        if (pi.PropertyType.Namespace != "System")
            continue;

        retVal += string.Format("{0}:{1}\n", pi.Name, pi.GetValue(obj, null));
    }

    return retVal;
}

Ich denke der Code ist soweit Selbsterklärend, falls nicht, einfach einen Kommentar abgeben und fragen, dann erläutern wir die Details.

Hier noch der Vollständigkeit halber, das Ergebnis im Debugger:

image

Und bevor ich es vergesse.

So sieht dann die Überschreibung in der jeweiligen Klasse aus:

public override string ToString()
{
    return Lager.Global.Common.Generic.ClassPropertiesToString(this);
}

Generische Methode um gleichnamige Properties einer Klasse zu einem Objekt einer anderen Klasse zu kopieren – C#

Bei meiner Arbeit kommt es häufiger vor, dass ich umfangreiche Klassen (welche die Daten einer Datenbank repräsentieren) verwende die mal schnell über 50 oder 100 Public Properties (Datenbankfelder) enthalten.

imageNun kommt immer wieder vor, dass ich zum Beispiel zum Archivieren solcher Daten, damit meine ich die Daten aus einer Tabelle in eine andere zu verschieben, nicht auf SQL Skripte auf dem Server zugreifen kann oder möchte, da dieses verschieben noch mit zusätzlicher Businesslogik hinterlegt werden muss.

Was bleibt ist dann so etwas in dieser Art:

var auftrag = new Auftrag
    { Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = new AuftragArchiv();
auftragsArchiv.Artikel = auftrag.Artikel;
auftragsArchiv.Beschreibung = auftrag.Beschreibung;
auftragsArchiv.Preis = auftrag.Preis;

if (auftragsArchiv.Save())
{
    auftrag.Delete();
}

Diese Vorgehensweise kann bei umfangreichen Klassen sehr arbeitsintensiv sein.

Nun gibt es verschiedene Ansätze um sich diesem Problem anzunehmen. Nachfolgend möchte ich einige Lösungsansätze unter Verwendung von Reflection aufzeigen.

Ich nehme an, jeder kennt die Methode ToString().

Die Idee ist nun eine generische To…. Methode zu erstellen, welche die Aufgaben wie im obigen Beispiel dargestellt, universell erledigt.

Das Ziel soll sein, dass die Public Property Werte eines Objektes, durch Aufruf einer Methode, alle Werte die den gleichen Property Namen  haben in ein anderes Objekt einer anderen Klasse kopiert werden.

Hier nun die Variante 1 (Methode einer Klasse):

public T ToTobject<T>(T target)
{
    const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
    var sourceProperties = this.GetType().GetProperties(Flags);

    foreach (PropertyInfo pi in sourceProperties)
        {
            if (pi.CanWrite)
            {
				var propInfoObj = target.GetType().GetProperty(pi.Name);
				if (propInfoObj != null)
				{
					var propValue = pi.GetValue(this, null);
					propInfoObj.SetValue(target, propValue, null);
				}                             
            }
        }

    return target;
}

Der Aufruf um diese Variante zu verwenden sieht dann so aus:

var auftrag = new Auftrag
	{ Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = new AuftragArchiv();

auftragsArchiv = auftrag.ToTobject(auftragsArchiv);

if (auftragsArchiv.Save())
{
	auftrag.Delete();
}

Sieht doch schon ganz gut aus.

Hier noch eine Variante, die noch etwas mehr generisch arbeitet und nur noch mitgeteilt bekommt, welchen Type ich als Rückgabe der Methode erwarte, die Instanz des Objekts der neuen Klasse wird in der Methode selbst erzeugt.

public T ToTobject<T>()
{
	var constructorInfo = typeof(T).GetConstructor(new Type[] { });
	if (constructorInfo != null)
	{
		var target = (T)constructorInfo.Invoke(new object[] { });

		const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
		var sourceProperties = this.GetType().GetProperties(Flags);

		foreach (PropertyInfo pi in sourceProperties)
		{
			if (pi.CanWrite)
			{
				if (!pi.IsDefined(typeof(XmlIgnoreAttribute), false))
				{
					var propInfoObj = target.GetType().GetProperty(pi.Name);
					if (propInfoObj != null)
					{
						var propValue = pi.GetValue(this, null);
						propInfoObj.SetValue(target, propValue, null);
					}
				}
			}
		}

		return target;
	}

	return default(T);
}

Diese Variante wird dann so verwendet:

var auftrag = new Auftrag
	{ Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = auftrag.ToTobject<AuftragArchiv>();

if (auftragsArchiv.Save())
{
	auftrag.Delete();
}

Diese Variante gefällt mir schon etwas besser

Aber das ist noch nicht das Ende der Methoden Evolution!

Es geht noch generischer (und statisch für alle Klassen verwendbar) und diese Variante folgt nun.

public static T ToTobject<T>(object source)
{
	var constructorInfo = typeof(T).GetConstructor(new Type[] { });
	if (constructorInfo != null)
	{
		var target = (T)constructorInfo.Invoke(new object[] { });

		const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
		var sourceProperties = source.GetType().GetProperties(Flags);

		foreach (PropertyInfo pi in sourceProperties)
		{
			if (pi.CanWrite)
			{
				var propInfoObj = target.GetType().GetProperty(pi.Name);
				if (propInfoObj != null)
				{
					var propValue = pi.GetValue(source, null);
					propInfoObj.SetValue(target, propValue, null);
				}
			}
		}

		return target;
	}

	return default(T);
}

Diese nun statische Variante kann auf jedes beliebige Objekt jeder beliebigen Klasse angewendet und für die Rückgabe jeder beliebigen Klasse verwendet werden. Natürlich nur solange diese Klassen auch gleiche Properties enthalten.

Und so wird diese statische Methode verwendet:

var auftrag = new Auftrag
	{ Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = Auftrag.ToTobject<AuftragArchiv>(auftrag);

if (auftragsArchiv.Save())
{
	auftrag.Delete();
}

Mir hat es Spaß gemacht diese Methodik zu entwickeln, evtl. hilft es auch mal jemand anderem beim Lösen “seines” Problem.

Anregungen und Kritik wie immer gerne als Kommentar zum Beitrag.

SQL Server Management Studio – Saving changes is not permitted

Heute bin ich wieder mal (jedes mal wenn ich das Management Studio neu installiere) über ein bereits seit langem bekanntes Problem gestolpert.

Nachdem ich einer vorhandenen Tabelle  einige Felder über den Table Designer hinzugefügt habe und diese Änderungen speichern wollte, habe ich folgende Meldung erhalten:

Saving changes is not permitted. The changes you have made require the<br />
following tables to be dropped and re-created, You have either made changes<br />
to a table that can’t be re-created or enabled the option Prevent saving<br />
changes that require the table to be re-created,

Verzeihung liebes Management Studio, Nein ich möchte nicht dass du die Tabelle löschst und sie mit der neuen Struktur Neu erstellst.

Da mir aber zu diesem Zeitpunkt wieder mal entfallen war, dass ich dieses Problem früher schon hatte, habe ich mich kurzerhand an meine Timeline in Twitter gewandt.

Dort habe ich prompt, wie fast immer,  die richtige Antwort erhalten.

Danke dafür an Sebastian und Norbert, bei den beiden Funktioniert das Gedächtnis wohl noch besser als bei mir.

Und damit ich in Zukunft selbst daran denke, und auch Uwe nachschauen kann wie man das Problem lösen kann, habe ich diesen Beitrag geschrieben.

Und hier nun die Lösung des Problems:

Man kann in den Optionen einstellen, ob das Management Studio diese Vorgehensweise “Warnung ausgeben und das Drop/Create Szenario” anstelle einer “Änderung der Tabellenstruktur”, verwenden soll (Siehe nachfolgende Abbildung).

SNAGHTML15e0776

Vielleicht hilft das hier nicht nur mir und Uwe