Table of Contents

Orpheus and DI

One of the first things that need to happen in an OrpheusORM application is registering Orpheus's services — and, for whichever database engine you're targeting, wiring up a connection factory and DDL helper for it.

Why use DI?

The reason for using an IoC container is configurability and extensibility. Orpheus doesn't depend on, or include, any code that targets a specific database engine — the engine is selected entirely by which services you register.

Read about Microsoft's DI here

The easy way: per-engine registration

Each database engine ships its own DDL helper package (OrpheusSQLServerDDLHelper, OrpheusMySQLDDLHelper, OrpheusPostgreSQLDDLHelper), and each one provides a single IServiceCollection extension method — mirroring the UseSqlServer()/UseNpgsql() pattern from EF Core — that registers everything Orpheus needs for that engine in one call: a pooled connection factory, the engine's DDL helper, and IOrpheusDatabase itself, already configured.

var services = new ServiceCollection();

services.AddOrpheusSqlServer(config =>
{
    config.Server = "YourServer";
    config.DatabaseName = "YourDatabase";
    config.UseIntegratedSecurity = true;
});

var provider = services.BuildServiceProvider();
var db = provider.GetRequiredService<IOrpheusDatabase>(); // already fully configured
db.Connect();

The MySQL and PostgreSQL equivalents are AddOrpheusMySql(...) (in OrpheusMySQLDDLHelper) and AddOrpheusPostgreSql(...) (in OrpheusPostgreSQLDDLHelper) — same shape, same configuration options. You can also pass an already-built IDatabaseConnectionConfiguration instead of a configuration lambda, which is convenient when the configuration is coming from a file (see below).

Internally, each of these calls AddOrpheusServices() to register Orpheus's own internal services (table options, module/schema types, and a console logging fallback if nothing else has registered ILoggerFactory) — you don't need to call AddOrpheusServices() yourself unless you're wiring up a database engine manually (see "Registering things yourself" below).

Configuration by file

The configuration lambda above can just as easily be filled from an IConfiguration section instead of hardcoded values. A typical OrpheusConfig.json looks like this:

{
  "OrpheusConfiguration": {
    "DatabaseConnections": [
      {
        "ConfigurationName": "Database1",
        "Server": "YourServer",
        "DatabaseName": "YourDatabase",
        "UseIntegratedSecurity": false,
        "UseIntegratedSecurityForServiceConnection": false,
        "UserName": "[yourusername]",
        "Password": "[yourpassword]",
        "ServiceUserName": "[yourusername]",
        "ServicePassword": "[yourpassword]",
        "Pooling": true,
        "MinPoolSize": 0,
        "MaxPoolSize": 100,
        "ConnectionIdleTimeout": 300
      }
    ]
  }
}
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.SetBasePath("YourPathHere");
configurationBuilder.AddJsonFile("OrpheusConfig.json", optional: false, reloadOnChange: true);
var configuration = configurationBuilder.Build();

var services = new ServiceCollection();
services.AddOrpheusSqlServer(configuration, "Database1");

AddOrpheusSqlServer(configuration, connectionName) binds the "OrpheusConfiguration" section and looks up the connection whose ConfigurationName matches connectionName for you. If your settings live under a different section, pass it as a third argument: services.AddOrpheusSqlServer(configuration, "Database1", "MyApp:Orpheus").

For details on every connection option — including the pooling settings (Pooling, MinPoolSize, MaxPoolSize, ConnectionIdleTimeout) that get applied to the underlying ADO.NET connection pool — see Connecting to a database and Database Connection Configuration.

Without a DI container

If your application isn't using an IServiceCollection at all (a console app or script, for example), each engine's DDL helper package also ships a static factory that builds a fully working, pooling-aware IOrpheusDatabase on its own:

var db = OrpheusSQLServerDatabase.CreateDatabase(config => {
    config.Server = "YourServer";
    config.DatabaseName = "YourDatabase";
    config.UseIntegratedSecurity = true;
});
db.Connect();

OrpheusMySQLServerDatabase.CreateDatabase(...) and OrpheusPostgreSQLServerDatabase.CreateDatabase(...) work the same way. Internally these build their own small, private service container, so the result is identical to what you'd get by registering with AddOrpheusSqlServer(...) and resolving — it's just self-contained.

Registering things yourself

The per-engine extension methods cover the common case, but they're a thin convenience layer — nothing stops you from registering IOrpheusConnectionFactory, IOrpheusDDLHelper, and IOrpheusDatabase directly, for example if you need a custom IOrpheusDDLHelper for a database engine Orpheus doesn't ship support for (see Orpheus DDL Helper):

services.AddOrpheusServices();
services.AddTransient<IOrpheusConnectionFactory, MyCustomConnectionFactory>();
services.AddTransient<IOrpheusDDLHelper, MyCustomDDLHelper>();
services.AddTransient<IOrpheusDatabase, OrpheusDatabase>();

Note: The static ServiceManager.ServiceProvider property and ServiceManager.Resolve<T>() helpers are retained for backward compatibility but are obsolete — prefer resolving IOrpheusDatabase from your IServiceProvider (constructor injection, or provider.GetRequiredService<IOrpheusDatabase>()) instead.