Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saga analyzer throws exception when handlers implemented in partial classes #6370

Closed
nrsmp opened this issue Apr 25, 2022 · 5 comments
Closed
Labels
Milestone

Comments

@nrsmp
Copy link

nrsmp commented Apr 25, 2022

When implementing a Saga in a partial class and using library versions 7.7.0 and 7.7.2, the roslyn analyzer throws an error System.ArgumentException: 'Syntax node is not within syntax tree'

By downgrading to version 7.6.2, the analyzer issue goes away.

Stack trace:

Severity	Code	Description	Project	File	Line	Suppression State
Error	AD0001	Analyzer 'NServiceBus.Core.Analyzer.SagaAnalyzer' threw an exception of type 'System.ArgumentException' with message 'Syntax node is not within syntax tree'.
Exception occurred with following context:
Compilation: <project namespace>
SyntaxTree: <full path to partial class file>
SyntaxNode: public partial class <class name> ... [ClassDeclarationSyntax]@[407..5663) (20,0)-(162,1)

System.ArgumentException: Syntax node is not within syntax tree
   at Microsoft.CodeAnalysis.CSharp.CSharpSemanticModel.CheckSyntaxNode(CSharpSyntaxNode syntax)
   at Microsoft.CodeAnalysis.CSharp.CSharpSemanticModel.GetTypeInfo(ExpressionSyntax expression, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetTypeInfo(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken)
   at NServiceBus.Core.Analyzer.SagaAnalyzer.<>c__DisplayClass5_0.<AnalyzeSagaClass>b__3(SagaMessageMapping m) in /_/src/NServiceBus.Core.Analyzer/Sagas/SagaAnalyzer.cs:line 238
   at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
   at System.Collections.Immutable.ImmutableHashSet`1.Union(IEnumerable`1 other, MutationInput origin)
   at System.Collections.Immutable.ImmutableHashSet`1.Union(IEnumerable`1 items, Boolean avoidWithComparer)
   at NServiceBus.Core.Analyzer.SagaAnalyzer.AnalyzeSagaClass(SyntaxNodeAnalysisContext context, ClassDeclarationSyntax classDeclaration, INamedTypeSymbol sagaType, KnownTypes knownTypes) in /_/src/NServiceBus.Core.Analyzer/Sagas/SagaAnalyzer.cs:line 237
   at NServiceBus.Core.Analyzer.SagaAnalyzer.Analyze(SyntaxNodeAnalysisContext context, KnownTypes knownTypes) in /_/src/NServiceBus.Core.Analyzer/Sagas/SagaAnalyzer.cs:line 70
   at NServiceBus.Core.Analyzer.SagaAnalyzer.<>c__DisplayClass3_0.<Analyze>b__0(SyntaxNodeAnalysisContext context) in /_/src/NServiceBus.Core.Analyzer/Sagas/SagaAnalyzer.cs:line 50
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__62`1.<ExecuteSyntaxNodeAction>b__62_0(ValueTuple`2 data)
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info)
-----

Suppress the following diagnostics to disable this analyzer: NSB0003, NSB0004, NSB0005, NSB0006, NSB0007, NSB0008, NSB0009, NSB0010, NSB0011, NSB0012, NSB0013, NSB0014, NSB0015, NSB0016, NSB0017, NSB0018	Microservice.Encompass.LoanDataBackup		1	Active
@DavidBoike
Copy link
Member

Hi @nrsmp, thanks for the report!

I included partial classes in the tests for the analyzer, as they present analyzers with very interesting edge and corner cases, but obviously I missed something.

Could you the code that caused the analyzer to fail? A simplified form of the saga with method implementations removed and variable names completely obfuscated would be totally fine.

@nrsmp
Copy link
Author

nrsmp commented Apr 26, 2022

Certainly. Here's simplified code that reproduces the analyzer errors. Each class lives in its own file, and all files live in the same parent folder.

SagaImplementation.cs

using System.Threading.Tasks;

using NServiceBus;

namespace Saga.Implementation;

public partial class SagaImplementation : Saga<SagaData>, IAmStartedByMessages<SagaStartMessage>
{
    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<SagaData> mapper)
    {
        mapper.MapSaga(s => s.OrderId)
            .ToMessage<SagaStartMessage>(b => b.OrderId)
            .ToMessage<SagaStep1>(b => b.OrderId)
            .ToMessage<SagaStep2>(s => s.OrderId)
            .ToMessage<SagaStep3>(b => b.OrderId);
    }

    public async Task Handle(SagaStartMessage message, IMessageHandlerContext context)
    {
        var options = new SendOptions();
        options.RouteToThisEndpoint();
        await context.Send(new SagaStep1 {OrderId = Data.OrderId}, options);
        await context.Send(new SagaStep2 {OrderId = Data.OrderId}, options);
        await context.Send(new SagaStep3 {OrderId = Data.OrderId}, options);
    }

    private void CompleteSaga()
    {
        if(Data.Step1Complete && Data.Step2Complete && Data.Step3Complete)
        {
            MarkAsComplete();
        }
    }
}

SagaStep1.cs

using System.Threading.Tasks;

using NServiceBus;

namespace Saga.Implementation;

public partial class SagaImplementation : IHandleMessages<SagaStep1>
{
    public Task Handle(SagaStep1 message, IMessageHandlerContext context)
    {
        Data.Step1Complete = true;
        CompleteSaga();
        return Task.CompletedTask;
    }
}

SagaStep2.cs

using System.Threading.Tasks;

using NServiceBus;

namespace Saga.Implementation;

public partial class SagaImplementation : IHandleMessages<SagaStep2>
{
    public  Task Handle(SagaStep2 message, IMessageHandlerContext context)
    {
        Data.Step2Complete = true;
        CompleteSaga();
        return Task.CompletedTask;
    }
}

SagaStep3.cs

using System.Threading.Tasks;

using NServiceBus;

using Shared.NServiceBus.Messages.Commands.Encompass;

namespace Saga.Implementation;

public partial class SagaImplementation : IHandleMessages<SagaStep3>
{
    public Task Handle(SagaStep3 message, IMessageHandlerContext context)
    {
        Data.Step3Complete = true;
        CompleteSaga();
        return Task.CompletedTask;
    }
}

SagaModels.cs

using NServiceBus;

namespace Saga.Implementation;

public class SagaData : ContainSagaData
{
    public string OrderId { get; set; } = null!;

    public bool Step1Complete { get; set; }
    public bool Step2Complete { get; set; }
    public bool Step3Complete { get; set; }
}

public class SagaStartMessage : ICommand
{
    public string OrderId { get; set; } = null!;
}

public class SagaStep1 : SagaStartMessage
{
}

public class SagaStep2 : SagaStartMessage
{
}

public class SagaStep3 : SagaStartMessage
{
}

@DavidBoike DavidBoike changed the title v7.7.0+ Saga<T> creates Syntax tree errors with partial classes Saga analyzer throws exception when handlers in partial classes Apr 26, 2022
@DavidBoike DavidBoike changed the title Saga analyzer throws exception when handlers in partial classes Saga analyzer throws exception when handlers implemented in partial classes Apr 26, 2022
@DavidBoike DavidBoike added the Bug label Apr 26, 2022
@DavidBoike DavidBoike added this to the 7.7.3 milestone Apr 26, 2022
@DavidBoike
Copy link
Member

Fixed in #6372. This will be released shortly in NServiceBus 7.7.3.

@DavidBoike
Copy link
Member

@nrsmp NServiceBus 7.7.3 is now released. Thanks again for the report and repro code!

@nrsmp
Copy link
Author

nrsmp commented Apr 26, 2022

Wow, that was fast! You're quite welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants