Compartir a través de


Cómo conectar tu aplicación de código a Azure SQL (versión preliminar)

En esta guía se explica cómo configurar una instancia de Azure SQL Database y conectarla a una aplicación de código de Power Apps mediante Power SDK.

Nota:

Las características en vista previa no se han diseñado para un uso de producción y pueden tener una funcionalidad restringida. Estas características están disponibles antes de una versión oficial para que los clientes puedan obtener acceso anticipado y proporcionar comentarios.

En esta guía se explica lo siguiente:

  • Aprovisionamiento de un Azure SQL Server y base de datos
  • Creación de tablas SQL y procedimientos almacenados
  • Conexión de una aplicación de código de Power Apps mediante Power SDK

Prerrequisitos

Configuración de Azure SQL Server y base de datos

  1. Vaya a Seleccionar opción de implementación de SQL en Microsoft Azure
  2. Seleccionar base de datos SQL -> Tipo de recurso: Base de datos única ->Crear
  3. Rellenar:
    • Grupo de recursos: seleccione Crear nuevo y escriba un nombre de grupo de recursos, por ejemplo, rg-codeapps-dev
    • Nombre de la base de datos: sqldb-codeapps-dev
    • Servidor: seleccione Crear nuevo y rellene:
      • Nombre del servidor: sql-codeapps-dev
      • Ubicación: seleccione la región más cercana al entorno de Power Platform.
      • Método de autenticación: Uso de la autenticación solo de Microsoft Entra
      • Establecer administrador de Microsoft Entra: seleccione Establecer administrador y, a continuación, Seleccione su propio usuario.
    • Seleccione Aceptar
  4. Proceso y almacenamiento: Uso general: sin servidor
  5. Selecciona Siguiente: Redes.
  6. Rellenar:
    • Método de conectividad: punto de conexión público
    • Permitir que los servicios y recursos de Azure accedan a este servidor:
    • Adición de la dirección IP del cliente actual:
  7. Seleccione Revisar y crear ->Crear.
  8. Espere hasta que finalice la implementación y, a continuación, seleccione Ir al recurso.

Despliegue de datos de ejemplo

  1. En Visual Studio Code, seleccione Extensiones (Ctrl + Mayús + X)

  2. Localiza la extensión SQL Server (mssql) en la Barra de Actividad y ábrela, o usa Ctrl + Alt + D.

  3. En Conexiones, seleccione + Agregar conexión.

    Agregar conexión en la extensión de SQL Server de VS Code

  4. En el cuadro de diálogo Conectar a base de datos , seleccione Examinar Azure, seleccione su suscripción, grupo de recursos (por ejemplo: rg-codeapps-dev), Servidor (por ejemplo: sql-codeapps-dev) y, a continuación, base de datos (por ejemplo sqldb-codeapps-dev )

  5. En Tipo de autenticación, seleccione Microsoft Entra ID - Universal con compatibilidad con MFA.

  6. Asegúrese de que tiene azure Portal abierto en el explorador y, después, seleccione Iniciar sesión. Se le pedirá que inicie sesión y, a continuación, vea:

    Solicitud de inicio de sesión de Microsoft Entra para la conexión SQL

  7. Seleccione Conectar.

    Conectado a Azure SQL Database en VS Code

  8. En el panel SQL SERVER, haga clic con el botón derecho en la base de datos y seleccione Nueva consulta.

    Comando New Query para la base de datos en la extensión SQL de VS Code

  9. En la nueva ventana de consulta, pegue el siguiente código SQL:

    -- 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. Seleccione el icono de reproducción verde (Ctrl-Mayús-E) para ejecutar la consulta.

  11. No debería ver ningún error en la salida RESULTADOS DE LA CONSULTA.

Inicializa tu aplicación de código

Si aún no lo ha realizado, cree o inicialice la aplicación de código siguiendo las instrucciones que se indican aquí: Creación de una aplicación desde cero.

Creación de una conexión de SQL Server en Power Platform

  1. Abra Power Apps

  2. Seleccione el entorno.

  3. Vaya a Conexiones. Podría estar en el ... Más menú.

  4. Seleccione + Nueva conexión.

    + Nueva conexión en Power Apps

  5. Selecciona SQL Server

  6. Seleccione Tipo de autenticación: Microsoft Entra ID Integrated

  7. Seleccione Crear e iniciar sesión en la ventana emergente de autenticación.

Adición de conexiones de tabla SQL a la aplicación

  1. Enumere las conexiones disponibles en su entorno. Debería ver la conexión que creó:

    pac connection list
    

    Debería ver una lista similar a la siguiente:

    Lista de conexiones de Power Platform que muestra la conexión SQL

  2. Para agregar la tabla de proyectos al proyecto, copie el identificador de conexión (la primera columna) y use el siguiente comando:

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

    Por ejemplo:

    pac code add-data-source -a "shared_sql" -c "aaaa0000bb11222233cc444444dddddd"  -d "sql-codeapps-dev.database.windows.net,sqldb-codeapps-dev" -sp "dbo.GetAllProjects"
    
  3. Abra la Services carpeta y Models y observe el código recién generado.

