Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 324 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ dotnet build

`JD.Efcpt.Build` transforms EF Core Power Tools into a **fully automated build step**. Instead of manually regenerating your EF Core models in Visual Studio, this package:

✅ **Automatically builds** your SQL Server Database Project (`.sqlproj`) to a DACPAC
✅ **Runs EF Core Power Tools** CLI during `dotnet build`
✅ **Generates DbContext and entities** from your database schema
✅ **Intelligently caches** - only regenerates when schema or config changes
✅ **Works everywhere** - local dev, CI/CD, Docker, anywhere .NET runs
✅ **Automatically builds** your SQL Server Database Project (`.sqlproj`) to a DACPAC
✅ **OR connects directly** to your database via connection string (**NEW in v0.2.0**)
✅ **Runs EF Core Power Tools** CLI during `dotnet build`
✅ **Generates DbContext and entities** from your database schema
✅ **Intelligently caches** - only regenerates when schema or config changes
✅ **Works everywhere** - local dev, CI/CD, Docker, anywhere .NET runs
✅ **Zero manual steps** - true database-first development automation

### Architecture
Expand Down Expand Up @@ -368,6 +369,324 @@ Customize table and column naming:

---

## 🔌 Connection String Mode (NEW in v0.2.0)

### Overview

Starting with version 0.2.0, `JD.Efcpt.Build` supports **direct database connection** as an alternative to DACPAC-based workflows. This "**connection string mode**" allows you to reverse-engineer your EF Core models directly from a live database without requiring a `.sqlproj` file.

**Key Benefits:**

✅ **No DACPAC required** - Connect directly to your database
✅ **Faster iteration** - Skip the `.sqlproj` build step
✅ **Flexible configuration** - Use appsettings.json, app.config, or explicit properties
✅ **Auto-discovery** - Automatically finds connection strings in your project
✅ **100% backward compatible** - Existing DACPAC workflows continue to work unchanged

### When to Use Connection String Mode vs DACPAC Mode

**Use Connection String Mode When:**

- You don't have a SQL Server Database Project (`.sqlproj`)
- You want faster builds (no DACPAC compilation step)
- You're working with a cloud database or managed database instance
- You prefer to scaffold from a live database environment

**Use DACPAC Mode When:**

- You have an existing `.sqlproj` that defines your schema
- You want schema versioning through database projects
- You prefer design-time schema validation
- Your CI/CD already builds DACPACs

### Configuration Methods

#### Method 1: Explicit Connection String (Highest Priority)

Set the connection string directly in your `.csproj`:

```xml
<PropertyGroup>
<EfcptConnectionString>Server=localhost;Database=MyDb;Integrated Security=True;</EfcptConnectionString>
</PropertyGroup>
```

Or use environment variables for security:

```xml
<PropertyGroup>
<EfcptConnectionString>$(DB_CONNECTION_STRING)</EfcptConnectionString>
</PropertyGroup>
```

#### Method 2: appsettings.json (ASP.NET Core)

**Recommended for ASP.NET Core projects.** Place your connection string in `appsettings.json`:

```json
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyDb;Integrated Security=True;"
}
}
```

Then configure in your `.csproj`:

```xml
<PropertyGroup>
<!-- Points to the appsettings.json file -->
<EfcptAppSettings>appsettings.json</EfcptAppSettings>

<!-- Optional: specify which key to use (defaults to "DefaultConnection") -->
<EfcptConnectionStringName>DefaultConnection</EfcptConnectionStringName>
</PropertyGroup>
```

You can also reference environment-specific files:

```xml
<PropertyGroup Condition="'$(Configuration)' == 'Development'">
<EfcptAppSettings>appsettings.Development.json</EfcptAppSettings>
</PropertyGroup>
```

#### Method 3: app.config or web.config (.NET Framework)

**Recommended for .NET Framework projects.** Add your connection string to `app.config` or `web.config`:

```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="DefaultConnection"
connectionString="Server=localhost;Database=MyDb;Integrated Security=True;"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
```

Configure in your `.csproj`:

```xml
<PropertyGroup>
<EfcptAppConfig>app.config</EfcptAppConfig>
<EfcptConnectionStringName>DefaultConnection</EfcptConnectionStringName>
</PropertyGroup>
```

#### Method 4: Auto-Discovery (Zero Configuration)

If you don't specify any connection string properties, `JD.Efcpt.Build` will **automatically search** for connection strings in this order:

1. **appsettings.json** in your project directory
2. **appsettings.Development.json** in your project directory
3. **app.config** in your project directory
4. **web.config** in your project directory

If a connection string named `DefaultConnection` exists, it will be used. If not, the **first available connection string** will be used (with a warning logged).

**Example - Zero configuration:**

```
MyApp/
├── MyApp.csproj
└── appsettings.json ← Connection string auto-discovered here
```

No properties needed! Just run `dotnet build`.

### Discovery Priority Chain

When multiple connection string sources are present, this priority order is used:

1. **`EfcptConnectionString`** property (highest priority)
2. **`EfcptAppSettings`** or **`EfcptAppConfig`** explicit paths
3. **Auto-discovered** configuration files
4. **Fallback to `.sqlproj`** (DACPAC mode) if no connection string found

### Migration Guide: From DACPAC Mode to Connection String Mode

#### Before (DACPAC Mode)

