# EF Core - Fonctions Partagées

> **Usage:** Référencé par toutes les commandes EF Core

---

## Détection Projet EF Core

```bash
detect_efcore_project() {
  CSPROJ=$(find . -name "*.csproj" -exec grep -l "Microsoft.EntityFrameworkCore" {} \; | head -1)
  PROJECT_DIR=$(dirname "$CSPROJ")
  PROJECT_NAME=$(basename "$CSPROJ" .csproj)
  MIGRATIONS_DIR="$PROJECT_DIR/Migrations"

  STARTUP_PROJECT=$(find . -name "*.Api.csproj" -o -name "*Web.csproj" | head -1)
  INFRA_PROJECT=$(find . -name "*Infrastructure.csproj" | head -1)
  [ -z "$INFRA_PROJECT" ] && INFRA_PROJECT="$CSPROJ"
}
```

---

## Détection Environnement

```bash
detect_environment() {
  API_DIR=$(find . -type d -name "*.Api" | head -1)
  [ -z "$API_DIR" ] && API_DIR="src/SmartStack.Api"

  # Priorité: --env flag > appsettings.Local.json > erreur
  if [ -n "$ENV_FLAG" ]; then
    SELECTED_ENV="appsettings.${ENV_FLAG}.json"
  elif [ -f "$API_DIR/appsettings.Local.json" ]; then
    SELECTED_ENV="appsettings.Local.json"
  else
    echo "ERROR: Aucun appsettings trouvé"; exit 1
  fi

  # Extraire connection string
  CONNECTION_STRING=$(powershell -Command "
    \$json = Get-Content '$API_DIR/$SELECTED_ENV' | ConvertFrom-Json
    \$json.ConnectionStrings.DefaultConnection
  ")
  DATABASE_NAME=$(echo "$CONNECTION_STRING" | grep -oP 'Database=\K[^;]+')
  SERVER_NAME=$(echo "$CONNECTION_STRING" | grep -oP 'Server=\K[^;]+')
}
```

---

## Protection Production

```bash
block_production() {
  if [[ "$SELECTED_ENV" == *"Production"* ]]; then
    echo "BLOQUÉ: Environnement Production détecté"
    echo "Utilisez un environnement non-production"
    exit 1
  fi
}
```

---

## Backup Base de Données

```bash
backup_database() {
  BACKUP_DIR=".claude/gitflow/backup/database"
  mkdir -p "$BACKUP_DIR"

  BACKUP_FILE="${BACKUP_DIR}/${DATABASE_NAME}_$(date +%Y%m%d_%H%M%S).bak"

  echo "Backup: $DATABASE_NAME → $BACKUP_FILE"

  sqlcmd -S "$SERVER_NAME" -E -Q "BACKUP DATABASE [$DATABASE_NAME] TO DISK='$BACKUP_FILE' WITH FORMAT, INIT, COMPRESSION"

  if [ $? -eq 0 ]; then
    echo "✓ Backup créé: $BACKUP_FILE"
    BACKUP_PATH="$BACKUP_FILE"
  else
    echo "⚠️ Backup échoué (sqlcmd non disponible ou erreur SQL)"
    BACKUP_PATH="none"
  fi
}
```

**Prérequis:** `sqlcmd` doit être installé et accessible dans le PATH.

---

## Détermination Branche Parente

**Règle d'or:** Le ModelSnapshot de référence doit venir de la branche parente (état validé).

