Creating an XtraReports report that works both with Microsoft SQL Server and VistaDB

Currently I am extending and enhancing the reporting system for our test management tool Zeta Test.

In this article I will describe the issues I discovered (and solved) when trying to design one single report that works with both Microsoft SQL Server and VistaDB.

Creating a report

I decided to go with XtraReports from DevExpress as the reporting system.

The basic design steps creating and deploying a report with XtraReports is similar to methods when working with e.g. Microsoft SQL Server Reporting Services:

  1. Create a new report in the designer of XtraReports.
  2. Specify connection string/settings (using the SqlClient namespace classes).
  3. Specify tables/SQL queries to use as the data source for the report.
  4. Design your report graphically.
  5. Save the report to a .REPX file.
  6. Pack the .REPX file into the deployment setup.
  7. Ship the setup to the customers.

Displaying a report

In Zeta Test I had to perform the following steps when displaying a report:

  1. Load the report.
  2. Replace the design-time connection string by the connection string to the currently used database of the currently loaded project in Zeta Test.
  3. Display the report in a print-preview form.

Two databases

Since a project in Zeta Test can either use Microsoft SQL Server or VistaDB as the back-end database, I wanted to design a single report so that it can be used with both back-end databases.

Inially I planned to simply design the reports with an OleDB connection to the SQL Server database and then later when displaying the report for a VistaDB database, simply switch the connection string to the VistaDB database.

Unfortunately there is no OleDB provider for VistaDB. Therefore this solution did not work. Another idea was to simply leave the SqlClient classes that were used when designing the report as they are, only changing the connection string. This did not work, either.

So I did come up with another solution that works quite well as of writing this article:

Rewriting the .REPX report

A .REPX report file is a C# source code file that gets compiled when loading the report with the XtraReport.LoadLayout method.

I decided to give the following „algorithm“ a try:

  1. Load a .REPX file that was designed with the SqlClient classes as a string into memory.
  2. Do some string replacements to replace all SqlClient classes by their VistaDB counterparts.
  3. Call XtraReport.LoadLayout on the replaced string.
  4. Display the report in a print-preview form.

Luckily, after some try and error I actually was able to get this working!

In the following chapter I outline some of the methods I created in order to hopefully give other developers some ideas to use in their own applications:

Code to rewrite a .REPX report

The following method is the main method that is being called in order to load and display a report. It is a member of the print-preview form:

public void Initialize(
    FileInfo reportFilePath,
    Pair<string, string>[] parameters)
{
    var report = new XtraReport();

    if (Host.Current.CurrentProject.Settings.IsVistaDBDatabase)
    {
        var rawContent = UnicodeString.ReadTextFile(reportFilePath);

        var replacedContent = replaceSqlClientForVistaDB(rawContent);

        using (var ms = new MemoryStream(
            Encoding.Default.GetBytes(replacedContent)))
        {
            report.LoadLayout(ms);
        }
    }
    else
    {
        // Assumes that the report was designed with SQL Server.
        // If not, one would have to replace the other way, i.e.
        // from VistaDB to SQL Server.
        // I am leaving this out until really needed.
        report.LoadLayout(reportFilePath.FullName);
    }

    // --
    // Change connection string.

    var da = report.DataAdapter as DbDataAdapter;

    if (da != null)
    {
        adjustConnectionString(da.SelectCommand);
        adjustConnectionString(da.InsertCommand);
        adjustConnectionString(da.DeleteCommand);
        adjustConnectionString(da.UpdateCommand);
    }

    // --
    // Adjust parameters.

    if (parameters != null && parameters.Length > 0)
    {
        foreach (var parameter in parameters)
        {
            if (containsParameter(report.Parameters, parameter.First))
            {
                report.Parameters[parameter.First].Value = parameter.Second;
            }
        }
    }

    // --
    // Bind the report's printing system to the print control.

    printControl.PrintingSystem = report.PrintingSystem;

    // Generate the report's print document.
    report.CreateDocument();
}

The actual method to replace the string is the following:

private static string replaceSqlClientForVistaDB(string content)
{
    if (string.IsNullOrEmpty(content))
    {
        return content;
    }
    else
    {
        var result = content;

        // Add reference.
        result =
            result.Replace(
                Environment.NewLine + @"///   </References>",
                string.Format(
                    Environment.NewLine + @"///     <Reference Path=""{0}"" />" +
                    Environment.NewLine + @"///   </References>",
                    typeof (VistaDB.IVistaDBValue).Assembly.Location
                    ));

        // Change class and namespace names.
        result =
            result.Replace(
                @"System.Data.SqlClient.Sql",
                @"VistaDB.Provider.VistaDB");

        // Comment out non-available VistaDB properties.
        result = Regex.Replace(
            result,
            @"(^.*?FireInfoMessageEventOnUserErrors.*?$)",
            @"//$1",
            RegexOptions.Multiline);

        return result;
    }
}

And the method to replace the connection string is defined as follows:

private static void adjustConnectionString(DbCommand command)
{
    if (command != null)
    {
        var conn = command.Connection;

        if (conn != null)
        {
            if (conn.State == ConnectionState.Open)
            {
                conn.Close();
            }

            conn.ConnectionString = getConnectionString();
        }
    }
}

Other methods are left out for simplicity. If you want to see them, simply drop me a note, e.g. by e-mail.

Epilog

In this short article I’ve shown you a way to modify an XtraReports report during runtime of your application to work with both a Microsoft SQL Server and a VistaDB database without the need to design separate reports.

As of writing this article the reports I tested work quite well, probably/possible I will have to do further adjustments when getting to more complex reports. I will update the article then.

I am looking forward for lot of feedback! 🙂

Zeta Test 1.0 ist da!

Hurra! Nach 1½ Jahren Entwicklungszeit ist Version 1.0 unserer Test-Management-Umgebung „Zeta Test“ nun verfügbar.

Das, zugegebenermaßen erklärungsbedürftige, Produkt ist hervorragend geeignet um Testpläne und Testfälle für Software-Tests zu erstellen.

Kurzer Auszug aus der Website zum Produkt:

Zeta Test ist eine integrierte Test-Management-Umgebung, mit der Sie Black-Box-Tests, White-Box-Tests, Regressionstests und Change-Management-Tests (Release-Wechsel) von Software durchführen können.

Zeta Test unterstützt Sie dabei bei der Planung, Durchführung, Monitoring, Protokollierung und Dokumentation der Tests sowie beim Auswerten der Ergebnisse.

Schreiben und verwalten Sie mit Zeta Test Ihre Testfälle, Testpläne und Vorlagen. Testen Sie Ihre Software nach Testdrehbüchern, die Sie mit Zeta Test erstellt haben.

Ich bin gespannt wie ein Flitzebogen, wie die Software einschlägt! Alle Beta-Tester waren hellauf begeistert, jetzt heißt es den Vertrieb ordentlich anlaufen zu lassen.

Hier die direkten Download-Hyperlinks (für Microsoft Windows):

Handbücher gibt’s auch:

Weil wir nett sind (und uns viel Verbreitung versprechen), ist die Software für Privatanwender, Institutionen wie Schulen und Universitäten, sowie für den Einsatz im Open-Source-Kontext komplett kostenlos erhältlich.

Freue mich über viel Feedback Eurerseits zum Programm!

(Vom Webdesign und -Layout haben wir uns übrigens von den Freunden bei TeamViewer und Mozilla inspirieren lassen. Dank geht auch an Brian von „Lost in Deutschland“ für die hervorragenden Übersetzungen ins Englische!)

Meniskus angerissen

Mist! Riss im Innen-Meniskus. Und, ggf. dadurch verursacht, eine Baker-Zyste.

Heute war ich beim MRT, am Montag werde ich dann die Bilder mit meinem Ortopäden, Dr. Sklepek, anschauen.

Tipps und Hinweise/Ratschläge Eurerseits?

Danke!

Ergänzung 2009-03-25:

