Freigeben über


Vorgehensweise: Verbinden Ihrer Code-App mit Azure SQL (Vorschau)

In diesem Leitfaden wird erläutert, wie Sie eine Azure SQL-Datenbank einrichten und mit einer Power Apps-Code-App mithilfe des Power SDK verbinden.

Hinweis

Previewfunktionen sind nicht für den Produktionseinsatz gedacht und können eine eingeschränkte Funktionalität aufweisen. Diese Funktionen sind vor einer offiziellen Veröffentlichung verfügbar, damit Kunden frühzeitig zugreifen und Feedback geben können.

In diesem Leitfaden werden folgende Themen behandelt:

  • Bereitstellen eines Azure SQL Server und einer Datenbank
  • Erstellen von SQL-Tabellen und gespeicherten Prozeduren
  • Verbinden einer Power Apps-Code-App mit dem Power SDK

Voraussetzungen

Einrichten von Azure SQL Server und Datenbank

  1. Navigieren Sie zur Option "SQL-Bereitstellung auswählen" – Microsoft Azure
  2. Sql-Datenbank auswählen -> Ressourcentyp: Einzelne Datenbank ->Create
  3. Ausfüllen:
    • Ressourcengruppe: Wählen Sie " Neu erstellen" aus, und geben Sie einen Ressourcengruppennamen ein, z. B. rg-codeapps-dev
    • Datenbankname: sqldb-codeapps-dev
    • Server: Wählen Sie "Neu erstellen" und füllen Sie aus:
      • Server-Name: sql-codeapps-dev
      • Standort: Wählen Sie die nächstgelegene Region ihrer Power Platform-Umgebung aus.
      • Authentifizierungsmethode: Microsoft Entra-only-Authentifizierung verwenden
      • Microsoft Entra-Administrator festlegen: Wählen Sie "Administrator festlegen" und dann Ihren eigenen Benutzer aus.
    • Wählen Sie OK aus
  4. Compute + Speicher: Allgemeiner Zweck – Serverless
  5. Klicken Sie auf Weiter: Netzwerk.
  6. Ausfüllen:
    • Konnektivitätsmethode: Öffentlicher Endpunkt
    • Zulassen, dass Azure-Dienste und -Ressourcen auf diesen Server zugreifen können: Ja
    • Aktuelle Client-IP-Adresse hinzufügen: Ja
  7. Wählen Sie "Überprüfen" und ">" aus.
  8. Warten Sie, bis die Bereitstellung abgeschlossen ist, und wählen Sie dann "Zur Ressource wechseln" aus.

Bereitstellen von Beispieldaten

  1. Wählen Sie in Visual Studio Code Erweiterungen (STRG+UMSCHALT+X) aus.

  2. Suchen Sie die SQL Server-Erweiterung (mssql) auf der Aktivitätsleiste, und öffnen Sie sie, oder verwenden Sie STRG+ALT+D.

  3. Wählen Sie unter "Verbindungen" +Verbindung hinzufügen

    Hinzufügen einer Verbindung in vs Code SQL Server-Erweiterung

  4. Wählen Sie im Dialogfeld "Mit Datenbank verbinden" die Option "Azure durchsuchen" aus, wählen Sie Ihr Abonnement, die Ressourcengruppe (z. B.: rg-codeapps-dev), den Server (z. B.: sql-codeapps-dev) und dann die Datenbank (z. B.: sqldb-codeapps-dev) aus.

  5. Wählen Sie unter "Authentifizierungstyp" die Option "Microsoft Entra ID – Universal" mit MFA-Unterstützung aus.

  6. Stellen Sie sicher, dass Ihr Azure-Portal in Ihrem Browser geöffnet ist, und wählen Sie dann "Anmelden" aus. Sie sollten aufgefordert werden, sich anzumelden, und dann sehen Sie:

    Microsoft Entra-Anmeldeaufforderung für die SQL-Verbindung

  7. Wählen Sie "Verbinden" aus.

    Verbunden mit azure SQL-Datenbank in VS Code

  8. Klicken Sie im SQL SERVER-Bereich mit der rechten Maustaste auf Ihre Datenbank, und wählen Sie "Neue Abfrage" aus.

    Neuer Abfragebefehl für die Datenbank in der SQL-Erweiterung VS Code

  9. Fügen Sie im neuen Abfragefenster die folgende SQL-Datei ein:

    -- Drop existing objects if they exist
    IF OBJECT_ID('dbo.Projects', 'U') IS NOT NULL DROP TABLE dbo.Projects;
    
    -- =============================================
    -- CREATE TABLES
    -- =============================================
    
    -- Projects Table
    CREATE TABLE [dbo].[Projects](
        [ProjectId] [int] IDENTITY(1,1) NOT NULL,
        [Name] [nvarchar](255) NOT NULL,
        [Description] [nvarchar](max) NULL,
        [StartDate] [date] NULL,
        [EndDate] [date] NULL,
        [Status] [nvarchar](50) NOT NULL DEFAULT ('Planning'),
        [Priority] [nvarchar](20) NOT NULL DEFAULT ('Medium'),
        [Budget] [decimal](18, 2) NULL,
        [ProjectManagerEmail] [nvarchar](255) NOT NULL,
        [CreatedBy] [nvarchar](255) NOT NULL,
        [CreatedDate] [datetime2](7) NOT NULL DEFAULT (getutcdate()),
        [IsActive] [bit] NOT NULL DEFAULT (1),
        CONSTRAINT [PK_Projects] PRIMARY KEY ([ProjectId])
    );
    GO
    
    -- =============================================
    -- ADD CONSTRAINTS
    -- =============================================
    
    -- Project Status Check
    ALTER TABLE [dbo].[Projects] ADD CONSTRAINT [CK_Projects_Status] 
    CHECK ([Status] IN ('Planning', 'Active', 'On Hold', 'Completed', 'Cancelled'));
    
    -- Project Priority Check
    ALTER TABLE [dbo].[Projects] ADD CONSTRAINT [CK_Projects_Priority] 
    CHECK ([Priority] IN ('Low', 'Medium', 'High', 'Critical'));
    GO
    
    -- =============================================
    -- STORED PROCEDURES
    -- =============================================
    
    -- Get All Projects
    IF OBJECT_ID('dbo.GetAllProjects', 'P') IS NOT NULL DROP PROCEDURE dbo.GetAllProjects;
    GO
    CREATE PROCEDURE [dbo].[GetAllProjects]
    AS
    BEGIN
        SET NOCOUNT ON;
    
        SELECT 
            [ProjectId], [Name], [Description], [StartDate], [EndDate],
            [Status], [Priority], [Budget], [ProjectManagerEmail],
            [CreatedBy], [CreatedDate], [IsActive]
        FROM [dbo].[Projects]
        WHERE [IsActive] = 1
        ORDER BY [CreatedDate] DESC;
    END
    GO
    
    -- Create Project
    IF OBJECT_ID('dbo.CreateProject', 'P') IS NOT NULL DROP PROCEDURE dbo.CreateProject;
    GO
    CREATE PROCEDURE [dbo].[CreateProject]
        @Name NVARCHAR(255),
        @Description NVARCHAR(MAX) = NULL,
        @StartDate DATE = NULL,
        @EndDate DATE = NULL,
        @Status NVARCHAR(50) = 'Planning',
        @Priority NVARCHAR(20) = 'Medium',
        @Budget DECIMAL(18,2) = NULL,
        @ProjectManagerEmail NVARCHAR(255),
        @CreatedBy NVARCHAR(255)
    AS
    BEGIN
        SET NOCOUNT ON;
    
        INSERT INTO [dbo].[Projects] (
            [Name], [Description], [StartDate], [EndDate], 
            [Status], [Priority], [Budget], [ProjectManagerEmail], [CreatedBy]
        )
        VALUES (
            @Name, @Description, @StartDate, @EndDate,
            @Status, @Priority, @Budget, @ProjectManagerEmail, @CreatedBy
        );
    
        SELECT SCOPE_IDENTITY() as ProjectId;
    END
    GO
    
    -- Update Project
    IF OBJECT_ID('dbo.UpdateProject', 'P') IS NOT NULL DROP PROCEDURE dbo.UpdateProject;
    GO
    CREATE PROCEDURE [dbo].[UpdateProject]
        @ProjectId INT,
        @Name NVARCHAR(255) = NULL,
        @Description NVARCHAR(MAX) = NULL,
        @StartDate DATE = NULL,
        @EndDate DATE = NULL,
        @Status NVARCHAR(50) = NULL,
        @Priority NVARCHAR(20) = NULL,
        @Budget DECIMAL(18,2) = NULL,
        @ProjectManagerEmail NVARCHAR(255) = NULL
    AS
    BEGIN
        SET NOCOUNT ON;
    
        UPDATE [dbo].[Projects]
        SET 
            [Name] = ISNULL(@Name, [Name]),
            [Description] = ISNULL(@Description, [Description]),
            [StartDate] = ISNULL(@StartDate, [StartDate]),
            [EndDate] = ISNULL(@EndDate, [EndDate]),
            [Status] = ISNULL(@Status, [Status]),
            [Priority] = ISNULL(@Priority, [Priority]),
            [Budget] = ISNULL(@Budget, [Budget]),
            [ProjectManagerEmail] = ISNULL(@ProjectManagerEmail, [ProjectManagerEmail])
        WHERE [ProjectId] = @ProjectId AND [IsActive] = 1;
    
        SELECT @@ROWCOUNT as RowsAffected;
    END
    GO
    
    -- Delete Project (Soft Delete)
    IF OBJECT_ID('dbo.DeleteProject', 'P') IS NOT NULL DROP PROCEDURE dbo.DeleteProject;
    GO
    CREATE PROCEDURE [dbo].[DeleteProject]
        @ProjectId INT
    AS
    BEGIN
        SET NOCOUNT ON;
    
        UPDATE [dbo].[Projects]
        SET [IsActive] = 0
        WHERE [ProjectId] = @ProjectId AND [IsActive] = 1;
    
        SELECT @@ROWCOUNT as RowsAffected;
    END
    GO
    
    -- =============================================
    -- SAMPLE DATA
    -- =============================================
    
    -- Insert Sample Projects
    INSERT INTO [dbo].[Projects] ([Name], [Description], [StartDate], [EndDate], [Status], [Priority], [Budget], [ProjectManagerEmail], [CreatedBy]) VALUES
    ('Website Redesign', 'Complete redesign of company website with modern UI/UX', '2025-06-01', '2025-08-31', 'Active', 'High', 75000.00, 'sarah.johnson@company.com', 'admin@company.com'),
    ('Mobile App Development', 'Develop iOS and Android mobile application for customer portal', '2025-07-01', '2025-12-31', 'Planning', 'Critical', 150000.00, 'mike.chen@company.com', 'admin@company.com'),
    ('Database Migration', 'Migrate legacy database to cloud infrastructure', '2025-05-15', '2025-09-30', 'Active', 'Medium', 50000.00, 'lisa.williams@company.com', 'admin@company.com');
    GO
    
    PRINT 'Projects-only database schema created successfully with sample data!';
    
  10. Wählen Sie das grüne Wiedergabesymbol (STRG-UMSCHALT-E) aus, um die Abfrage auszuführen.

  11. In der ABFRAGEERGEBNISSE-Ausgabe sollten keine Fehler angezeigt werden.

