Dependency Injection

Runs on your machine

Lets the framework construct services and pass their dependencies.

Lets the framework construct the book service and pass it what it depends on - a repository and the clock - so the service does not need to know how they are made, and tests can pass fakes instead. The services are registered with Microsoft’s dependency injection, and AddDependencyInjection makes them available to the server.

AddDependentService creates BookService with the repository passed to its constructor, and Add receives the TimeProvider as a parameter of the method.

Runs on your machine only. Dependency injection is enabled for the whole server, which this website shares with others.

To run it, create a console app (dotnet new console), add GenHTTP (dotnet add package GenHTTP.Full), replace Program.cs with the code below and start it with dotnet run - it answers at http://localhost:8080/.

Program.cs72 lines
using GenHTTP.Engine.Internal;

using GenHTTP.Modules.DependencyInjection;
using GenHTTP.Modules.Layouting;
using GenHTTP.Modules.Practices;
using GenHTTP.Modules.Reflection;
using GenHTTP.Modules.Webservices;

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

services.AddSingleton<IBookRepository, InMemoryBookRepository>()
        .AddSingleton(TimeProvider.System);

var app = Layout.Create()
                .AddDependentService<BookService>("books"); // GET /books/

await Host.Create()
          .Handler(app)
          .AddDependencyInjection(services.BuildServiceProvider())
          .Defaults()
          .RunAsync();

public record Book(int Id, string Title, DateTimeOffset? Added = null);

public interface IBookRepository
{

    List<Book> GetAll();

    Book Add(Book book);

}

public class InMemoryBookRepository : IBookRepository
{
    private readonly List<Book> _books = [new(1, "Lord of the Rings")];

    private readonly Lock _sync = new();

    public List<Book> GetAll()
    {
        lock (_sync) return [.._books];
    }

    public Book Add(Book book)
    {
        lock (_sync)
        {
            var added = book with { Id = _books.Select(b => b.Id).DefaultIfEmpty().Max() + 1 };

            _books.Add(added);

            return added;
        }
    }

}

// dependencies are passed to the constructor ...
public class BookService(IBookRepository repository)
{

    [ResourceMethod]
    public List<Book> List() => repository.GetAll();

    // ... or to a method
    [ResourceMethod(Method.Put)]
    public Book Add(Book book, TimeProvider time) => repository.Add(book with { Added = time.GetUtcNow() });

}

Try it

With the sample running on your machine:

  1. List all books

    curl -i http://localhost:8080/books/
  2. Add a book

    curl -i -X PUT -H "Content-Type: application/json" -d '{ "title": "Dune" }' http://localhost:8080/books/