Rainer's profileCyrons BlogPhotosBlogListsMore Tools Help

Blog


    November 30

    Update in einem GridView mit foreign keys

    Es ist ein Kinderspiel, mit dem Designer eine Gridview zu erstellen, das alle 4 SQL-Funktionen anbietet - solange es sich um eine ganz simple DB-Tabelle handelt. Einfach die Tabelle vom Server Explorer auf die Design-Oberfläche der aspx-Seite ziehen, noch editing und deleting anhaken und fertig ist die Laube.

    Ganz anders stellt es sich dar, wenn die GridView Daten darstellen soll, die aus verschiedenen Tabellen stammen und diese auch noch miteinander in Beziehung stehen.

     

    Problem :

    Wandelt man im Dataset (xsd-File) die Basis-SELECT-Funktion so um, daß sie mittels INNER JOIN zu den in Beziehung stehenden Tabellen verbindet, werden die UPDATE-, INSERT- und Delete-Methoden nicht geschrieben.

    Lösung:

    Man schreibt die SELECT-Anweisung trotzdem mit INNER JOINS. Es müssen nur zusätzliche Funktionen für INSERT, UPDATE und DELETE geschrieben werden. Sinnvollerweise geschieht das in Form von Stored Procedures. Das entlastet den Webserver, weil ein Teil der Arbeit an den SQL-Server abgegeben wird.

     

    Problem:

    Die Tabelle, die foreign keys enthält wird für die GridView benutzt. Diese Keys sollen aber

    1.       nicht angezeigt werden und

    2.       es ist dem User nicht zuzumuten, daß er GUIDs eintippt um andere Daten der verbundenen Tabellen zu referenzieren.

    Lösung:

    Die PK-Spalte kann ganz einfach über das Bool-Property "Visible" ausgeblendet werden. FK-Spalten gibt es nicht mehr, aber dazu weiter unten mehr.

    Das mit den GUIDs ist nur leicht komplizierter, wirklich kein großes Ding.

    Hier ist eine Update-Funktion in Form einer Stored Procedure:

     

    ALTER PROCEDURE dbo.UpdatePC         

    (

    @PC_ID uniqueidentifier,

    @Bezeichnung varchar(50),

    @Inventarnummer varchar(50),

    @Kostentraeger varchar(50),

    @Seriennummer varchar(50),

    @Zustand varchar(50),

    @Sonderregelung varchar(255),

    @LieferantName varchar(50),

    @Herstellername varchar(50)

    )

     

    AS

    SET NOCOUNT OFF

    DECLARE @ID_Hersteller uniqueidentifier        

    DECLARE @ID_Lieferant uniqueidentifier

    DECLARE @ID_Zustand uniqueidentifier

    -- Zugehörige IDs holen

    SET @ID_Hersteller =  (SELECT Hersteller_ID FROM T_Hersteller WHERE (Herstellername = @Herstellername))

    SET @ID_Lieferant = (SELECT Lieferant_ID FROM T_Lieferant WHERE (LieferantName = @LieferantName))

    SET @ID_Zustand = (SELECT Zustand_ID FROM T_Zustand WHERE (Zustand = @Zustand))        

    -- Update

    UPDATE T_PC SET [Bezeichnung] = @Bezeichnung, [Inventarnummer] = @Inventarnummer, [Kostentraeger] = @Kostentraeger, [Seriennummer] = @Seriennummer, [ID_Zustand] = @ID_Zustand, [Sonderregelung] = @Sonderregelung, [ID_Lieferant] = @ID_Lieferant, [ID_Hersteller] = @ID_Hersteller WHERE (PC_ID = @PC_ID)

    RETURN

     

    Im Dataset wird der SELECT so umgebaut, daß die FK-Columns nicht mehr ausgewählt werden. Dafür werden die Felder der gebundenen Tabellen ausgewählt, die statt dessen angezeigt werden sollen.

    Hier die SELECT-Anweisung:

    SELECT

    T_PC.PC_ID,

    T_PC.Bezeichnung,

    T_PC.Inventarnummer,

    T_PC.Kostentraeger,

    T_PC.Seriennummer,

    T_PC.Sonderregelung,

    T_Hersteller.Herstellername,

    T_Lieferant.LieferantName,

    T_Zustand.Zustand

    FROM T_PC

    INNER JOIN T_Zustand ON T_PC.ID_Zustand = T_Zustand.Zustand_ID

    INNER JOIN T_Lieferant ON T_PC.ID_Lieferant = T_Lieferant.Lieferant_ID

    INNER JOIN T_Hersteller ON T_PC.ID_Hersteller = T_Hersteller.Hersteller_ID

     

    Zurück zur UPDATE-Funktion. Man sieht dort, daß die Werte der verbundenen Tabellen referenziert werden und damit die passende ID geholt wird, um sie in das FK-Feld der Haupt-Tabelle einzusetzen.

    Beispiel:

    SET @ID_Zustand = (SELECT Zustand_ID FROM T_Zustand WHERE (Zustand = @Zustand))

     

    Problem:

    Wie gibt man für die GridView verschiedene Funktionen an?

    Lösung:

    In der SQL-DataSource wird mit "Configure DataSource" "Specify a custom SQL-Statement or stored procedure" ausgewählt. Damit bekommt man für alle 4 SQL-Funktionen separate Möglichkeiten.

     

    Problem:

    SQLVariant kann nicht in uniqueidentifier umgewandelt werden

    So oder so ähnlich lautete die Exception.

    Alle Parameter des Typs GUID werden durch den Datasource-Designer als Typ Objekt behandelt. Hier handelt es sich um einen Bug in Visual Studio.

    Lösung:

    Den Typ-Spezifizierer im aspx-Code löschen. Beispiel:

    <InsertParameters>

                <asp:Parameter Name="PC_ID" />

                <asp:Parameter Name="Bezeichnung" Type="String" />

                <asp:Parameter Name="Inventarnummer" Type="String" />

                <asp:Parameter Name="Kostentraeger" Type="String" />

                <asp:Parameter Name="Seriennummer" Type="String" />

                <asp:Parameter Name="ID_Zustand" />

                <asp:Parameter Name="Sonderregelung" Type="String" />

                <asp:Parameter Name="ID_Lieferant" />

                <asp:Parameter Name="ID_Hersteller" />

     </InsertParameters>

     

    Hier kann man sehen daß alle ID-Parameter keinen Typ mehr haben.

     

    Problem:

    Procedure or function has too many arguments specified

    Diese Exception hat mich auch wieder ein paar graue Haare gekostet.

    Lösung:

    Alle Parameternamen müssen exakt den Feldnamen der Tabellen entsprechen!

    Die Trace-Funktion kann beim Aufspüren des Täters wertvolle Hilfe leisten.

    Um die Trace-Funktion zu aktivieren, schreibt man in die Headerzeile der aspx-Seite das Attribut Trace="true". Beispiel:

    <%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"

       CodeFile="PC.aspx.cs" Inherits="PC" Title="PC-Form" ErrorPage="~/GenericErrorPage.htm" Trace="true"%>

     

    Das reicht aber noch nicht. Damit die gewünschten Werte auch angezeigt werden, bedarf es einer Methode:

    /// <summary>

       /// Dieser Eventhandler liefert im Tracemodus alle Parameter, die an die Stored Procedure UpdatePC übergeben werden.

       /// </summary>

       /// <param name="sender"></param>

       /// <param name="e"></param>  

       protected void SqlDataSource_PC_Updating(object sender, SqlDataSourceCommandEventArgs e)

       {

          string TraceValue = string.Empty;

          object test = null;

          for(int i = 0; i < e.Command.Parameters.Count; i++)

          {

             Trace.Write(e.Command.Parameters[i].ParameterName);

             test = e.Command.Parameters[i].Value;

             if(test != null)

             {

                TraceValue = test.ToString();

                Trace.Write(TraceValue);

             }

          }

       }

     

    Problem:

    Der Primary Key der Haupttabelle wird bei einem UPDATE nicht übergeben.

    Lösung:

    Zuerst muß sichergestellt werden, daß der PK-Parameter überhaupt vom Designer generiert wird. Bei Bedarf muß er von Hand hinzugefügt werden. Dann braucht dieser Parameter folgende Attribute:

    Achso, erst einmal möchte ich schildern, wo diese überhaupt zu finden sind. Man wählt die SQL-DataSource aus.

    Rechts im Property-Fenster findet man unten "UpdateQuery" .

    In das Feld rechts daneben klicken und ganz rechts erscheint ein Button.

    Diesen Button klicken und ein neues Fenster öffnet sich. Hier werden alle Parameter aufgelistet.

    Nun zu den Attributen:

    Für den PK als ParameterSource "Control" wählen.

    Als Quelle wird die ID der GridView angegeben.

    Nun auf "advanced Properties" klicken.

    Bei "PropertyName" "DataKeys" auswählen.

    Richtig, zum Schluß muß das GridView-Property "DataKeyNames" noch den Namen des PK-Feldes gesetzt bekommen. Im aspx-Code sieht es dann so aus:

    <UpdateParameters>
                <asp:ControlParameter ControlID="grdv_PC" Name="PC_ID" PropertyName="DataKeys" />
                <asp:Parameter Name="Bezeichnung" Type="String" />
                <asp:Parameter Name="Inventarnummer" Type="String" />
                <asp:Parameter Name="Kostentraeger" Type="String" />
                <asp:Parameter Name="Seriennummer" Type="String" />
                <asp:Parameter Name="Zustand" Type="String" />
                <asp:Parameter Name="Sonderregelung" Type="String" />
                <asp:Parameter Name="LieferantName" Type="String" />
                <asp:Parameter Name="Herstellername" Type="String" />
                <asp:Parameter Direction="ReturnValue" Name="RETURN_VALUE" Type="Int32" />
     </UpdateParameters>

     

    Schlußbemerkungen

    Eigentlich werden die SQL-Anweisungen im Dataset gar nicht mehr benötigt. Durch die benutzerdefinierten Funktionen werden diese nicht mehr angefahren.

    November 22

    Fading MessageBox ohne Buttons

    Hi,
    ich habe gerade bei .Net-Snippets meinen MessageBox-Ersatz "FadingMessageBox" veröffentlicht. Wer interesse hat, kann sich den Code hier ziehen:
    http://www.dotnet-snippets.de/dns/fading-messagebox-ohne-buttons-SID694.aspx

    Das Ding hat auch schon die erste Bewertung bekommen - 9 von 10 Punkte!
    Dankeschön Herr Steitz. :-)
    http://www.codezone.de/DetailPage.Codezone?GUID=d844d3c4-2a29-4352-8a03-d1bdab878a4d