Skip to content

Without Dependency Injection

The underlying libraries can be used without a host, service provider, or hosting integration. Construct the objects directly and supply their dependencies. Install TcfOss.DatabaseManager.Core for the parsing and formatting examples, or TcfOss.DatabaseManager.MySql for the MySQL definition example.

Parse SQL

For generic parsing, the complete object graph is just a lexer, parser, and TextParser:

using TcfOss.DatabaseManager.Core.Lexing;
using TcfOss.DatabaseManager.Core.Parsing;

var lexer = new GenericLexer();
var parser = new Parser();
var textParser = new TextParser(lexer, parser);
var statements = textParser.ParseText(
    "CREATE TABLE widgets (id INT);",
    filename: "widgets.sql");

Console.WriteLine($"Parsed {statements.Count} statement(s).");

No configuration or database connection is needed. Choose a matching lexer/parser pair for dialect-specific syntax:

Syntax Lexer Parser Namespaces
Generic GenericLexer Parser TcfOss.DatabaseManager.Core.Lexing and .Parsing
MySQL/MariaDB MyLexer MyParser TcfOss.DatabaseManager.MySql.Lexing and .Parsing
SQL Server MsLexer MsParser TcfOss.DatabaseManager.MsSql.Lexing and .Parsing

For example, MySQL and MariaDB syntax both use new TextParser(new MyLexer(), new MyParser()). The other provider workflows still have distinct configuration and metadata behavior.

Format SQL in Memory

Formatting also needs configuration and a built-in function-name provider. GenericConfigurationBuilder prepares configuration. This complete generic example uses defaults and does not contact a server:

using Microsoft.Extensions.Logging.Abstractions;
using TcfOss.DatabaseManager.Core.App;
using TcfOss.DatabaseManager.Core.BuiltIn;
using TcfOss.DatabaseManager.Core.Configuration;
using TcfOss.DatabaseManager.Core.Formatting;
using TcfOss.DatabaseManager.Core.Lexing;
using TcfOss.DatabaseManager.Core.Parsing;

var workingDirectory = Directory.GetCurrentDirectory();
var configurationBuilder = new GenericConfigurationBuilder();
var rawConfig = configurationBuilder.GetDefaultConfig(workingDirectory);
var config = configurationBuilder.BuildConfiguration(
    workingDirectory, rawConfig, new EnvironmentVariableReader(),
    relaxed: false, logger: NullLogger.Instance);

var textParser = new TextParser(new GenericLexer(), new Parser());
var functionNames = new FunctionNameProvider();
using var formatter = new Formatter(config, textParser, functionNames);
var formatted = formatter.GetFormatted("create table widgets(id int);");

Console.WriteLine(formatted);

GetFormatted returns the formatted SQL and clears the formatter's output buffer. This example reads and writes no SQL files. Formatter owns mutable formatting state; unlike the lexer/parser pair, use a separate formatter for each concurrent operation. The null logger suppresses diagnostic output; an application can supply its own logger instead.

Configuration Files

ConfigurationFile, in TcfOss.DatabaseManager.Core.Configuration, handles discovery and raw YAML loading. To find a configuration from a working directory, call ConfigurationFile.GetConfigPath(workingDirectory, configPath: null). Discovery searches that directory and its parents using the same filenames as the CLI. An explicit configPath is resolved relative to the working directory instead.

Pass the returned FileInfo to ConfigurationFile.LoadRawConfig. It returns null for a missing file; the caller chooses whether to throw or use GetDefaultConfig. Pass the raw configuration to the matching builder:

Base package Configuration builder Processed configuration
Core GenericConfigurationBuilder ConfigGeneric
MySql MyConfigurationBuilder MyConfig
MariaDb MaConfigurationBuilder MyConfig
MsSql MsConfigurationBuilder MsConfig

Each builder lives in its base package's .Configuration namespace. Builders prepare defaults, substitute credential environment variables where applicable, and invoke the configuration loaders. MySQL/MariaDB preparation can contact a server to discover defaults; without a connection it retains the existing configured/default fallback behavior.

Load and Compare MySQL Definitions

Filesystem definition loading needs a larger graph. This example uses an existing MySQL project configuration and SQL definition files. Configuration processing can contact the configured server to discover defaults, just as it does during CLI startup. Only TcfOss.DatabaseManager.MySql is needed; it includes configuration preparation and brings Core transitively.

Note

The comparison uses an empty starting definition, producing a create script from the definition files. It does not load or compare the live server's objects.

using Microsoft.Extensions.Logging.Abstractions;
using TcfOss.DatabaseManager.Core.App;
using TcfOss.DatabaseManager.Core.Configuration;
using TcfOss.DatabaseManager.Core.DefinitionBuilding;
using TcfOss.DatabaseManager.Core.IO;
using TcfOss.DatabaseManager.Core.Parsing;
using TcfOss.DatabaseManager.MySql.BuiltIn;
using TcfOss.DatabaseManager.MySql.Configuration;
using TcfOss.DatabaseManager.MySql.DatabaseObjects;
using TcfOss.DatabaseManager.MySql.DefinitionBuilding;
using TcfOss.DatabaseManager.MySql.DefinitionMapping;
using TcfOss.DatabaseManager.MySql.Lexing;
using TcfOss.DatabaseManager.MySql.Parsing;

var configFile = new FileInfo("/path/to/project/database-manager.yaml");
var rawConfig = ConfigurationFile.LoadRawConfig(configFile)
    ?? throw new FileNotFoundException("Configuration file not found.", configFile.FullName);
var config = new MyConfigurationBuilder().BuildConfiguration(
    configFile.DirectoryName!, rawConfig, new EnvironmentVariableReader(),
    relaxed: false, logger: NullLogger.Instance);

var textParser = new TextParser(new MyLexer(), new MyParser());
var sources = new SourceManager();
var functionNames = new MyFunctionNameProvider();
var fileLoader = new DefinitionFileLoader<MySchemaMapping>(
    config,
    NullLogger<DefinitionFileLoader<MySchemaMapping>>.Instance);
var definitionLoader = new MyFsDefinitionLoader(
    config,
    fileLoader,
    textParser,
    sources,
    functionNames,
    NullLogger<MyFsDefinitionLoader>.Instance,
    NullLoggerFactory.Instance);

var desiredDefinition = definitionLoader.LoadDefinition(relaxed: false);
var startingDefinition = new MyDefinition();
var differ = new MyDiffer(
    config, startingDefinition, desiredDefinition,
    refactors: [], deployScripts: [], logger: NullLogger.Instance);
var changes = differ.ComputeChanges();

using var output = new StringWriter();
var writer = new DifferStatementWriter(config.DifferFormatting, output);
foreach (var change in changes)
{
    writer.WriteDefinitionAlter(change);
}
Console.WriteLine(output.ToString());

The same processed configuration is passed to both the file loader and definition loader. SourceManager tracks source locations during definition building, while the function-name provider supplies MySQL built-ins. MyFsDefinitionLoader also needs a logger factory for the definition builders it constructs internally.

MyDiffer compares the two supplied MyDefinition objects. To compare against a non-empty definition, replace startingDefinition with the definition you loaded or constructed. This example passes empty refactor and deploy-script collections; loading those for a live-server migration is separate work. Creating the differ alone does not determine which scripts or refactors have already run.

Where DI Saves Wiring

The biggest advantage to a hosted approach is that it spares you from having to manually construct and supply dependencies to every object you need and partially insulates you from constructor signature changes.