csharp-language-server-protocol icon indicating copy to clipboard operation
csharp-language-server-protocol copied to clipboard

Problem with dependency injecting records

Open mikkleini opened this issue 4 years ago • 1 comments

I can't use records to do dependency injection into handlers. A recurssive dependency exception is thrown. I have reduced it down this basic code:

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Server;

namespace MyLSP
{
    public static class Program
    {
        public static void Main()
        {
            MainAsync().Wait();
        }

        private static async Task MainAsync()
        {
            var server = await LanguageServer
                .From(options =>
                    options
                        .WithInput(Console.OpenStandardInput())
                        .WithOutput(Console.OpenStandardOutput())
                        .WithServices(s => s.AddSingleton<SomethingUseful>())
                        .WithHandler<TextDocumentLinkHandler>())
                    .ConfigureAwait(false);

            await server.WaitForExit
                .ConfigureAwait(false);
        }
    }

    public record SomethingUseful
    {

    }

    public record TextDocumentLinkHandler : IDocumentLinkHandler
    {
        public TextDocumentLinkHandler(SomethingUseful stuff)
        {
        }

        public DocumentLinkRegistrationOptions GetRegistrationOptions(DocumentLinkCapability capability, ClientCapabilities clientCapabilities)
        {
            throw new NotImplementedException();
        }

        public Task<DocumentLinkContainer> Handle(DocumentLinkParams request, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }
}

Following exception is thrown in main function:

ContainerException: code: Error.RecursiveDependencyDetected;
message: Recursive dependency is detected when resolving 
MyLSP.SomethingUseful {DryIoc.IfUnresolved.ReturnDefault} as parameter "original" (IsSingletonOrDependencyOfSingleton) <--recursive
  in Singleton MyLSP.SomethingUseful {DryIoc.IfUnresolved.ReturnDefault} as parameter "stuff" FactoryId=39 (IsSingletonOrDependencyOfSingleton) <--recursive
  in resolution root MyLSP.TextDocumentLinkHandler {DryIoc.IfUnresolved.ReturnDefault} FactoryId=153
  from container without scope
 with Rules with {TrackingDisposableTransients, ResolveIEnumerableAsLazyEnumerable, UseDynamicRegistrationsAsFallbackOnly, SelectLastRegisteredFactory} and without {ThrowOnRegisteringDisposableTransient, VariantGenericTypesInResolvedCollection}
 with DefaultReuse=Scoped {Lifespan=100}
 with FactorySelector=SelectLastRegisteredFactory
 with Made={FactoryMethod=ConstructorWithResolvableArgumentsIncludingNonPublic}.

Change SomethingUseful from record to class and problem is gone. I tried something similar with generic host model but couldn't get this fault. So.... maybe someting in LSP ? Same issue from LSP version 0.19.0 to 0.19.5. Didn't try older ones, they have compatibility breaks. Same issue on .NET 5.0 and .NET 6.0 preview 7.

mikkleini avatar Aug 28 '21 21:08 mikkleini

Found the cuplrit with JustDecompile. Compiler create constructor of class itself with "original" argument:

protected SomethingUseful(SomethingUseful original)
{
}

Maybe Microsoft DI extensions or DryLoc need a fix?

mikkleini avatar Aug 29 '21 09:08 mikkleini