Ich war jetzt beim Dr. Sklepek. Er sagte mir, dass der Riss wohl schon mehrere Jahre bestehen würde (WTF?) und es wohl einen akuten Anlass vor 4 Wochen gab, dass es sich entzündet hat (Ggf. stimmt die Rosenzüchter-Theorie?).

Er würde nicht operieren, da Nähen wohl nur außen geht, bei mir aber innen ist. Entfernen würde er auch nicht, da alles glatt ist und nichts scheuert.

Statt dessen hat er mir jetzt so eine Bein-Bandage ausm Sanitätshaus verschrieben fürs Gehen und Laufen die nächste Zeit; und etwas das „DD-Ströme (DF+CF)“ aufm Rezept heißt.

Ich bin mal gespannt, ggf. regeneriert sich der Meniskus ja doch? Knochen können auch zusammenwachsen, da sollte so eine komische Scheibe das doch auch können?!

Jara in Action

Heute nur Hundebilder, ich hoffe die Frequenz ist noch erträglich.

Gestern waren wir mit Jana und ihren Hunden in Denkendorf. Dank Janas toller Kamera sind auch tolle Bilder entstanden. Drei davon nachfolgend.


Klick für Großbild


Klick für Großbild


Klick für Großbild

Hinweis: War schon leicht dunkel, deswegen sind die Bilder etwas in Mitleidenschaft gezogen.

Vorgehen bei Manifestdatei-Fehlern mit VC++-CRT-DLLs

Situation:

Eine C++-DLL mit managed und unmanaged Code läuft auf manchen Zielsystemen nicht, es erscheint eine Fehlermeldung:

Could not load file or assembly ‚ZetaHtmlTidy, Version=1.0.3236.15571, Culture=neutral, PublicKeyToken=null‘ or one of its dependencies. Diese Anwendung konnte nicht gestartet werden, weil die Anwenungskonfiguration nicht korrekt ist. Zur Problembehebung sollten Sie die Anwendung neu installieren. (Exception from HRESULT: 0x800736B1)

Dies, obwohl alle Dateien der CRT vorhanden sind:

  • Microsoft.VC90.CRT.manifest
  • msvcm90.dll
  • msvcp90.dll
  • msvcr90.dll

Ursache:

In meinem Fall war es so, dass die DLL nicht gegen die neueste Version der CRT gelinkt war. Sie war noch gegen die CRT aus Microsoft Visual Studio 2008 (SP 0) gelinkt, ausgeliefert habe ich aber die CRT-Version von Microsoft Visual Studio 2008, SP 1.

Konkret wurde die Version 9.0.30729.1 der CRT ausgeliefert, in der Manifest-Datei meiner Anwendung stand aber noch die Referenz auf 9.0.21022.8 drin.

Lösung:

Das Projekt neu kompilieren, aber mit der Besonderheit, dass der „/D“-Compiler-Schalter zusätzlich noch den Befehl „_BIND_TO_CURRENT_VCLIBS_VERSION=1“ mitgegeben bekam. Siehe Erklärung hier und hier.

Interessanterweise hatte ich erst Erfolg, als ich den Befehl vor alle anderen Befehle in den Projekt-Optionen schrieb. Also z.B. vor die „NDEBUG“-Präprozessor-Definition.

Außerdem habe ich zur Sicherheit den kompletten Ausgabeordner der zu kompilierenden DLL gelöscht.

Danach neu kompiliert und es hat funktioniert.

Anmerkungen:

Einige Hyperlinks zu guten Erklärungen bzw. Werkzeugen die Hilfreich waren:

  • Der bereits oben verlinkte Weblog-Artikel „Binding to the most recent Visual Studio Libraries“ beschreibt ausführlich was zu tun ist. Dort wird auch die Verwendung des Werkzeugs „dumpbin“ beschrieben, was mir hilfreich war.
  • Um zu sehen, auf welches CRT-Manifest/-Version tatsächlich in meiner erzeugten DLL verwiesen wird, ist das Werkzeug „Manifest View 1.0“ sehr hilfreich gewesen.
  • Ansonsten gibt’s zur Fehlersuche und allgemein zum Deployment von VC++-Anwendungen/-DLLs einiges in der MSDN-Bibliothek.

