Search code examples
dependency-injectiongraphqlgraphql-dotnet

How to DI repository into Type-class?


Need some help here please ...

I'm looking at the example "graphql-dotnet/server" where the exposed objects contains just plain properties. But what if I need to resolve a property and get data from a repository, how can I get hold of the respository class in the Type-class?

Example: The sample has a ChatQuery exposing "messages".

public ChatQuery(IChat chat)
    {
        Field<ListGraphType<MessageType>>("messages", resolve: context => chat.AllMessages.Take(100));
    }

Instance "chat" is the repository here and is serving data (messages) via chat.AllMessages.

Let's say that a message has a list of viewers. Then I need to resolve that list from a repository. This is done in the other example "graphql-dotnet/examples" where the "StarWars/Types/StarWarsCharacter.cs" has a list of friends and the "StarWars/Types/HumanType" has the repository (StarWarsData) inserted in the constructor and can be used in the resolve method for "friends":

public class HumanType : ObjectGraphType<Human>
{
    public HumanType(StarWarsData data)
    {
        Name = "Human";

        Field(h => h.Id).Description("The id of the human.");
        Field(h => h.Name, nullable: true).Description("The name of the human.");

        Field<ListGraphType<CharacterInterface>>(
            "friends",
            resolve: context => data.GetFriends(context.Source)
        );
        Field<ListGraphType<EpisodeEnum>>("appearsIn", "Which movie they appear in.");

        Field(h => h.HomePlanet, nullable: true).Description("The home planet of the human.");

        Interface<CharacterInterface>();
    }
}

BUT, doing the same thing in the server sample won't work.

public class MessageType : ObjectGraphType<Message>
{
    public MessageType(IChat chat)
    {
        Field(o => o.Content);
        Field(o => o.SentAt);
        Field(o => o.From, false, typeof(MessageFromType)).Resolve(ResolveFrom);
        Field<ListGraphType<Viewer>>(
            "viewers",
            resolve: context => chat.GetViewers(context.Source)
        );
    }

    private MessageFrom ResolveFrom(ResolveFieldContext<Message> context)
    {
        var message = context.Source;
        return message.From;
    }
}

When I add the chat repository to the constructor in MessageType it fails.

I'm obviously missing something here; why isn't Dependency Injection injecting the chat instance into the MessageType class in the "graphql-dotnet/server" project? But it works in the "graphql-dotnet/examples" project.

Best, Magnus


Solution

  • To use DI you need to pass a dependency resolver in the constructor of your Schema class. The default resolver uses Activator.CreateInstance, so you have to teach it about the Container you are using.

    services.AddSingleton<IDependencyResolver>(
      s => new FuncDependencyResolver(s.GetRequiredService));
    

    IDependecyResolver is an interface in the graphql-dotnet project.

    public class StarWarsSchema : Schema
    {
        public StarWarsSchema(IDependencyResolver resolver)
            : base(resolver)
        {
            Query = resolver.Resolve<StarWarsQuery>();
            Mutation = resolver.Resolve<StarWarsMutation>();
        }
    }
    

    https://github.com/graphql-dotnet/examples/blob/bcf46c5c502f8ce75022c50b9b23792e5146f6d2/src/AspNetCore/Example/Startup.cs#L20

    https://github.com/graphql-dotnet/examples/blob/bcf46c5c502f8ce75022c50b9b23792e5146f6d2/src/StarWars/StarWarsSchema.cs#L6-L14