Agregar tabla de proyectos

  1. Usamos la interfaz de usuario de Fluent para mostrar una tabla de proyectos, así que cambie a React 18 e instale mediante:

    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. Agregue un nuevo archivo en src denominado ProjectsTable.tsx con el código siguiente:

    /**
     * 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. Agregue FluentProvider y ProjectsTable a maint.tsx:

     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. Ejecute la aplicación mediante:

    npm run dev
    

    En la ventana de comandos que se abre, abra el vínculo de la aplicación proporcionado:

    Consola del servidor de Power SDK con dirección URL y estado de la aplicación

  5. Cuando se abra la aplicación, debería ver un cuadro de diálogo de consentimiento, seleccione Permitir.

    Cuadro de diálogo de consentimiento que solicita permisos para la aplicación

  6. Debería ver la cuadrícula de datos de los proyectos:

    Cuadrícula de datos de proyectos con columnas y distintivos ordenables

Publicación de la aplicación en Power Apps

  1. Una vez que la aplicación esté lista para publicar y compartir, asegúrese de que el servidor Vite esté detenido mediante Ctrl + C y, a continuación, use el siguiente PowerShell:

    npm run build
    pac code push
    
  2. Abra la aplicación con el vínculo proporcionado para probarla.

    Aplicación publicada en Power Apps con el vínculo Abrir aplicación

Solución de problemas

En esta sección se tratan los problemas comunes que pueden surgir al configurar aplicaciones de código de Power Apps con Azure SQL Database.

Problemas de Azure SQL Database

Es posible que experimente estos problemas al usar bases de datos de Azure SQL.

No se puede conectar a Azure SQL Database

Síntomas:

  • Errores de tiempo de espera de conexión
  • Errores de autenticación al conectarse desde la extensión SQL de VS Code

Soluciones:

  1. Compruebe la configuración del firewall:

    • En Azure Portal, vaya a SQL Server.
    • Vaya a SeguridadRedes
    • Asegúrese de que "Permitir que los servicios y recursos de Azure accedan a este servidor" esté establecido en .
    • Adición de la dirección IP del cliente actual a las reglas de firewall
  2. Compruebe el método de autenticación:

    • Asegúrese de que usa el identificador de Entra de Microsoft: universal con compatibilidad con MFA en VS Code
    • Asegúrese de que ha iniciado sesión en la misma cuenta de Azure en Azure Portal y VS Code.
    • Pruebe a cerrar sesión y volver a iniciar sesión para actualizar los tokens de autenticación
  3. Compruebe la conectividad de red:

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

Errores de ejecución de consultas SQL

Síntomas:

  • Errores de denegación de permiso al ejecutar scripts SQL
  • Errores de objeto ya existente

Soluciones:

  1. Problemas de permisos:

    • Asegúrese de que la cuenta de usuario esté establecida como administrador de Microsoft Entra para SQL Server.
    • Compruebe que tiene db_owner o los permisos adecuados en la base de datos.
  2. Errores de Existencia del Objeto:

    • El script SQL incluye DROP instrucciones: son seguras para ejecutarse varias veces.
    • Si recibe errores de restricción, primero ejecute individualmente las sentencias DROP.

problemas de Node.js y npm

Es posible que experimente estos problemas al usar Node.js y npm.

Puerto 3000 ya en uso

Síntomas:

  • "EADDRINUSE: dirección que ya está en uso :::3000"
  • El servidor Vite no se inicia

Soluciones:

  1. Eliminación del proceso existente:

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

    • Para usar un puerto diferente, actualice vite.config.ts
    • Actualización de la configuración de Power SDK en consecuencia

Errores de instalación de paquetes

Síntomas:

  • Errores de instalación de npm
  • No se encontraron errores en el módulo

Soluciones:

  1. Borrar la caché de npm:

    npm cache clean --force
    npm install
    
  2. Elimine node_modules y vuelva a instalar:

    Remove-Item -Recurse -Force node_modules
    Remove-Item package-lock.json
    npm install
    
  3. Problemas de versión del nodo:

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

Errores de conexión en tiempo de ejecución

Síntomas:

  • "Error al cargar datos" en la aplicación React
  • Errores de conexión rechazada

Soluciones:

  1. Compruebe la conexión de Power Platform:

    • Comprobación de que la conexión de SQL Server funciona en Power Platform
    • Prueba de la conexión en Power Apps
  2. Problemas de consentimiento:

    • Asegúrese de dar su consentimiento cuando se cargue la aplicación por primera vez.
    • Borrar la memoria caché del explorador e intentarlo de nuevo
  3. Error de incompatibilidad del entorno:

    • Compruebe que está ejecutando la aplicación en el mismo entorno en el que se creó la conexión.
    • Compruebe que el perfil del explorador coincide con la cuenta de Power Platform.