Initialisiere deine Code-App

Falls noch nicht, erstellen und/oder initialisieren Sie Ihre Code-App mithilfe der hier aufgeführten Anweisungen: Erstellen Sie eine App von Grund auf neu.

Erstellen einer SQL Server-Verbindung in Power Platform

  1. Öffnen von Power Apps

  2. Wählen Sie Ihre Umgebung aus

  3. Navigieren Sie zu "Verbindungen". Es könnte im ... Mehr-Menü sein.

  4. Wählen Sie + Neue Verbindung aus.

    + Neue Verbindung in Power Apps

  5. SQL Server auswählen

  6. Authentifizierungstyp auswählen: Microsoft Entra ID Integrated

  7. Wählen Sie "Erstellen " aus, und melden Sie sich in der Popupauthentifizierungsaufforderung an.

Hinzufügen von SQL-Tabellenverbindungen zu Ihrer App

  1. Auflisten der verfügbaren Verbindungen in Ihrer Umgebung. Die erstellte Verbindung sollte angezeigt werden:

    pac connection list
    

    Es sollte eine Liste angezeigt werden, die ähnlich wie die folgende ist:

    Liste der Power Platform-Verbindungen mit SQL-Verbindung

  2. Um die Projekttabelle zum Projekt hinzuzufügen, kopieren Sie die Verbindungs-ID (die erste Spalte), und verwenden Sie den folgenden Befehl:

    pac code add-data-source -a "shared_sql" -c "[CONNECTION ID]"  -d "[SQL SERVER NAME].database.windows.net,[DATA BASE NAME]" -sp "dbo.GetAllProjects"
    

    Beispiel:

    pac code add-data-source -a "shared_sql" -c "aaaa0000bb11222233cc444444dddddd"  -d "sql-codeapps-dev.database.windows.net,sqldb-codeapps-dev" -sp "dbo.GetAllProjects"
    
  3. Öffnen Sie die Ordner Services und Models und betrachten Sie den neu generierten Code.

Tabelle mit Projekten hinzufügen

  1. Wir verwenden Fluent UI, um eine Tabelle mit Projekten anzuzeigen. Führen Sie ein Downgrade auf React 18 durch und installieren Sie es mit:

    npm install react@^18.0.0 react-dom@^18.0.0 @types/react@^18.0.0 @types/react-dom@^18.0.0
    npm install @fluentui/react-components
    
  2. Fügen Sie eine neue Datei unter src dem Namen ProjectsTable.tsx mit dem folgenden Code hinzu:

    /**
     * ProjectsTable Component - Displays project data from Power Platform in a sortable DataGrid
     */
    import React, { useEffect, useState, useCallback, useMemo } from 'react';
    import {
      DataGrid,
      DataGridHeader,
      DataGridRow,
      DataGridHeaderCell,
      DataGridCell,
      DataGridBody,
      TableColumnDefinition,
      TableRowId,
      Spinner,
      MessageBar,
      Badge,
      makeStyles,
      tokens,
    } from '@fluentui/react-components';
    import { GetAllProjectsService } from './Services/GetAllProjectsService';
    
    // String formatting utility for localizable messages
    const formatMessage = (template: string, params: Record<string, string | number> = {}): string => {
      return template.replace(/\{(\w+)\}/g, (match, key) => {
        const value = params[key];
        return value !== undefined ? String(value) : match;
      });
    };
    
    // Common UI messages
    const MESSAGE_STRINGS = {
      LOADING: 'Loading data...',
      NO_DATA: 'No data found.',
      GENERIC_ERROR: 'An unexpected error occurred',
      LOAD_ERROR: 'Failed to load data. Please try again.',
      PROJECT_COUNTER_SINGLE: 'Showing {count} project',
      PROJECT_COUNTER_PLURAL: 'Showing {count} projects',
      COLUMN_PROJECT_NAME: 'Project Name',
      COLUMN_DESCRIPTION: 'Description',
      COLUMN_START_DATE: 'Start Date',
      COLUMN_END_DATE: 'End Date',
      COLUMN_STATUS: 'Status',
      COLUMN_PRIORITY: 'Priority',
      ARIA_LABEL_DATA_GRID: 'Projects data grid',
    } as const;
    
    // Project data type
    type ProjectItem = {
      ProjectId?: number;
      Name?: string;
      Description?: string;
      StartDate?: string;
      EndDate?: string;
      Status?: string;
      Priority?: string;
      Budget?: number;
      ProjectManagerEmail?: string;
      CreatedBy?: string;
      CreatedDate?: string;
      IsActive?: boolean;
    };
    
    // DataGrid columns
    const COLUMNS: TableColumnDefinition<ProjectItem>[] = [
      {
        columnId: 'name',
        compare: (a, b) => (a.Name || '').localeCompare(b.Name || ''),
        renderHeaderCell: () => MESSAGE_STRINGS.COLUMN_PROJECT_NAME,
        renderCell: (item) => item.Name || '',
      },
      {
        columnId: 'description',
        compare: (a, b) => (a.Description || '').localeCompare(b.Description || ''),
        renderHeaderCell: () => MESSAGE_STRINGS.COLUMN_DESCRIPTION,
        renderCell: (item) => item.Description || '',
      },
      {
        columnId: 'startDate',
        compare: (a, b) => new Date(a.StartDate || '').getTime() - new Date(b.StartDate || '').getTime(),
        renderHeaderCell: () => MESSAGE_STRINGS.COLUMN_START_DATE,
        renderCell: (item) => item.StartDate ? new Date(item.StartDate).toLocaleDateString() : '',
      },
      {
        columnId: 'endDate',
        compare: (a, b) => new Date(a.EndDate || '').getTime() - new Date(b.EndDate || '').getTime(),
        renderHeaderCell: () => MESSAGE_STRINGS.COLUMN_END_DATE,
        renderCell: (item) => item.EndDate ? new Date(item.EndDate).toLocaleDateString() : '',
      }, {
        columnId: 'status',
        compare: (a, b) => (a.Status || '').localeCompare(b.Status || ''),
        renderHeaderCell: () => MESSAGE_STRINGS.COLUMN_STATUS,
        renderCell: (item) => <StatusBadge status={item.Status || ''} />,
      },
      {
        columnId: 'priority',
        compare: (a, b) => (a.Priority || '').localeCompare(b.Priority || ''),
        renderHeaderCell: () => MESSAGE_STRINGS.COLUMN_PRIORITY,
        renderCell: (item) => <PriorityBadge priority={item.Priority || ''} />,
      },
    ];
    
    // Row ID generator
    const getRowId = (item: ProjectItem): TableRowId =>
      item.ProjectId?.toString() || `temp-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
    
    // Extracts a user-friendly error message from various error types
    const extractErrorMessage = (
      error: unknown,
      fallbackMessage = MESSAGE_STRINGS.GENERIC_ERROR
    ): string => {
      if (error instanceof Error) {
        return error.message;
      }
      if (typeof error === 'string') {
        return error;
      } return fallbackMessage;
    };
    
    // Badge component for Priority
    const PriorityBadge: React.FC<{ priority: string }> = React.memo(({ priority }) => {
      const styles = useStyles();
      const badgeProps = useMemo(() => {
        const getPriorityAppearance = (priority: string) => {
          switch (priority?.toLowerCase()) {
            case 'critical':
              return { appearance: 'filled' as const, color: 'danger' as const };
            case 'high':
              return { appearance: 'filled' as const, color: 'important' as const };
            case 'medium':
              return { appearance: 'filled' as const, color: 'warning' as const };
            case 'low':
              return { appearance: 'filled' as const, color: 'success' as const };
            default:
              return { appearance: 'outline' as const, color: 'subtle' as const };
          }
        };
        return getPriorityAppearance(priority);
      }, [priority]);
    
      return (
        <Badge {...badgeProps} className={styles.badge}>
          {priority || 'Unknown'}
        </Badge>
      );
    });
    
    PriorityBadge.displayName = 'PriorityBadge';
    
    // Badge component for Status
    const StatusBadge: React.FC<{ status: string }> = React.memo(({ status }) => {
      const styles = useStyles();
      const badgeProps = useMemo(() => {
        const getStatusAppearance = (status: string) => {
          switch (status?.toLowerCase()) {
            case 'completed':
              return { appearance: 'filled' as const, color: 'success' as const };
            case 'active':
              return { appearance: 'filled' as const, color: 'brand' as const };
            case 'planning':
              return { appearance: 'filled' as const, color: 'informative' as const };
            case 'on hold':
              return { appearance: 'filled' as const, color: 'warning' as const };
            case 'cancelled':
              return { appearance: 'filled' as const, color: 'danger' as const };
            default:
              return { appearance: 'outline' as const, color: 'subtle' as const };
          }
        };
        return getStatusAppearance(status);
      }, [status]);
    
      return (
        <Badge {...badgeProps} className={styles.badge}>
          {status || 'Unknown'}
        </Badge>
      );
    });
    
    StatusBadge.displayName = 'StatusBadge';
    
    // Styles
    const useStyles = makeStyles({
      container: {
        padding: tokens.spacingVerticalXXL,
      },
      loadingContainer: {
        display: 'flex',
        alignItems: 'center',
        gap: tokens.spacingHorizontalS,
        padding: tokens.spacingVerticalXXL,
      },
      messageBar: {
        marginBottom: tokens.spacingVerticalXL,
      },
      projectCounter: {
        marginBottom: tokens.spacingVerticalM,
        fontSize: tokens.fontSizeBase200,
        color: tokens.colorNeutralForeground2,
      }, dataGrid: {
        width: '100%',
      },
      badge: {
        fontSize: tokens.fontSizeBase200,
        fontWeight: tokens.fontWeightMedium,
        textTransform: 'capitalize',
      },
    });
    
    // Custom hook to fetch and manage project data
    const useProjectsData = (): {
      projects: ProjectItem[];
      loading: boolean;
      error: string | null;
      refetch: () => Promise<void>;
    } => {
      const [projects, setProjects] = useState<ProjectItem[]>([]);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState<string | null>(null);
    
      const fetchProjects = useCallback(async () => {
        try {
          setLoading(true);
          setError(null); const result = await GetAllProjectsService.GetAllProjects();
          if (result.success && result.data?.ResultSets?.Table1) {
            const projectsData = Array.isArray(result.data.ResultSets.Table1)
              ? result.data.ResultSets.Table1 as ProjectItem[]
              : [result.data.ResultSets.Table1] as ProjectItem[];
            setProjects(projectsData);
          } else {
            const errorMsg = result.error instanceof Error
              ? result.error.message
              : result.error || MESSAGE_STRINGS.LOAD_ERROR;
            setError(errorMsg);
            console.error('Failed to fetch projects:', result.error);
          }
        } catch (error) {
          const errorMessage = extractErrorMessage(error, MESSAGE_STRINGS.GENERIC_ERROR);
          setError(errorMessage);
          console.error('Error fetching projects:', error);
        } finally {
          setLoading(false);
        }
      }, []);
    
      useEffect(() => {
        fetchProjects();
      }, [fetchProjects]);
    
      return { projects, loading, error, refetch: fetchProjects };
    };
    
    // UI Components
    const LoadingSpinner: React.FC = () => {
      const styles = useStyles();
      return (
        <div className={styles.loadingContainer}>
          <Spinner size="small" />
          <span>{MESSAGE_STRINGS.LOADING}</span>
        </div>
      );
    };
    
    const ErrorMessage: React.FC<{ error: string }> = ({ error }) => {
      const styles = useStyles();
      return (
        <MessageBar intent="error" className={styles.messageBar}>
          {error}
        </MessageBar>
      );
    };
    
    const EmptyState: React.FC = () => {
      const styles = useStyles();
      return (
        <MessageBar intent="info" className={styles.messageBar} style={{ textAlign: 'center' }}>
          {MESSAGE_STRINGS.NO_DATA}
        </MessageBar>
      );
    };
    
    const ProjectCounter: React.FC<{ count: number }> = ({ count }) => {
      const styles = useStyles();
    
      const counterMessage = useMemo(() => {
        return count === 1
          ? formatMessage(MESSAGE_STRINGS.PROJECT_COUNTER_SINGLE, { count })
          : formatMessage(MESSAGE_STRINGS.PROJECT_COUNTER_PLURAL, { count });
      }, [count]);
    
      return (
        <div className={styles.projectCounter}>
          {counterMessage}
        </div>
      );
    };
    
    // Main component
    const ProjectsTable: React.FC = () => {
      const styles = useStyles();
      const { projects, loading, error } = useProjectsData();
      const projectCount = useMemo(() => projects.length, [projects.length]);
      const memoizedProjects = useMemo(() => projects, [projects]);
      const dataGridProps = useMemo(() => ({
        items: memoizedProjects,
        columns: COLUMNS,
        sortable: true,
        getRowId,
        focusMode: "cell" as const,
        className: styles.dataGrid,
        "aria-label": MESSAGE_STRINGS.ARIA_LABEL_DATA_GRID,
      }), [memoizedProjects, styles.dataGrid]);
    
      if (loading) {
        return (
          <div className={styles.container}>
            <LoadingSpinner />
          </div>
        );
      }
    
      if (error) {
        return (
          <div className={styles.container}>
            <ErrorMessage error={error} />
          </div>
        );
      }
    
      if (projectCount === 0) {
        return (
          <div className={styles.container}>
            <EmptyState />
          </div>
        );
      }
      return (
        <div className={styles.container}>
          <ProjectCounter count={projectCount} />      <DataGrid {...dataGridProps}>
            <DataGridHeader>
              <DataGridRow>
                {({ renderHeaderCell }) => (
                  <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>
                )}
              </DataGridRow>
            </DataGridHeader>
            <DataGridBody<ProjectItem>>
              {({ item, rowId }) => (
                <DataGridRow<ProjectItem> key={rowId}>
                  {({ renderCell }) => (
                    <DataGridCell>{renderCell(item)}</DataGridCell>
                  )}
                </DataGridRow>
              )}
            </DataGridBody>
          </DataGrid>
        </div>
      );
    };
    
    export default React.memo(ProjectsTable);
    
    
  3. Fügen Sie FluentProvider und ProjectsTable zu maint.tsx hinzu.

     import { StrictMode } from 'react'
     import { createRoot } from 'react-dom/client'
     import './index.css'
     import PowerProvider from './PowerProvider.tsx'
     import { FluentProvider, webLightTheme } from '@fluentui/react-components'
     import ProjectsTable from './ProjectsTable.tsx'
    
     createRoot(document.getElementById('root')!).render(
       <StrictMode>
         <PowerProvider>
           <FluentProvider theme={webLightTheme}>
             <ProjectsTable />
           </FluentProvider>
         </PowerProvider>
       </StrictMode>,
     )
    
    
  4. Führen Sie Ihre App aus mit:

    npm run dev
    

    Öffnen Sie im daraufhin geöffneten Befehlsfenster den bereitgestellten App-Link:

    Power SDK-Serverkonsole mit App-URL und -Status

  5. Wenn die App geöffnet wird, sollte ein Zustimmungsdialogfeld angezeigt werden, wählen Sie "Zulassen" aus.

    Zustimmungsdialogfeld, in dem Berechtigungen für die App angefordert werden

  6. Du solltest ein Datenraster der Projekte sehen:

    Projekt-Datenraster mit sortierbaren Spalten und Abzeichen

Veröffentlichen der App in Power Apps

  1. Nachdem Ihre App für die Veröffentlichung und Freigabe bereit ist, stellen Sie sicher, dass der Vite-Server mit STRG+C beendet wird, und verwenden Sie dann die folgende PowerShell:

    npm run build
    pac code push
    
  2. Öffnen Sie die App über den bereitgestellten Link, um sie zu testen!

    In Power Apps veröffentlichte App mit Link

Problembehandlung

In diesem Abschnitt werden häufig auftretende Probleme behandelt, die beim Einrichten von Power Apps-Code-Apps mit Azure SQL-Datenbank auftreten können.

Azure SQL-Datenbankprobleme

Möglicherweise treten diese Probleme bei der Verwendung von Azure SQL-Datenbanken auf.

Verbindung mit Azure SQL-Datenbank nicht möglich

Symptome:

  • Verbindungs-Timeout-Fehler
  • Authentifizierungsfehler beim Herstellen einer Verbindung mit der SQL-Erweiterung von VS Code

Lösungen:

  1. Firewalleinstellungen überprüfen:

    • Navigieren Sie im Azure-Portal zu Ihrem SQL Server
    • Wechseln sie zu SecurityNetworking
    • Stellen Sie sicher, dass "Azure-Dienste und -Ressourcen für den Zugriff auf diesen Server zulassen" auf "Ja" festgelegt ist.
    • Hinzufügen Ihrer aktuellen Client-IP-Adresse zu den Firewallregeln
  2. Authentifizierungsmethode überprüfen:

    • Stellen Sie sicher, dass Sie Microsoft Entra ID – Universal mit MFA-Unterstützung in VS Code verwenden
    • Stellen Sie sicher, dass Sie sowohl im Azure-Portal als auch im VS-Code bei demselben Azure-Konto angemeldet sind
    • Versuchen Sie, sich abzumelden und wieder einzuchecken, um Authentifizierungstoken zu aktualisieren.
  3. Überprüfen der Netzwerkkonnektivität:

    # Test connectivity to SQL Server
    Test-NetConnection -ComputerName "your-sql-server.database.windows.net" -Port 1433
    

SQL-Abfrageausführungsfehler

Symptome:

  • Fehler aufgrund verweigerter Berechtigung beim Ausführen von SQL-Skripts
  • Objekt enthält bereits Fehler.

Lösungen:

  1. Berechtigungsprobleme:

    • Stellen Sie sicher, dass Ihr Benutzerkonto als Microsoft Entra-Administrator für sql Server festgelegt ist.
    • Überprüfen Sie, ob Sie über db_owner oder entsprechende Berechtigungen für die Datenbank verfügen.
  2. Existenzfehler des Objekts:

    • Das SQL-Skript enthält DROP Anweisungen – diese sind sicher, mehrmals auszuführen.
    • Wenn Einschränkungsfehler auftreten, führen Sie die Drop-Anweisungen zuerst einzeln aus.

Node.js- und npm-Probleme

Bei verwendung von Node.js und npm können diese Probleme auftreten.

Port 3000 wird bereits verwendet

Symptome:

  • "EADDRINUSE: Adresse, die bereits verwendet wird :::3000"
  • Der Vite-Server wird nicht gestartet.

Lösungen:

  1. Vorhandenen Prozess beenden:

    # Find process using port 3000
    netstat -ano | findstr :3000
    # Kill the process (replace PID with actual process ID)
    taskkill /PID [PID] /F
    
  2. Alternativer Port verwenden:

    • Um einen anderen Port zu verwenden, aktualisieren Sie vite.config.ts
    • Aktualisieren Sie die Power SDK-Konfiguration entsprechend

Paketinstallationsfehler

Symptome:

  • npm-Installationsfehler
  • Modul nicht gefunden-Fehler

Lösungen:

  1. Npm-Cache löschen:

    npm cache clean --force
    npm install
    
  2. Löschen Sie node_modules, und installieren Sie es erneut:

    Remove-Item -Recurse -Force node_modules
    Remove-Item package-lock.json
    npm install
    
  3. Probleme mit der Knotenversion:

    # Check Node version
    node --version
    # Should be LTS version (18.x or 20.x)
    

Laufzeitverbindungsfehler

Symptome:

  • "Daten konnten nicht geladen werden" in der React-App
  • Verbindung verweigert-Fehler

Lösungen:

  1. Überprüfen Sie die Power Platform-Verbindung:

    • Überprüfen, ob die SQL Server-Verbindung in Power Platform funktioniert
    • Testen der Verbindung in Power Apps
  2. Zustimmungsprobleme:

    • Stellen Sie sicher, dass Sie die Zustimmung erteilen, wenn die App zum ersten Mal geladen wird.
    • Löschen sie den Browsercache, und versuchen Sie es erneut.
  3. Umgebungskonflikt:

    • Überprüfen, ob Sie die App in derselben Umgebung ausführen, in der die Verbindung erstellt wurde
    • Überprüfen Sie, ob das Browserprofil Ihrem Power Platform-Konto entspricht.