SmartData.Server.SqlServer
SQL Server database provider for SmartData.Server.
SQL Server database provider for SmartData.Server. Implements four provider interfaces using Microsoft.Data.SqlClient and T-SQL. Built on .NET 10.
Dependencies
- SmartData.Server — Provider interfaces and core engine
- Microsoft.Data.SqlClient — SQL Server ADO.NET provider
Registration
builder.Services.AddSmartDataSqlServer(o =>
{
o.ConnectionString = @"Server=(localdb)\MSSQLLocalDB;Integrated Security=true;TrustServerCertificate=True";
});
Registers SqlServerDatabaseProvider as a singleton for IDatabaseProvider. The three sub-providers — SqlServerSchemaProvider, SqlServerSchemaOperations, SqlServerRawDataProvider — are exposed as properties (Schema, SchemaOperations, RawData) on the root and are not separately registered in DI.
Configuration
public class SqlServerDatabaseOptions
{
public string ConnectionString { get; set; } // Base connection string (no Initial Catalog)
public string DataDirectory { get; set; } // default: {AppBase}/data — for backups/exports
}
The ConnectionString should contain Server + authentication only. The provider appends Initial Catalog per database automatically.
Project Structure
SmartData.Server.SqlServer/
├── SqlServerDatabaseProvider.cs IDatabaseProvider — SQL Server instance management, master DB operations
├── SqlServerSchemaProvider.cs ISchemaProvider — INFORMATION_SCHEMA + sys.* catalog views
├── SqlServerSchemaOperations.cs ISchemaOperations — T-SQL DDL, IDENTITY, SQL Server type mapping
├── SqlServerRawDataProvider.cs IRawDataProvider — SqlConnection for dynamic CRUD + raw SQL + OpenReader
├── SqlServerDatabaseOptions.cs Configuration options
├── ServiceCollectionExtensions.cs AddSmartDataSqlServer() extension method
└── SmartData.Server.SqlServer.csproj
Implementation Details
SqlServerDatabaseProvider
- Each "database" maps to a SQL Server database on the configured instance
- Connection strings built by appending
Initial Catalog={dbName}to the base connection string EnsureDatabase()— creates database viaCREATE DATABASEif it doesn't existDropDatabase()— setsSINGLE_USER WITH ROLLBACK IMMEDIATEbefore dropping to close active connectionsListDatabases()— queriessys.databases WHERE database_id > 4(excludes system databases)GetDatabaseInfo()— queriessys.database_filesfor size,sys.databasesfor creation dateDataDirectoryproperty exposes the working directory for backups/exports (used byBackupService)
SqlServerSchemaProvider
GetTables()— queriesINFORMATION_SCHEMA.TABLESfor table names + column counts,COUNT(*)for row countsGetColumns()—INFORMATION_SCHEMA.COLUMNSjoined withKEY_COLUMN_USAGEfor PK detection;COLUMNPROPERTY(IsIdentity)for identity columnsGetIndexes()— queriessys.indexes+sys.index_columns+sys.columnswithSTRING_AGGfor column lists; excludes primary key indexesTableExists()—INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ... AND TABLE_TYPE = 'BASE TABLE'GetTableSchema()— batched single-connection override: runs TableExists + GetColumns + GetIndexes on one connection for SchemaManager validation
SqlServerSchemaOperations
- Type mapping: string→NVARCHAR(MAX), int→INT, long→BIGINT, decimal→DECIMAL(18,4), double→FLOAT, bool→BIT, datetime→DATETIME2, guid→UNIQUEIDENTIFIER, byte[]→VARBINARY(MAX)
MapTypeReverse()— reverse mapping for backup system portability. Handles parameterized types (e.g.,NVARCHAR(255)→ string) via contains-based fallbackCreateTable()— withIDENTITY(1,1)for identity columns, named PK constraints (PK_{tableName})AlterColumn()— nativeALTER TABLE ALTER COLUMN(no copy strategy needed). Fills NULL values with defaults before changing nullable→non-nullableAddColumn()— uses temporary named default constraint for NOT NULL columns, then drops the constraintRenameColumn()/RenameTable()— usessp_renameDropColumn()— drops any default constraints before dropping the columnDropIndex()— looks up the owning table fromsys.indexes(SQL Server requiresDROP INDEX ON table)
Full-Text Search
CreateFullTextIndex()— Creates a shared fulltext catalog (SmartDataFTCatalog) withIF NOT EXISTSguard, then creates aFULLTEXT INDEXon the table usingKEY INDEX [PK_{table}]DropFullTextIndex()—DROP FULLTEXT INDEX ON [{table}]FullTextIndexExists()— Queriessys.fulltext_indexesbyobject_idBuildFullTextSearchSql()— Returns:SELECT TOP({limit}) t.* FROM [{table}] t WHERE CONTAINS(({columns}), @searchTerm)- Population is async (standard SQL Server behavior, resolves within seconds for small tables)
SqlServerRawDataProvider
- Uses
SqlConnectiondirectly for dynamic queries (no linq2db entity mapping) Select()— parameterized WHERE withOFFSET/FETCH NEXTpagination (SQL Server requiresORDER BYfor offset)Insert()— returnsSCOPE_IDENTITY()Import()— transactional with three modes:"insert"— standard INSERT (fails on conflict)"skip"— usesTRY/CATCHto silently skip constraint violations (error 2627/2601)"replace"— usesMERGEstatement with primary key matching for upsert behavior
OpenReader()— returns a streamingIDataReaderover all rows, usingCommandBehavior.CloseConnectionExecuteRawSql()— detects SELECT/WITH as queries vs DML. ReturnsQueryResultwith columns, rows, or affected countNotEqualoperator uses<>(ANSI standard) instead of!=