Docs Hub
Documentation/Languages/C#/Advanced

Dependency Injection | Part 2

Start developing with C#

Let’s continue and break it down a little more…

This is a full application using dependency injection (DI), and it’s written entirely in one file: Program.cs.


File Structure

Here's a simplified view of what the file structure looks like:

ascii table

Program.cs

At the top of the file, we’re importing a NuGet package called SimpleInjector:

using SimpleInjector;

SimpleInjector helps us set up and manage dependencies easily. It gives us a container where we register everything we want the app to know how to "inject" later on. There are other DI frameworks, for example Microsoft.Extensions.DependencyInjection is very common, but we’ll stick with SimpleInjector for now because it’s simple and easy to use.


What’s happening in Main?

We’ll explain each part soon, but first, take a look at the full program:

namespace DependencyInjectionDemo;

using SimpleInjector

class Program
{
    static void Main()
    {
        var container = new Container();

        container.Register<IConnection, AppConnectionSettings>();
        container.Register<IDbContext, DbContext>();
        container.Register<ModelService>();
        container.Verify();

        var modelService = container.GetInstance<ModelService>();
        var models = modelService.GetSomeModels();

        foreach (var model in models)
        {
            Console.WriteLine(model.Name);
        }
    }
}

Here’s a breakdown:

We create a container.
We register a few types: some interfaces with their matching implementations.
We ask the container to give us a ModelService instance.
We call a method on that service and print the names of the models returned.

What’s a Model?

A model usually represents a table in a database. For example, customer_table, employee_table, or product_table.

public class Model
{
    public int Id { get; set; }
    public string? Name { get; set; }
}

The Id field is very common in database models and often becomes the primary key automatically when using frameworks like Entity Framework (ORM).
You can also use attributes like [Key] to mark it explicitly:

[Key]
public int Id { get; set; }

Let's continue and create a Service that will use this model.

ModelService — this is where DI happens
This service depends on a database context (IDbContext) — but we don’t create the context ourselves. Instead, the container injects it for us.

Let's also create a second class for returning a specific name of a model:

public class GetModelByName
{
    public string Name { get; set; }
}
public class ModelService
{
    private readonly IDbContext _modelcontext;

    public ModelService(IDbContext modelcontext)
    {
        _modelcontext = modelcontext;
    }

    public IEnumerable<GetModelByName> GetSomeModels()
    {
        var totalModels = new Random().Next(5, 10);

        var allModels = _modelcontext
            .CreateModels()
            .Where(m => m.Name != null)
            .Take(totalModels)
            .Select(c => new GetModelByName
            {
                Name = c.Name
            })
            .ToList();

        return allModels;
    }
}

Implementing the interfaces This is the part where Dependency Inversion Principle (DIP) and Dependency Injection (DI) really show up.

Let’s break it down.

DbContext – implements IDbContext

public class DbContext : IDbContext
{
    private readonly string _connection;

    public DbContext(IConnection connection)
    {
        _connection = connection.ConnectionString;
    }

    public List<Model> CreateModels() => new List<Model>
    {
        new Model { Id = 1, Name = "Bill Gates" }
    };
}

This class depends on an IConnection, which we’ll create next. It also creates a dummy list of models.

Configuration with AppConnectionSettings This is where we fake a database connection string:

public class AppConnectionSettings : IConnection
{
    public string ConnectionString { get; } = "server=db.cloud.com;port=1234;username=admin;password=suP3rPassw0rd";
}

In a real app, this would come from something like appsettings.json or environment variables.

Interfaces (Abstractions)

public interface IDbContext
{
    List<Model> CreateModels();
}

public interface IConnection
{
    string ConnectionString { get; }
}

These interfaces describe what the implementations must provide, but don’t include any logic themselves. This makes the app flexible — we can swap out one implementation for another without changing the rest of the code.

Final Thoughts This small example shows how to:

Use SimpleInjector to manage dependencies

Register interfaces and classes in a container

Separate logic across different files

Use dependency injection to make the code easier to test and maintain

We'll build on this by using appsettings.json and a more complete file structure in future parts.

.... More to come...

On this page