Ich hoffe der Artikel hier hilft anderen und mir zu einem späteren Zeitpunkt.

Bunker im Stauferpark

Heute beim Hundespaziergang im Stauferpark in 73037 Göppingen habe ich festgestellt, dass wir mitten im Wald einen Bunker oder irgend so was haben:

Find ich originell, und das weniger als 300 Meter vom Büro entfernt.

Der genaue Standort ist ungefähr hier.

Ggf. ist es ja auch eher ein ehemaliger Schießstand oder so was; mein Armee-Wissen reicht für eine genaue Bewertung leider nicht aus.

Hundespaziergang

Nachfolgender Artikel ist reine Selbstbeweihräucherung, ich hoffe es ist erträglich.

In diesem Sinne: „Schaut her liebe Damenwelt, was für eine süüüüüüüüße Hündin ich habe“.

Jara im Wald

Jara macht Häufchen (muss auch sein)

Jara holt Futterbeutel

Jara wartet

Jara kommt her

Jara kommt nochmal her

Jara kommt nach Hause

Jara wartet vor der Türe

Jara wird gefüttert

Jara empfängt natürlich jederzeit Besuche.

Die Vitrine

Es war so 1993, 1994 als ich gerade mit meiner ersten Freundin Manuela zusammengezogen war.

Sie hat noch studiert (Uni Ulm, Biologie) und ich war Zivildienstleistender im Kurhaus Bad Boll.

Deshalb war das Geld natürlich knapp. Wir waren damals gerade damit beschäftigt, die Wohnungseinrichtung zusammenzu“kratzen“, und haben uns sehr gefreut, als wir in einem lokalen Anzeigenblättchen unter „Die gute Tat“ gelesen hatten, dass jemand eine Wohnzimmervitrine verschenkt.

„Klasse, die holen wir uns!“

Also bei den Herrschaften angerufen und einen Termin ausgemacht zur Abholung. Ich glaube die haben in Filderstadt oder Umgebung gewohnt, also ca. 40 km zum Fahren.

Flux den Anhänger an meinen damaligen Golf 2 gebunden und mit Manuela an einem Samstag dorthingedüst, in freudiger Erwartung einer schönen Wohnzimmereinrichtung.

Als wir dann ankamen fand wir ein sehr nettes Ehepaar um die 60 vor und die Vitrine. Und was für eine Vitrine! Die hässlichste Vitrine die wir in unserem gesamten bisherigen Leben gesehen hatten. Mit großem Abstand.

Es zeigte sich dann, dass das Ehepaar so erfreut war, einer jungen Beziehung (also unserer) auf die Beine helfen zu dürfen, indem Sie etwas zur Wohnungsausstattung beitrugen, dass die beiden richtig leuchtende Augen hatten.

Eigentlich wollten wir die Vitrine ja gar nicht mitnehmen, aber die beiden waren soooo nett und glücklich, wir wollten sie dann nicht enttäuschen.

Also Vitrine auf den Hänger geladen, artig bedankt, verabschiedet und losgefahren.

Doch was jetzt machen mit dem hässlichen Teil?

Wir hatten schon damit gerechnet, das Möbelstück direkt in den Keller zum „Zwischenlagern“ zu verfrachten, doch sind dann bei der Heimfahrt zufälligerweise an einer großen Sperrmüllhalde vorbeigekommen (in Zell u. A. war das), die an dem Tag auch noch geöffnet hatte.

Also ganz spontan angehalten, reingefahren und die netten Männer dort gefragt ob wir noch „was Kleines mit dazulegen dürfen“.

Wir durften, haben frohen Gemütes die Vitrine dort abgelegt und sind dann, zwar ohne Wohnzimmervitrine aber glücklich, heim gefahren.

Es war zumindest ein schöner Ausflug gewesen, und wir konnten ein Ehepaar glücklich machen (in der Hoffnung dass diese den Artikel hier nie lesen).