How to Build an AI-Powered SQL Server Query Assistant in ASP.NET Core 10 Using C# (2026)
A natural-language SQL assistant should never connect a model’s free-form output directly to a database executor. Even a read-only SELECT can leak sensitive data or consume too many resources. The safest first version limits the model to selecting from a small reporting catalog.
This guide builds an AI-powered SQL Server query assistant in ASP.NET Core 10. The model selects one intent. C# resolves that label to fixed parameterized SQL and runs it through a principal that can read one reporting view.
How should natural language become SQL?
This design separates interpretation from SQL execution. The prompt contains an approved view name and allowed operations. The model can suggest count_orders, count_open, or rejection. It cannot add a WHERE expression, specify a table name, execute a stored procedure, or change permissions. Application code accepts only exact labels and binds the region as a typed parameter.
| Approach | Capability | Main concern |
|---|---|---|
| Fixed query templates | Small approved reporting catalog | New reports require code changes |
| Generated SQL plus parser | More flexible analysis | AST validation, permissions, resource and disclosure controls |
| Human-reviewed query | Sensitive or exceptional report | Review must be bound to the exact query and parameters |
Schema discovery should be curated. Expose approved views and definitions to the model, not credentials, sample private data, hidden table names or arbitrary database metadata. Rebuild the catalog after schema migrations. This fixture writes the schema summary explicitly.
Create the view and API
Install .NET 10, SQL Server and a client that supports GO batches. Use a disposable database. Apply the script with a setup principal, then provision a separate application login mapped to a database user in the restricted role. Check that it does not inherit privileged roles or additional grants. Configure a TLS-validated connection string and store it outside source control.
Set OpenAI__ApiKey, a chat-compatible OpenAI__Model, and ConnectionStrings__Reporting. The sample bounds inputs, uses a provider timeout, and uses a separate five-second SQL command timeout. It returns only a count and approved template name. Audit logs record the template, not question text or credentials. The public API would also require verified caller identity, region authorization and request quotas. This demonstration must run on loopback.
Test a permitted count and a destructive request
If the model selects count_open, the fixture returns count 1. A generated string such as DROP TABLE dbo.OrdersLab does not match a catalog entry, so there is no database call. A model could still classify an unrelated question as an allowed report. Limit outputs to authorized aggregates, and verify that application ownership rules prevent a caller from choosing another user’s region.
For an adversarial test, use a fake provider that returns SQL text, a combined label, an oversized response, and an allowed label. Assert that only the exact known label reaches the database. Independently sign in as the SQL service user and verify that the view can be read and writes and underlying-table reads are denied under the deployed permission configuration.
The C# project compiled on .NET 10. A live model and SQL Server were not exercised.
Add approval and production limits
For sensitive reports, persist a proposal containing the caller, approved template, typed parameters and expiry. Require a distinct authorized approver. Execute only after checking the stored proposal version and reauthorizing the current actor. Never interpret a free-form “approved” model response as permission. Use a durable decision and receipt to handle retries.
Query cost matters even when the result is a single count. Add indexes appropriate for filters, inspect execution plans, and cap concurrency. Read-only access limits writes but does not make data safe to disclose. If future requirements truly need generated SQL, validate the parsed structure with a maintained parser and use a separate constrained service. Reject unknown statements, dynamic SQL and unsafe functions. Avoid validating SQL with keyword blacklists alone.
| Symptom | Check |
|---|---|
| Every prompt returns 400 | Model content must match an exact catalog label |
| View cannot be read | Correct role and schema-qualified grant |
| Query times out | Index, plan and concurrency |
| User can choose another region | Missing application-level data authorization |
| Logs contain prompts | Logging scopes and provider telemetry |
FAQ and references
Does this execute model-generated SQL? It executes only one of two fixed C# query templates. The model proposes an intent.
Can parameterization make arbitrary generated SQL safe? Parameters protect value positions. They do not authorize a query’s structure or access to data.
Should sensitive queries be human-approved? For high-impact reports, bind approval to the exact operation and verify access again at execution time.
Start with the smallest reporting catalog that answers real questions. Add templates through normal code and permission review.
Comments
Post a Comment