```xml
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="JD.Efcpt.Build" Version="1.x.x" />
</ItemGroup>

<PropertyGroup>
<EfcptSqlProj>..\Database\Database.sqlproj</EfcptSqlProj>
</PropertyGroup>
</Project>
```

#### After (Connection String Mode)

**Option A: Explicit connection string**

```xml
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="JD.Efcpt.Build" Version="2.x.x" />
</ItemGroup>

<PropertyGroup>
<EfcptConnectionString>Server=localhost;Database=MyDb;Integrated Security=True;</EfcptConnectionString>
</PropertyGroup>
</Project>
```

**Option B: Use existing appsettings.json (Recommended)**

```xml
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="JD.Efcpt.Build" Version="2.x.x" />
</ItemGroup>

<PropertyGroup>
<EfcptAppSettings>appsettings.json</EfcptAppSettings>
</PropertyGroup>
</Project>
```

**Option C: Auto-discovery (Simplest)**

```xml
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="JD.Efcpt.Build" Version="2.x.x" />
</ItemGroup>

<!-- No connection string config needed! -->
<!-- Will auto-discover from appsettings.json -->
</Project>
```

### Connection String Mode Properties Reference

#### Input Properties

| Property | Default | Description |
|----------|---------|-------------|
| `EfcptConnectionString` | *(empty)* | Explicit connection string override. **Takes highest priority.** |
| `EfcptAppSettings` | *(empty)* | Path to `appsettings.json` file containing connection strings. |
| `EfcptAppConfig` | *(empty)* | Path to `app.config` or `web.config` file containing connection strings. |
| `EfcptConnectionStringName` | `DefaultConnection` | Name of the connection string key to use from configuration files. |
| `EfcptProvider` | `mssql` | Database provider (currently only `mssql` is supported). |

#### Output Properties

| Property | Description |
|----------|-------------|
| `ResolvedConnectionString` | The resolved connection string that will be used. |
| `UseConnectionString` | `true` when using connection string mode, `false` for DACPAC mode. |

### Database Provider Support

**Currently Supported:**
- ✅ **SQL Server** (`mssql`) - Fully supported

**Planned for Future Versions:**
- ⏳ PostgreSQL (`postgresql`)
- ⏳ MySQL (`mysql`)
- ⏳ MariaDB (`mariadb`)
- ⏳ Oracle (`oracle`)
- ⏳ SQLite (`sqlite`)

### Security Best Practices

**❌ DON'T** commit connection strings with passwords to source control:

```xml
<!-- BAD: Password in plain text -->
<EfcptConnectionString>Server=prod;Database=MyDb;User=sa;Password=Secret123;</EfcptConnectionString>
```

**✅ DO** use environment variables or user secrets:

```xml
<!-- GOOD: Reference environment variable -->
<EfcptConnectionString>$(ProductionDbConnectionString)</EfcptConnectionString>
```

**✅ DO** use Windows/Integrated Authentication when possible:

```xml
<EfcptConnectionString>Server=localhost;Database=MyDb;Integrated Security=True;</EfcptConnectionString>
```

**✅ DO** use different connection strings for different environments:

```xml
<PropertyGroup Condition="'$(Configuration)' == 'Development'">
<EfcptConnectionString>Server=localhost;Database=MyDb_Dev;Integrated Security=True;</EfcptConnectionString>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Production'">
<EfcptConnectionString>$(PRODUCTION_DB_CONNECTION_STRING)</EfcptConnectionString>
</PropertyGroup>
```

### How Schema Fingerprinting Works

In connection string mode, instead of hashing the DACPAC file, `JD.Efcpt.Build`:

1. **Queries the database** system tables (`sys.tables`, `sys.columns`, `sys.indexes`, etc.)
2. **Builds a canonical schema model** with all tables, columns, indexes, foreign keys, and constraints
3. **Computes an XxHash64 fingerprint** of the schema structure
4. **Caches the fingerprint** to skip regeneration when the schema hasn't changed

This means your builds are still **incremental** - models are only regenerated when the database schema actually changes!

### Example: ASP.NET Core with Connection String Mode

```xml
<!-- MyApp.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JD.Efcpt.Build" Version="2.x.x" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.x" />
</ItemGroup>

<!-- Connection string mode: Use appsettings.json -->
<PropertyGroup>
<EfcptAppSettings>appsettings.json</EfcptAppSettings>
<EfcptConnectionStringName>DefaultConnection</EfcptConnectionStringName>
</PropertyGroup>
</Project>
```

```json
// appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyApp;Integrated Security=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
```

Build your project:

```bash
dotnet build
```

Generated models appear in `obj/efcpt/Generated/` automatically!

---

## 🐛 Troubleshooting

### Generated Files Don't Appear
Expand Down
10 changes: 10 additions & 0 deletions src/JD.Efcpt.Build.Tasks/BuildLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,15 @@ public void Detail(string message)

public void Warn(string message) => log.LogWarning(message);

public void Warn(string code, string message)
=> log.LogWarning(subcategory: null, code, helpKeyword: null,
file: null, lineNumber: 0, columnNumber: 0,
endLineNumber: 0, endColumnNumber: 0, message);

public void Error(string message) => log.LogError(message);

public void Error(string code, string message)
=> log.LogError(subcategory: null, code, helpKeyword: null,
file: null, lineNumber: 0, columnNumber: 0,
endLineNumber: 0, endColumnNumber: 0, message);
}
Loading