```bash
determine_base_branch() {
  CURRENT_BRANCH=$(git branch --show-current)

  # Lire config GitFlow si disponible
  CONFIG_FILE=".claude/gitflow/config.json"
  GF_MAIN=$(grep -oP '"main":\s*"\K[^"]+' "$CONFIG_FILE" 2>/dev/null || echo "main")
  GF_DEVELOP=$(grep -oP '"develop":\s*"\K[^"]+' "$CONFIG_FILE" 2>/dev/null || echo "develop")

  case "$CURRENT_BRANCH" in
    feature/*|bugfix/*)
      BASE_BRANCH="$GF_DEVELOP"
      BRANCH_TYPE="feature"
      ;;
    develop)
      BASE_BRANCH="$GF_MAIN"
      BRANCH_TYPE="develop"
      ;;
    release/*)
      BASE_BRANCH="$GF_MAIN"
      BRANCH_TYPE="release"
      ;;
    hotfix/*)
      BASE_BRANCH="$GF_MAIN"
      BRANCH_TYPE="hotfix"
      ;;
    main|master)
      echo "❌ BLOQUÉ: Opération interdite sur $CURRENT_BRANCH"
      exit 1
      ;;
    *)
      echo "⚠️ Branche non reconnue: $CURRENT_BRANCH"
      echo "→ Utilisation de $GF_DEVELOP comme référence par défaut"
      BASE_BRANCH="$GF_DEVELOP"
      BRANCH_TYPE="unknown"
      ;;
  esac

  # Vérifier que la branche parente existe
  if ! git rev-parse --verify "origin/$BASE_BRANCH" >/dev/null 2>&1; then
    echo "❌ Branche parente '$BASE_BRANCH' introuvable sur origin"
    exit 1
  fi

  echo "Branche parente: $BASE_BRANCH"
}
```

| Branche courante | Branche parente | Raison |
|------------------|-----------------|--------|
| `feature/*` | `develop` | État validé de develop |
| `bugfix/*` | `develop` | État validé de develop |
| `develop` | `main` | État en production |
| `release/*` | `main` | État en production |
| `hotfix/*` | `main` | État en production |
| `main/master` | **BLOQUÉ** | Jamais squash sur branche protégée |

---

## Convention Nommage Migration

Pattern: `{context}_v{version}_{sequence}_{Description}`

| Contexte | Version | Séquence | Description | Exemple |
|----------|---------|----------|-------------|---------|
| core | 1.2.0 | 001 | AddUserRoles | `core_v1.2.0_001_AddUserRoles` |
| extensions | 1.3.0 | 001 | AddCustomFields | `extensions_v1.3.0_001_AddCustomFields` |

---

## Règle d'Or: 1 Migration par Feature

**Ne jamais accumuler plusieurs migrations sur une feature.** Si modification du modèle:
1. Supprimer la migration existante
2. Recréer avec `/efcore:migration`

---

## Règles de Conformité

| ❌ INTERDIT | ✅ OBLIGATOIRE |
|-------------|----------------|
| Scripts SQL (.sql) dans le repo | `dotnet ef database update` |
| `sqlcmd` pour DDL/DML | `dotnet ef migrations add` |
| `migrationBuilder.Sql()` INSERT | `HasData()` pour seeding |

**Exception:** `sqlcmd` est autorisé UNIQUEMENT pour `BACKUP DATABASE` (backup avant reset).

---

## MCP Tool Reference

**Tool:** `mcp__smartstack__check_migrations`

| Paramètre | Type | Description |
|-----------|------|-------------|
| `projectPath` | string | Chemin projet EF Core (auto-detect) |
| `branch` | string | Branche Git (current) |
| `compareBranch` | string | Branche de comparaison |

**Response:** `MigrationCheckResult { hasConflicts, migrations[], conflicts[], suggestions[] }`

---

## Niveaux de Risque Conflit

| Condition | Risque | Action |
|-----------|--------|--------|
| Snapshot = develop | NONE | Merge OK |
| Tables différentes | LOW | Merge OK |
| FK vers même table | MEDIUM | Ordre important |
| Même tables/colonnes | HIGH | Rebase requis |

---

## Configuration

`.claude/gitflow/config.json`:

```json
{
  "efcore": {
    "database": {
      "configFile": "appsettings.Local.json",
      "connectionStringName": "DefaultConnection"
    },
    "crossBranch": {
      "enabled": true,
      "blockOnConflict": true
    }
  }
}
```

---

## Contact Support

📧 **support@atlshub.ch**
