SQL Data Access Workflow
This guide shows a clean path for building a reusable relational data-service layer with VersaTul.
The goal is not just to execute SQL once. The goal is to create a data access shape you can keep reusing across services and repositories.
The main package combination is:
VersaTul.Data.Sqlfor provider-agnostic command execution.VersaTul.ConfigurationsandVersaTul.Configuration.Defaultsfor explicit connection and timeout settings.VersaTul.Data.MsSqlonly when SQL Server-specific behavior becomes necessary.
When To Use This Workflow
Use this workflow when you need to:
Keep relational data access logic out of controllers and handlers.
Standardize how commands, parameters, and mapping are handled.
Stay provider-agnostic unless a SQL Server-specific need is proven.
Packages To Install
dotnet add package VersaTul.Data.Sql
dotnet add package VersaTul.Configurations
dotnet add package VersaTul.Configuration.Defaults
Step 1: Configure The Connection
Register the provider and build the configuration source.
using System.Data.Common;
using VersaTul.Configuration.Defaults.Sql;
DbProviderFactories.RegisterFactory("Oracle.ManagedDataAccess.Client.OracleClientFactory", OracleClientFactory.Instance);
var configSettings = new Builder().AddOrReplace(new[]
{
new KeyValuePair<string, object>(
"OracleSqlDb",
new ConnectionInfo(
"User Id=SYS;Password=Secret;Data Source=database-address.local.com/ORCLCDB;DBA Privilege=SYSDBA;",
"Oracle.ManagedDataAccess.Client.OracleClientFactory")),
new KeyValuePair<string, object>("SqlDbConnectionName", "OracleSqlDb")
}).BuildConfig();
Step 2: Build The Data Source
using VersaTul.Data.Sql;
using VersaTul.Data.Sql.Configurations;
var dataConfiguration = new DataConfiguration(configSettings);
var providerFactory = new ProviderFactory();
var commandFactory = new CommandFactory(dataConfiguration, providerFactory);
var dataSource = new SqlDbDataSource(commandFactory);
This gives you a reusable execution surface for reads, writes, async operations, and scalar values.
Step 3: Put Mapping In A Service Layer
Implement a project-specific data service on top of BaseDataService.
using System.Data;
using VersaTul.Data.Sql.Contracts;
public class ProductDataService : BaseDataService
{
public ProductDataService(IDataSource dataSource, INullFiltering filtering, IUtility utility)
: base(dataSource, filtering, utility)
{
}
public Product? Get(int productId)
{
Product? product = null;
var commandText = @"select product_id as Id,
product_name as Name,
description as Description
from products
where product_id = :productId";
var parameters = new ParameterCollection();
parameters.Add(new Parameter("productId", productId, DbType.Int32, 0, ParameterDirection.Input));
ProcessReader(ExecuteReader(new DataCommand(commandText, DataCommandType.Query), parameters), _ =>
{
product = new Product
{
Id = Get((Product model) => model.Id),
Name = Get((Product model) => model.Name),
Description = Get((Product model) => model.Description)
};
});
return product;
}
}
When To Switch To MsSql
Stay on VersaTul.Data.Sql if provider-agnostic access is still a requirement.
Switch to Data MsSql when you need:
SQL Server-specific parameter handling,
table-valued parameters, or
SQL Server bulk copy.
What You Should See
When this workflow is working:
Connection and provider settings stay explicit and centralized.
SQL execution stays behind a reusable service abstraction.
Mapping logic stays consistent across queries instead of being rewritten in each caller.
Common Mistakes
Starting with
VersaTul.Data.MsSqlbefore SQL Server-specific requirements are clear.Keeping SQL statements in controllers, handlers, or unrelated service layers.
Treating connection configuration as an incidental detail instead of part of the data-access contract.
What To Read Next
Read Data Sql for the full relational package surface.
Read Data MsSql if SQL Server-specific behavior is the next requirement.
Read File Import Workflow if the next step is ingesting flat-file data into a relational store.