in

DevWare GmbH

Blog- und Forum-Seite der Firma DevWare GmbH

Tibor Csizmadia

  • Entity Framework – BindingSource - Sorting

    Seit einiger Zeit beschäftige ich mich mit dem Entity Framework um einen neuen Weg der Datenzugriffschichten zu erleben. Leider muss man bei EDM wirklich noch von "Erleben" reden, da einige Funktionalitäten nicht implementiert sind. So die Unterstützung der Sorting und Filtering Funktionen von QueryObject – Auflistungen.

    Da ich auch ein sehr großer Fan von DataBinding (Darunter müssen auch alle Personen in meinem Umfeld leiden), wollte ich die Einfachheit von Entity Framework und WinForm Applikation mit Hilfe von Databinding ausprobieren. Leider mußte ich dann feststellen, dass das BindingSource- Control mit den EntityFramework Klassen klar kommt, aber die Funktionalität für Sortieren und Filtering nicht in der BindingSource Klasse implementiert ist, sondern durchgereicht wird an die gebundende DataSource. Und da liegt das Problem: Die EntityFramework Auflistungs Klassen unterstützen kein Sorting!

    Nun kann man darüber philosophieren ob es an dem Entity Framework oder an der BindingSource Klasse liegt, oder einfach eine EntityBindingSource Klasse schreiben: 

    public class EntityBindingSource : BindingSource, IBindingList
    {     
        private enum Direction
     {Ascending = 0,Descending = 1 }
       
    private class CompareEntities<T> : IComparer<T>     
       
    {
               
          
    private Direction _sortDirection;
           public Direction SortDirection 
           {                 
              
    get { return _sortDirection; }
               set { _sortDirection = value; }
           }
           private string _propertyName;
           public string PropertyName
           {
               get { return _propertyName; }
               set { _propertyName = value; }
           }

           public CompareEntities(Direction dir, string fieldName)
           { 
              
    SortDirection = dir;
               PropertyName = fieldName;
           }

           public int Compare(T x, T y)           
           {
               if (x.GetType().GetProperty(PropertyName) == null) 
                  
    throw new Exception(String.Format("Given property is not part of the type {0}", PropertyName));

               object objX = x.GetType().GetProperty(PropertyName).GetValue(x, null);                 
              
    object objY = y.GetType().GetProperty(PropertyName).GetValue(y, null);
                  
              
    if (objX == null && objY == null)
                   return (0);
               
    if (objX == null)
                   return (-1);          
              
    if (objY == null)
           
                  
    return (1);
            

              
    int retVal = default(int);
               if (SortDirection == Direction.Ascending)   
                   retVal = ((IComparable)objX).CompareTo((IComparable)objY); 
               else                            
                  
    retVal = ((IComparable)objY).CompareTo((IComparable)objX);                   
     
               return retVal;     
           
    }                 
        
       
    }           
                  

        
    void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
         
       
    {
           IEnumerable myOrgData = (IEnumerable)DataSource;           
          
    List<object>  myData = new List<object>();
               
          
    foreach (object myObj in myOrgData)
                     
             
    myData.Add(myObj);
                

          
    if (myData.Count < 2)
                     
            
    return;

           if (_SortDirection == ListSortDirection.Ascending)         
          
    {
     
            
    _SortDirection = ListSortDirection.Descending;
                     
             
    myData.Sort(new CompareEntities<object>(Direction.Ascending, property.Name));
              
          
    }
     
          
    else
       
          
    {
                   
            
    _SortDirection = ListSortDirection.Ascending;
        
            
    myData.Sort(new CompareEntities<object>(Direction.Descending, property.Name));
             
          
    }  

           _SortProperty = property;                     
          
    DataSource = myData;
          
      
    } 

       private ListSortDirection _SortDirection = ListSortDirection.Ascending;     
       
    ListSortDirection IBindingList.SortDirection
         
      
    {
      get 
         
    {
    return _SortDirection; } }       

       
    private PropertyDescriptor _SortProperty = null;
         
      
    PropertyDescriptor IBindingList.SortProperty
      
      
    {
      get  { return (_SortProperty); } }       

      
    bool IBindingList.IsSorted
         
      
    {
     get { return (true); } }       

      
    bool IBindingList.SupportsSorting
       { get { return (true); }}       

    }

      

    Das Prinziep dahinter ist ganz einfach, man leitet von der Klasse BindingSource ab und überlädt die Methode ApplySort diese Methode kommt aus dem interface IBindingList und wird jedesmal aufgerufen, wenn die DataSource sortiert werden soll. Der Rest in dieser Klasse dient dazu eine generichse Sortierung von einer beliebigen Entity Liste durchzuführen. Also nichts besonderes, was Microsoft nicht auch direkt implementieren hätte können. (PS: Und auch bestimmt besser als ich J)
  • ASP.NET Error: “The state information is invalid for this page …”

    Nach langem suchen habe ich heute endlich eine Erklärung und eine Lösung für die Fehlermeldung "The state information is invalide for this page" gefunden. Das Problem ist extrem weitreichend und wird durch eine winzige Kleinigkeit ausgelöst, dass ich mich entschlossen habe diesen Fall einmal genauer zu beschreiben.

    Wir haben eine ASP.NET 2.0 Web Applikation geschrieben, die in der Entwicklung einwandfrei läuft und getestet wurde. Nun ging diese Applikation in das Live System über und, (wie es bei größeren Web Anwendungen vorkommen kann) somit nicht nur auf einem Web Server sonder auf zwei WebServern läuft. Neben der Kleinigkeiten, das die machinkey in der web.config oder machine.config auf beiden WebServer gleich lauten muss will ich jetzt gar nicht sprechen.

    Die Applikation ist auf dem Live System erfolgreich und fehlerfrei gelaufen. Leider häuften sich dann die Aussagen der Benutzer, das die Applikation ab und zu eine Fehlersituation auswirft. Nach näherer Betrachtung und Analyse der Fehlermeldung könnten wir zwei Seiten ausmachen, die sporadisch die Fehlermeldung gebracht hatten:

     

    Der Hinweis auf den ViewState lässt ja erst einmal darauf schließen, das durch die beiden WebServer der State nicht richtig aufgelöst werden kann, wenn z.b. ein Request auf Server 1 landet und der darauffolgende Request auf Server 2. Wenn diese beiden Server nicht auf den gleichen Stateserver verweisen, kann diese Fehlermeldung vorkommen. In unserem Fall war das aber nicht so, also mussten wir weiter forschen… In der Fehlermeldung konnten wir dann noch weitere Informationen entnehmen wie z.B. die Meldung:

    "Could not load file or assembly "App_Web_4ec219hu, Version 0.0.0.0 Culture=neutral, PublicKeyToken=null …"

    Und diese Meldung hat mich dann auf den richtigen Weg gebracht. Wenn man sich das Ausführungsmodell von ASP.NET anschaut, wird folgendes auf dem WebServer bei jedem Request durchgeführt:

    • File ASPX laden
    • CodeBehind Klasse compilieren und ein dynamisches Assembly erzeugen oder aus dem Cache laden

    Die Fehlermeldung lässt darauf deuten, das der Webserver ein dynamisch erzeugtes Assembly nicht finden kann, weil er es für die Pageklasse benötigt. Das kann ja passieren, wenn man mehrere WebServer hat und das Assembly mit einem autogenerierten Namen nur auf dem ersten WebServer erzeugt wurde und der Request auf dem zweiten WebServer landet.

    Aber wieso ist das ein Problem, da bei jedem Request das ASP.NET Framework ja die CodeBehind Klasse neu compiliert, wenn er sie nicht finden kann, somit sollte auf dem zweiten Server das nicht vorhandende Assembly auch erzeugt werden? Das passiert auch und bis dahin läuft alles richtig wie gewünscht und gedacht… ABER…

    Wenn man nun in seiner PageKlasse einen ViewState mit einem Objekt setzt, dessen Klassen-Beschreibung innerhalb eines Autogenerierten Assemblies liegt, wie z.B folgendes Property mit einem Enum:

    public partial class Admin_Security : System.Web.UI.Page
    {
       
    public enum PageView { Liste = 0, Detail };
       
    public PageView CurrentView
        {
            
    get
            
    {
                
    if (ViewState["Admin_Security_CurrentView"] != null)
                   
    return ((PageView)ViewState["Admin_Security_CurrentView"]);
                
    return (PageView.Liste); 
             
    }

             set
            
    {
                
    ViewState["Admin_Security_CurrentView"] = value;
            

        
    }
    }

    Dann passiert folgendes: Das Objekt in der ViewState Variable "" wird für den ViewState ja serialisiert und bei diesem Prozess wird auch der Typ mit vollqualifizierten Namen in den Serialisierungsstream geschrieben, somit die Referenz auf das autogenerierte Assembly.

    Wird nun der Request von dem zweiten Server bearbeitet, versucht der Server den ViewState wieder zu deserialisieren und holt die Typ Definition aus dem Stream und versucht so ein Objekt zu instanziieren. Da das autogenerierte Assembly aber nicht auf diesem Server liegt und er auch keine Ahnung hat wo er das Assembly herbekommen soll, kommt es zu der Fehlermeldung:

    "Could not load file or assembly "App_Web_4ec219hu, Version 0.0.0.0 Culture=neutral, PublicKeyToken=null …"

    Das hat zur Folge, dass der ViewState nicht deserialisiert werden kann und es kommt zu der Fehlermeldung:

    "The state information is invalid for this page …"

    Quod erat demonstrandum!

     

    Die Lösung:
    Das Problem kann man also umgehen, wenn man dafür sorgt, das es keine Objekte in ViewState gehalten werden, deren Klassendefinitionen in autogenerierten Assemblies liegen.

     

  • MOSS2007 on Windows Server 2008

    Heute habe ich versucht unseren Sharepoint WSS 3.0 auf MOSS 2007 upgraden, eigentlich kein Problem... Wenn das ganze allerdings auf einem Windows Server 2008 installiert ist, bekommt man eine Kombatibilitäts Fehlermeldung und das Setup bricht hab. Schön das der Hinweis kommt, dass man eine Installation von MOSS2007 mit SP1 benötigt, welches noch nicht zum download bereit steht.

    Was nun? Jetzt muß man sich einfach selber eine integriertes Setup mit SP1 von MOSS2007 erstellen. Microsoft nennt das "Slipstream" installation und geht relativ einfach:

    1. MOSS2007 Setup-Verzeichnis auf eine Festplatte kopieren

    2. MOSS SP1 herunter laden

    3. SP1 extrahieren und in das Updates Verzeichnis der orginal Installation kopieren

    Feritg. Nun kann man das Setup auf einem Windows Server 2008 ohne Probleme und mit SP1 installieren.

    Quelle: http://www.scolab.ch/howto-slipstream-installation-von-office-sharepoint-server-fur-windows-server-2008-rc1.html

     

  • SQL Server 2005 Installation auf Windows Vista

    Jedesmal das gleiche: Wenn eine Installationsroutine nicht alle notwendigen Schritte ausführt die man zur Installation braucht (wie Framework installation, konfiguration), verzeifelt an merkwürdigen Fehlermeldungen... Es war mal wieder so weit, ein neues frisch Installiertes Windows Vista mit Development-Tools auszurüsten, so auch SQL Server 2005.

    Hatte das Setup-Programm immer wieder angemekert, das ohne IIS-Installation kein Reporting-Service installiert werden kann. Schaut man dann in der Systemsteuerung nach, ist IIS (allerdings 7) installiert. Das ist für das SQL Server 2005 Setup anscheint zu neu und deswegen müssen einige Einstellungen von hand durchgeführt werden, damit die Installation sauber durchlaufen kann.

    Zum Glück habe ich einen Microsoft KB Eintrag gefunden der die Schritte kurz beschriebt:

     http://support.microsoft.com/kb/934164/en-us

     

  • UNION Statement - Spaltenreihenfolge entscheident!

    Heute habe ich mal wieder eine Kleinigkeit zu SQL gelernt. Ich hatte ein UNION Statement um zwei Tabellenabfragen als eine Tabelle an mein DataReader zurückzugeben:

       Select 0 as OrderID, null as Firma, null as FirmID
    UNION
       select 1 as OrderID, Firm.Firmname, Convert(nvarchar(80),Firm.FirmID) from Firm

    So wird jeder erwarten, dass alle Firmeneinträge plus der null Eintrag in einer Tabelle zurückgegeben wird, ist auch so, leider hatte ich mich in der Codierung des SQL Statements vertan (Es war auch etwas umfangreicher aber für diese Demo ist es so besser ersichtlich) und aber die Spalten der beiden SELECT Statements nicht in der gleichen Reihenfolge :

       Select 0 as OrderID, null as Firma, null as FirmID
    UNION
       select 1 as OrderID,  Convert(nvarchar(80),Firm.FirmID), Firm.Firmname from Firm

    Ich hätte erwartet, dass ein SQL-Fehler geworfen wird, stattdessen wird das Statement fehlerfrei ausgeführt, nur die Inhalte sind von der zweiten Tabelle mit den beiden Spalten vertauscht. (Wahrscheinlich da beide Spalten den gleichen Datentyp hatten)!

    Tja SQL ist leider nicht typ-safe und durch das "reincodieren" der SQL Statements in den C# Code als string kann auch keinerlei Unterstützung durch die IDE gegeben werden...

     

     

  • VS2005 SP1 on Windows Server 2003

    Wer schonmal versucht hat das SP1 vom Visual Studio 2005 auf einer Windows Server 2003 Box zu installieren, wird wahrscheinlich nach x frustrierten Versuchen aufgeben, da immer der Fehler 1718 vom Installer hochkommt. Ärgerlicherweise immer erst nach x Minuten extracting...

    Error 1718.File D:\WINDOWS\Installer\50baad.msp was rejected by digital signature policy.  

    Ich habe zum Glück nach einigen Anläufen ein Hotfix gefunden mit dem die Installation dann funktioniert, also erst Knowledge Base article 925336 installieren und dann geht die SP1 Installation auch durch.

    Quelle: http://blogs.msdn.com/heaths/archive/2007/01/11/workaround-for-error-1718.aspx

     

  • Windows Vista - Unable to rename a file/folder

    Heute hatte ich eine erkenntnis der besonderen Art. Seit einigen Tagen konnte ich von meinem Vista-Desktop keine Files mehr auf den Fileshare unserer Server umbennenen. Komischerweise konnte ich Files anlegen und editieren und auch wieder speichern. Nur das Umbennenen ging nicht. Da wir zur gleichen zeit auch ein paar Änderungen im Rechenzentrum hatten und ein Fileserver gewandert ist, habe ich erst gedacht das es damit zusammen hängt.

    So vergingen ein paar Tage und ich mußte schon meine Dateien über einen Trick auf den FileServer speichern. Da ich das Problem mit meinem Notebook (WindowsXP) nicht hatte, lang die Vermutung nahe das es irgendwie mit Vista zu tun hat.

    Aber

    Die Lösung des Problems ist eigentlich ganz einfach aber auch verblüffend. Ich habe einen Forums Eintrag gefunden  (http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=1211320&SiteID=17) den ich einfach ausprobiert habe und was wirklich zum Erfolg geführt hat. Anscheint war auf meinem Vista Rechner die Offline-Datenbank für Files defekt. Einfaches Deaktivieren der Offline-File Features hat meinen Vista Rechner wieder zu einem Normalen Rechner gemacht.

  • Report exportieren ins Excel Format

    Heute hatte ich einmal die knifflige Aufgabe, einen Report über das ReportViewer-Control in das Excel Format zu exportieren. Eigentlich ist das eine ganz schöne Funktion, jedoch muss man bei der Definition des Reports auf einige Punkte achten, damit die Formatierung auch unter Excel noch zufriedenstellend aussieht:

    Die TextBox-Elemente die auf einen Report platziert werden und nicht in der Daten-Matrix enthalten sind, sollten von den Ausmaßen an die Spalten der Matrix ausgerichtet sein. Wenn das nicht so ist wird beim Excel Export immer leere Spalten hinzugefügt, um so diese Textboxen zu platzieren. Das wäre nicht so schlimm, allerdings müssen, dann Zellen in den Datenzeilen verbunden werden und ich hatte dann genau das Problem, das die Zeilenumbruch-Formatierung nicht sauber funktioniert.

    Bei der Suche nach einer Lösung bin ich auch noch auf den Parameter DeviceInfo in der Render- Funktion gestoßen, d.h. wenn man den Report per Code erzeugt und selber exportieren möchte kann man das ja wie folgt machen:

    public void ExportReportToExcel(string sReportname, Dictionary<string, object> myParamValues)

    {

    string sOutputPath = "c:\\temp\\Export.xls";

    LocalReport myReport = new LocalReport();

    string deviceInfo =

    "<DeviceInfo>" +

    "<SimplePageHeaders>True</SimplePageHeaders>" +

    "</DeviceInfo>";

    myReport.ReportPath = sLieferscheinReport;

    List<ReportParameter> myRepListe = new List<ReportParameter>();

    foreach (string myItem in myParamValues.Keys)

         myRepListe.Add(new ReportParameter(myItem, myParamValues[myItem].ToString()));

    myReport.SetParameters(myRepListe);

    ReportSystem.Instance.LoadReportData(myReport, myParamValues);

    Warning[] warnings;

    string[] streamids;

    string mimeType;

    string encoding;

    string extension;

    string sCurrentAbsPath;

    byte[] bytes;

    string sFilename;

    bytes = myReport.Render(

    "Excel", deviceInfo, out mimeType, out encoding, out extension,

    out streamids, out warnings);

    using (FileStream fs = new FileStream(sOutputPath, FileMode.Create))

    {

    fs.Write(bytes, 0, bytes.Length);

    fs.Close();

    }

    }

    Dabei fragt man sich natürlich was darf/kann man alles in den XML-Parameter deviceInfo übergeben? Die Antwort findet man unter MSDN: http://msdn2.microsoft.com/en-us/library/aa179622(SQL.80).aspx

  • Online-Hilfe aus Word-Dokument

    Heute ist so ein Tag (man nennt es ja auch so schon Zwischen den Jahren) an dem ich so richtig viel Zeit habe und mal Dinge aufarbeite zu den ich sonst nicht komme. Und was macht man dann als Softwareentwickler:

    Genau das was "wir" ja sonst so ungern machen: Dokumentation schreiben und Online Hilfe Files. Dabei habe ich nach einer schnellen und einfachen Lösung gesucht aus einem Word-Dokument ein richtiges chm-Helpfile zu generieren. Nachdem ich mir das "Html Help Workshop" Tool angeschaut hatte und schnell festgestellt habe, dass es nicht so einfach war aus meinem Word-Handbuch in ein chm-Helpfile zu generieren. So bin ich auf ein Smartes Tool Doc2Chm von der Firma Dawningsoft (http://www.dawningsoft.com/products/doc2chm.htm ). Damit lässt sich ganz schnell aus einem Word-Dokument ein Chm-File erzeugen wobei man auch noch einige Parameter konfigurieren kann.

    Dieses File habe ich dann in meine Solution mit aufgenommen und die Build-Action auf Content und das Property "Copy to Output Directory" auf "Copy allways". Somit kann man das Help-File aus seinem Programmcode wie folgt benutzen:

    private void onlineHilfeToolStripMenuItem_Click(object sender, EventArgs e)
    {
     
    Help.ShowHelp(this, "BusinessNewsDB.chm" );
    }

    Das war jetzt so einfach, das ich mich Frage warum wir nie Online-Hilfen mit unseren Applikation erstellen?

  • Databound Header/Footer Fields in Reporting-Service

    Heute bin ich auf ein echt gravierendes Problem beim erstellen von Reports mit den Microsoft Reporting Service gestoßen. Ich wollte in meinen Header einige Datenfelder Benutzer mit First() so das z.B. die Adresse eines Lieferscheins im Header Bereich benutzt werden kann. Leider kommt dann beim kompilieren des Reports die Fehlermeldung: "Felder können in Seitenkopfzeilen oder –fußzeilen verwendet werden". Da ich allerdings den Content-Bereich im Report dreispaltig aufbauen wollte und die Anschrift natürlich nicht in das dreispaltige Layout platzieren wollte, musste ich eine andere Lösung finden. Da bin ich auf folgenden Artikel aufmerksam geworden (http://msdn2.microsoft.com/en-us/library/ms159677.aspx) Dort wird ein kleiner Trick beschrieben, womit man im Header Bereich über die Referenz einer Textbox im Content Bereich auf den ersten Wert zugreifen kann.

    Leider ist das wirklich ein Workaround und von einem Reporting–Tool hätte ich eigentlich eine saubere Lösung erwartet…

  • Kalenderwoche aus DateTime ermitteln

    Heute bin ich auf einen echt sehr guten Tipp gestoßen, nachdem ich seltsame Implementierung gefunden habe. Ich war auf der Suche nach einer einfachen Möglichkeit unter .Net die Kalenderwoche aus einer DateTime Struktur zu ermitteln. Ich war sehr erstaunt darüber das diese Funktion nicht in der Klasse DateTime implementiert ist.

    Dann machte ich mich bei Google & Co auf der Suche nach einem einfachen Algorithmus....dabei sind mir echt abstruse Kandidaten untergekommen, aber auf diese Lösung wär ich nicht gekommen:

    using System;
    using System.Globalization ;

    namespace ConsoleApplication1
    {
        class Class1
        {
            [STAThread]
            static void Main(string[] args)
            {
                DateTime dt = DateTime.Now;
                System.Globalization.Calendar objCal = CultureInfo.CurrentCulture.Calendar;    
                int weekofyear = objCal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
                
                Console.WriteLine(weekofyear.ToString() ) ;
            }
        }
    }

     Somit ist mein Erstaunen über das Fehlen der Funktion in der DateTime-Klasse wieder beruhgt worden: .Net ist eben cool!

     

  • Visual Studio 2005 Web-Projekte + IIS7 unter Vista

    Ich habe mich gewundert warum meine ASP.NET Projekte unter VS2005 und Vista nicht mehr richtig funktionieren und nach langem Suchen bin ich dann auf das Blog von Sarang Datye (http://sarangdatye.blogspot.com/2006/11/windows-vista-iis7-vs2005-confusion.html) gestossen und endlich eine Lösung gefunden.

    Da wird nur mal wieder bewußt wie Abhängig einige Applikation von der Administrator- Rolle sind!

     

  • SQL-Server Installation auf Vista

    Heute habe ich mal feststellen müssen, dass die Installation des SQL-Servers 2005 auf einem Vista Betriebsystem nicht so einfach ist! Nachdem ich über Google auf Steffen Krause´s Blog Eintrag zum Installieren von SQL auf Vista (http://blogs.technet.com/steffenk/archive/2006/11/07/reporting-services-unter-vista.aspx ) gestoßen bin, hat die Installation auch geklappt. Allerdings habe ich anscheint etwas noch falsch gemacht, da ich mit dem SQL Managment Studio keine Verbindung zu der SQL-Instanz aufbauen konnte. Es erscheint immer wieder die Meldung:

    Attachment: SQLError18456.jpg (16641 bytes)

    Wenn man dann nach dieser Fehlermeldung einmal im Internet sucht, stößt man auf eine KB-Eintrag http://support.microsoft.com/kb/925744/en-us der darauf hindeutet, das es wohl ein Problem ist und erst im nächsten SP des SQL Servers (Also SP2) gefixt ist. Da ich mir den Rechner nicht mit einem CTP des SP2 vollmüllen wollte habe ich frustriert aufgegeben und mir hier mal einen Server beschafft, um dort SQL-Server 2005 zu installieren. (Zum Glück haben wir einen grooossseeennn Server auf dem Virtuell Server läuft, somit war das eben so einfach wie SQL-Server dann auch dort zu installieren.)

    Posted Dez 05 2006, 05:47 by Tibor Csizmadia with no comments
    Filed under:
  • Royalty@Night -Veranstaltung und das ISV- Royalty -Programm

    Ich vergaß ganz letzte Woche meine Eindrücke zu der Veranstaltung „Die Vermarktung von SQL Server 2005 im ISV Royalty-Lizenzprogramm (Royalty@Night)  “bei Microsoft in Neuss zu schreiben. Aufgrund geringer Teilnehmerzahl war es dann auch eine recht gemütliche Veranstaltung, nicht mehr als 5-6 Unternehmen schienen sich für das Thema zu interessieren. Ich frage mich ob es ein gutes Zeichen ist oder ehr ein schlechtes. Bedeutet es nun das es in Deutschland so wenige ISV (Independent Software Vendor, sprich Softwarehersteller) gibt, oder hierzulande einfach keine Software produziert wird? Natürlich ist das Thema Lizenzierung von Microsoft Software nicht gerade eins der spannendsten, wobei ein Einkäufer oder ein Geschäftsführer sollte sich schon einmal die Frage stellen, ob man seinen Einkauf von Lizenzen nicht optimieren kann.

    Auch aus diesem Grund wollte ich mir die Veranstaltung anschauen, um ggf. nach neuen Beschaffunsgwegen zu forschen. Mein Ansatz ist dabei immer schon gewesen, das wir als ISV eine Menge für Microsoft tun, da ohne die Lösungen hätte sich ein Betriebsystem Windows nicht so verbreitet. Um so ärgerlicher war es in der Vergangenheit, das man keine Möglichkeit bekommen hatte entsprechend seines Status von Microsoft direkt zu kaufen.

    Diesen Umstand soll das ISV- Royalty Programm beheben (es gab in der Vergangenheit schon dieses Programm nur mit etwas anderen Bedingungen), so dass man sich zu einem Mindestumsatz und einer Laufzeit verpflichtet und erhält dann entsprechende Lizenzen zu einem durchaus viel günstigeren Preis als im Laden oder über entsprechend OPEN oder SELECT Verträge.

    Das klingt erstmal ganz gut, allerdings sehe ich auch einige Probleme.

    Einmal sind nicht alle Produkte in diesem Royalty Programm, so stehen z.B. Betriebsystem nicht auf der Liste, was ich als Lösungsanbieter aus einer Hand nicht ganz nachvollziehen kann. Da ich dem Kunden ein komplett Angebot unterbreite ggf. noch mit Hardware.

    Auf der anderen Seite benutzen wir für unsere Lösung den Microsoft SQL Server der mittlerweile ja schon (fast) Betriebsystem – Status erfahren hat, weil sehr viele Produkte von Microsoft den Server auch nutzen. Somit ist die Runtime-Lizenz aus dem Roality Programm nicht ganz so interessant, da diese dann lizenztechnisch auf eine Lösung beschränkt ist. D.h. Sollte ich einmal einen Kunden die Runtime-Lizenz des SQL Servers als Vollversion angeboten haben und der Kunde möchte dann noch zusätzlich eine andere Applikation mit dieser SQL-Instanz laufen lassen, ist es lizenztechnisch (und ob es auch technisch nicht geht weiß ich nicht) nicht erlaubt. Somit würde der Kunde verärgert sein und ich mit meinem Angebot schon wieder gar nicht so gut dastehen. Somit bleibt nur die Vollversion des SQL-Servers zu nehmen und da schwindet schon wieder meine neu gewonnen Marge, die ich aus dem Royalty Programm erhofft hatte.

    Somit ist die Aussage eines Referenten schon ganz hilfreich gewesen; wenn man sich nur auf die SQL-Lizenz aus dem Royalty Programm stützt, wird man den Mindestumsatz kaum erreichen.

    Ein Trostpflaster gibt es allerdings noch, in Zukunft soll auch der Microsoft Small Business-Server 2003 in das Programm aufgenommen werden.

     Attachment: ISV.jpg (15599 bytes)

     

    Posted Dez 04 2006, 08:13 by Tibor Csizmadia with no comments
    Filed under:
  • Tipps und Tricks mit dem ReportViewer-Control für Local-Reports

    Die Reporting Features vom neuen SQL-Server 2005 finde ich als AdHoc Lösung ein schneller und eleganter Weg, um Daten anspruchsvoll zu präsentieren UND auch noch einfach druckbar oder nach PDF konvertierbar zu machen.  Wir setzten in vielen Projekten schon die Reporing Service ein, wobei ich festgestellt habe, das die der Einsatz von lokal gerenderten Reports ebenso einfach wie elegant eingsetzt werden kann. Lokale-Reports können mit dem Visual Studio 2005 auch ohne SQL-Reporting Service erzeugt und gerendert werden. Der einzige Nachteil dabei ist, dass man sich selber um die Datenquellen und Daten kümmern muß. Da ich diese Möglichkeit in einem aktuellen Projekt eingesetzt habe und einige Fallstricke bei der Benutzung des ReportViewer-Control festgestellt habe, wird ich mal einige Punkte hier beleuchten:

    SampleReport.rdl = SampleReport.rdlc

    Auch wenn in einem VS-Projekt die beiden Reportarten mit unterschiedlichen File-Extensions aufgelistet werden, sind die Dateiformat kompatibel. D.h. Wenn man ein Server-Report *.rdl in ein lokalen – Report umwandeln möchte, muss man nur das File von *.rdl in *.rdlc umbenennen. Dadurch gewinnt man auch noch den XML-Konten für das SQL-Sequenz, um die Daten für diesen Report zu ermitteln. Das habe ich mir zum nutzen gemacht, um die Reports einfach mit dem Server-Report Designer zu erstellen (was im übrigen dann sehr elegant ist, da man den Report inkl. SQL-Statement konfigurieren kann) und dann in das entsprechende Projekt als *.rdlc rein kopieren. Mit dem folgenden Code kann man dann so einen Report einfach lokal laden und render inkl. Datenermittlung:

    private void LoadReportData(Dictionary<string, object> myParamValues)

    {

               

    // Daten laden    

           string mySQL = "";

           XmlDocument myReportXML = new XmlDocument();

           myReportXML.Load(this.reportViewer1.LocalReport.ReportPath);

           XmlNodeList myList = myReportXML.GetElementsByTagName("DataSet");

     

           this.reportViewer1.LocalReport.DataSources.Clear();

           foreach (XmlNode myDSNode in myList)

           {

                        DataTable myDataTable = new DataTable(myDSNode.Attributes["Name"].Value);

                   XmlNode myQuery = FindXmlNode(myDSNode, "Query");

                   if (myQuery != null)

                   {

                        XmlNode myCmd = FindXmlNode(myQuery, "CommandText");

                        XmlNode myDSName = FindXmlNode(myQuery, "DataSourceName");

     

    SqlDatabase myDB = new SqlDatabase(ConfigurationManager.ConnectionStrings["mySQLConnectionString"].ConnectionString);

            SqlCommand myCmd = new SqlCommand(sSql);

     

     

           if (myParams != null)

                  {

                        foreach (string myParam in myParams.Keys)

                                myCmd.Parameters.Add(new SqlParameter(myParam, myParams[myParam]));

                        }

     

           DataSet myDS = myDB.ExecuteDataSet(myCmd);

     

                        if (myDS.Tables.Count > 0)

                                      myDataTable =myDS.Tables[0]);

                        else

                               myDataTable = new DataTable();

               

         

                        ReportDataSource myData = new ReportDataSource(myDSName.InnerText, myDataTable);

     

                        this.reportViewer1.LocalReport.DataSources.Add(myData);

                }

           }

     

           this.reportViewer1.RefreshReport();

           this.reportViewer1.Update();

    }

    Diese Funktion holt nun aus der *.rdlc XML-Datei das SQL-Statement und führt es, wobei dieser Funktion auch noch ein Array mit Eingabeparametern übergeben werden kann.

    Parameter-Eingabe bei lokalen Reports im ReportViewer-Control

    Die Parameter-Definition von Reports ist leider bei den lokal gerenderten reports nicht so vollständig implementiert wie bei den serverseitigen Reports. Somit stellt sich das ReportViewer-Control auch im Bereich der Parametr anders dar. Zum Leid der Entwickler muss man sich für die Paramtereingabe eine eingene Lösung bauen, da das Parameter-Panel mit der dynamisch generierten Eingabecontrols für lokale Reports nicht zur Verfügung steht.

    Ich habe mir dazu einfach ein Usercontrol gebaut, dem ich einfach per Methode ein Array der ReportParameter übergebe und dieses dann die Eingabecontrols dynmisch generiert:

    public void SetParamWindow( ReportParameterInfoCollection myParams )

    Dieses Control habe ich dann in meinem eigenen Report-Viewer Control, welches aus einem Split-Container mit zwei Panels besteht und dort einmal mein Parameter-Control und auf der anderen Seite das ReportViewer-Control befinden. Die Parameter werden dann wie folgt an das Parameter-Control übergeben:

    // prüfe ob Report mit Parameter?

    ReportParameterInfoCollection myParams = reportViewer1.LocalReport.GetParameters();

    if (myParams != null && myParams.Count > 0)

    {

    splitContainer2.Panel1Collapsed = false;

    ucReportParameters1.SetParamWindow(myParams);

    }

    else

    {

    splitContainer2.Panel1Collapsed = true;

    LoadReportData(null);

    }

     

    Reports dynamisch dem ReportViewer-Control zuweisen

    Nach den o.b. Hürden war ich nun überglücklich lokal gerenderte Reports einfach wie serverseitige Reports zu designen und im ReportViewer-Control auch genau so darzustellen. Nun lässt die Methode this.reportViewer1.RefreshReport()darauf schlissen, dass mit der Kombination des Properties this.reportViewer1.LocalReport.ReportPath (welches als  ein Getter und ein Setter besitzt) dem ReportVIewer-Control auch zur Laufzeit einmal einen neuen Report zur Darstellung mitgeben kann. Leider hat sich nach einigen frustrierten Test ergeben, das dieses nicht so funktioniert. Entweder ist es ein Feature oder ein Bug! Da ich diese Funktionalität allerdings unbedingt benötigte habe ich mir ein Workaround (mal wieder) gebaut. Der einfachste Trick der mir dazu eingefallen war ist, das anscheint dieses Property nur beim Erzeugen des Controls ausgewertet wird. Somit habe ich in mein Panel vom Split-Container, welches das ReportViewer-Control beinhalttet, einfach zum Zeitpunkt des Neuladen eine neue Instanz des ReportViewer-Control erzeugt habe.

    Somit ergibt sich eine Lade-Methoide für neue Reports wie folgt:

    private void LoadReportByClient(string sPath)

    {

    this.splitContainer2.Panel2.Controls.Clear();

    this.reportViewer1 = new Microsoft.Reporting.WinForms.ReportViewer();

    this.reportViewer1.Dock = System.Windows.Forms.DockStyle.Fill;

    this.reportViewer1.Location = new System.Drawing.Point(0, 0);

    this.reportViewer1.Name = "reportViewer1";

    this.reportViewer1.Size = new System.Drawing.Size(454, 366);

    this.reportViewer1.TabIndex = 0;           

    this.splitContainer2.Panel2.Controls.Add(this.reportViewer1);

     

    this.reportViewer1.LocalReport.ReportPath = sPath;

                                      

    // prüfe ob Report mit Parameter?

    ReportParameterInfoCollection myParams = reportViewer1.LocalReport.GetParameters();

    if (myParams != null && myParams.Count > 0)

    {

    splitContainer2.Panel1Collapsed = false;

    ucReportParameters1.SetParamWindow(myParams);

    }

    else

    {

    splitContainer2.Panel1Collapsed = true;

    LoadReportData(null);

    }

    this.reportViewer1.LocalReport.ReportPath                      

    }

     

More Posts Next page »
Copyright (c) 2008 DevWare GmbH. All rights reserved.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems