z4kn4fein / stashbox

A lightweight, fast, and portable dependency injection framework for .NET-based solutions.
https://z4kn4fein.github.io/stashbox
MIT License
140 stars 10 forks source link

3.1.0-preview-531: Exception when building expressions #76

Closed schuettecarsten closed 4 years ago

schuettecarsten commented 4 years ago

I get the following exception when upgrading one of my projects to Stashbox preview version 3.1.0-preview-531. The same code worked fine using 2.8.9 before. Exception message and callstack are: Auf die Variable "" vom Typ "Neusta.Shared.Services.UnitOfWork.IServiceContextWithUnitOfWork" wird vom Bereich "" verwiesen, sie ist jedoch nicht definiert.

   at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitNew(NewExpression node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator)
   at System.Linq.Expressions.ExpressionExtensions.CompileDelegate(Expression expression, ResolutionContext resolutionContext, ContainerConfiguration containerConfiguration)
   at Stashbox.Lifetime.ScopedLifetime.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, IObjectBuilder objectBuilder, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.Registration.ServiceRegistration.ConstructExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.Registration.ServiceRegistration.GetExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.Resolution.ResolutionStrategy.BuildResolutionExpression(IContainerContext containerContext, ResolutionContext resolutionContext, TypeInformation typeInformation, IEnumerable`1 injectionParameters, Boolean forceSkipUnknownTypeCheck)
   at Stashbox.BuildUp.Expressions.MethodExpressionBuilder.TryBuildMethod(MethodBase method, RegistrationContext registrationContext, ResolutionContext resolutionContext, IContainerContext containerContext, TypeInformation& failedParameter, Expression[]& parameterExpressions, Boolean skipUknownResolution)
   at Stashbox.BuildUp.Expressions.MethodExpressionBuilder.SelectConstructor(IContainerContext containerContext, RegistrationContext registrationContext, ResolutionContext resolutionContext, ConstructorInfo[] constructors, Expression[]& parameterExpressions)
   at Stashbox.BuildUp.Expressions.ExpressionBuilder.CreateInitExpression(IContainerContext containerContext, RegistrationContext registrationContext, ResolutionContext resolutionContext, ConstructorInfo[] constructors)
   at Stashbox.BuildUp.Expressions.ExpressionBuilder.ConstructExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type serviceType)
   at Stashbox.BuildUp.ObjectBuilders.DefaultObjectBuilder.PrepareExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.BuildUp.ObjectBuilders.DefaultObjectBuilder.GetExpressionInternal(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.BuildUp.ObjectBuilderBase.BuildDisposalTrackingAndFinalizerExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.BuildUp.ObjectBuilderBase.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.Lifetime.ScopedLifetime.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, IObjectBuilder objectBuilder, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.Registration.ServiceRegistration.ConstructExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.Registration.ServiceRegistration.GetExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType)
   at Stashbox.ResolutionScope.Activate(ResolutionContext resolutionContext, Type type, Object name)
   at Stashbox.ResolutionScope.Resolve(Type typeFrom, Boolean nullResultAllowed, Object[] dependencyOverrides)
   at Stashbox.DependencyResolverExtensions.Resolve[TKey](IDependencyResolver resolver, Boolean nullResultAllowed, Object[] dependencyOverrides)
   at Neusta.Shared.ObjectProvider.Stashbox.Service.StashboxScope.GetInstance[TService](Object[] dependencyOverrides) in D:\Projekte\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Service\StashboxScope.cs:line 117
z4kn4fein commented 4 years ago

Thanks for reporting this issue! This looks bad, I did a major refactor in 3.1 but it seems some cases are not covered yet, I'm going to dig into this, in the meantime could you please give me some details about the context? The type requested when this error occurred, the lifetimes of the dependencies or a small sample project where I can reproduce, every detail could help, thanks in advance!

schuettecarsten commented 4 years ago

The type requested is registered as plain class, no interfaces, and without lifetime. It is requested using Resolve<BillingPeriodService>(false, null); from a named scope. I will try to reproduce in a small test project or - maybe it's a good idea - checkout the stashbox source code and try to get any details from the debugger.

    [UsedImplicitly]
    public class BillingPeriodService : BaseServiceWithUnitOfWork
    {
        public BillingPeriodService(IServiceContextWithUnitOfWork serviceContext, AccountService accountService)
            : base(serviceContext)
        {
    [UsedImplicitly]
    public class AccountService : BaseServiceWithUnitOfWork
    {
        public AccountService(IServiceContextWithUnitOfWork serviceContext)
            : base(serviceContext)
        {
    [UsedImplicitly]
    public class ServiceContextWithUnitOfWork : ServiceContext, IServiceContextWithUnitOfWork
    {
        public ServiceContextWithUnitOfWork(IObjectProvider objectProvider)
            : base(objectProvider)
        {

Please note that the IObjectProvider is the same interface from my previous ticket, where resolving it to the factory does not work in my project.

z4kn4fein commented 4 years ago

Thanks for the context! I'm going to check what could be the issue here.

schuettecarsten commented 4 years ago

I just figured out that it works with preview-519, but fails with preview-520. Maybe this helps, unfortunately, I cannot see which changes were made between these two preview releases.

z4kn4fein commented 4 years ago

Yep, the changes between those are related to the scoped resolution, thanks for the info!

schuettecarsten commented 4 years ago

This is the expression that fails to compile (from latest master):

.Block(AccountService $var1) {
    .Call $scope.CheckRuntimeCircularDependencyBarrier(
        459,
        .Constant<System.RuntimeType>(AccountService));
    $var1 = .Call $scope.AddDisposableTracking(.New AccountService($var2)
    );
    .Call $scope.ResetRuntimeCircularDependencyBarrier(459);
    $var1
}

Just another idea, it's using a block here, starts a check and resets a check. Isn't a try/finally block a good idea here, to make sure that everything stays intact when the constructor itself fails with an exception?

z4kn4fein commented 4 years ago

Thanks! I found the root cause of this issue, I'll let you know when the fix is ready.

schuettecarsten commented 4 years ago

Just for your information, it still does not work after commit https://github.com/z4kn4fein/stashbox/commit/c20c4c5bd4874e44c84dd5f296a05600221a3859


System.InvalidOperationException
  HResult=0x80131509
  Message=Auf die Variable "" vom Typ "Neusta.Shared.Services.UnitOfWork.IServiceContextWithUnitOfWork" wird vom Bereich "" verwiesen, sie ist jedoch nicht definiert.
  Source=System.Core
  StackTrace:
   at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator)
   at System.Linq.Expressions.Expression`1.Compile()
   at System.Linq.Expressions.ExpressionExtensions.CompileDelegate(Expression expression, ResolutionContext resolutionContext, ContainerConfiguration containerConfiguration) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Extensions\ExpressionExtensions.cs:line 47
   at Stashbox.Lifetime.SingletonLifetime.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, IObjectBuilder objectBuilder, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Lifetime\SingletonLifetime.cs:line 29
   at Stashbox.Registration.ServiceRegistration.ConstructExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Registration\ServiceRegistration.cs:line 124
   at Stashbox.Registration.ServiceRegistration.GetExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Registration\ServiceRegistration.cs:line 104
   at Stashbox.Resolution.ResolutionStrategy.BuildResolutionExpression(IContainerContext containerContext, ResolutionContext resolutionContext, TypeInformation typeInformation, IEnumerable`1 injectionParameters, Boolean forceSkipUnknownTypeCheck) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Resolution\ResolutionStrategy.cs:line 76
   at Stashbox.BuildUp.Expressions.MethodExpressionBuilder.TryBuildMethod(MethodBase method, RegistrationContext registrationContext, ResolutionContext resolutionContext, IContainerContext containerContext, TypeInformation& failedParameter, Expression[]& parameterExpressions, Boolean skipUknownResolution) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\Expressions\MethodExpressionBuilder.cs:line 120
   at Stashbox.BuildUp.Expressions.MethodExpressionBuilder.SelectConstructor(IContainerContext containerContext, RegistrationContext registrationContext, ResolutionContext resolutionContext, ConstructorInfo[] constructors, Expression[]& parameterExpressions) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\Expressions\MethodExpressionBuilder.cs:line 52
   at Stashbox.BuildUp.Expressions.ExpressionBuilder.CreateInitExpression(IContainerContext containerContext, RegistrationContext registrationContext, ResolutionContext resolutionContext, ConstructorInfo[] constructors) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\Expressions\ExpressionBuilder.cs:line 204
   at Stashbox.BuildUp.Expressions.ExpressionBuilder.ConstructExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type serviceType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\Expressions\ExpressionBuilder.cs:line 110
   at Stashbox.BuildUp.ObjectBuilders.DefaultObjectBuilder.PrepareExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\ObjectBuilders\DefaultObjectBuilder.cs:line 38
   at Stashbox.BuildUp.ObjectBuilders.DefaultObjectBuilder.GetExpressionInternal(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\ObjectBuilders\DefaultObjectBuilder.cs:line 30
   at Stashbox.BuildUp.ObjectBuilderBase.BuildDisposalTrackingAndFinalizerExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\ObjectBuilderBase.cs:line 52
   at Stashbox.BuildUp.ObjectBuilderBase.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\BuildUp\ObjectBuilderBase.cs:line 21
   at Stashbox.Lifetime.LifetimeBase.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, IObjectBuilder objectBuilder, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Lifetime\LifetimeBase.cs:line 16
   at Stashbox.Lifetime.ScopedLifetimeBase.GetFactoryExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, IObjectBuilder objectBuilder, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Lifetime\ScopedLifetimeBase.cs:line 39
   at Stashbox.Lifetime.ScopedLifetime.GetExpression(IContainerContext containerContext, IServiceRegistration serviceRegistration, IObjectBuilder objectBuilder, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Lifetime\ScopedLifetime.cs:line 25
   at Stashbox.Registration.ServiceRegistration.ConstructExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Registration\ServiceRegistration.cs:line 124
   at Stashbox.Registration.ServiceRegistration.GetExpression(IContainerContext containerContext, ResolutionContext resolutionContext, Type resolveType) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Registration\ServiceRegistration.cs:line 104
   at Stashbox.ResolutionScope.Activate(ResolutionContext resolutionContext, Type type, Object name) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\ResolutionScope.cs:line 292
   at Stashbox.ResolutionScope.Resolve(Type typeFrom, Boolean nullResultAllowed, Object[] dependencyOverrides) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\ResolutionScope.cs:line 86
   at Stashbox.DependencyResolverExtensions.Resolve[TKey](IDependencyResolver resolver, Boolean nullResultAllowed, Object[] dependencyOverrides) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Stashbox\src\Resolution\Extensions\DependencyResolverExtensions.cs:line 20
   at Neusta.Shared.ObjectProvider.Stashbox.Service.StashboxScope.GetInstance[TService](Object[] dependencyOverrides) in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Stashbox\Service\StashboxScope.cs:line 117
   at Neusta.Shared.ObjectProvider.Utils.ScopeHelper.<ExecuteServiceInScopeAsync>d__6`1.MoveNext() in D:\Projekte\Azure\CloudManagementTool\Source\SharedLibrary\Neusta.Shared.ObjectProvider.Abstractions\Utils\ScopeHelper.cs:line 126
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at CloudManagementTool.InteractiveWorker.<RunDebugAsync>d__2.MoveNext() in D:\Projekte\Azure\CloudManagementTool\Source\CloudManagementTool\CloudManagementTool\InteractiveWorker.cs:line 50

  This exception was originally thrown at this call stack:
    System.Linq.Expressions.Compiler.VariableBinder.Reference(System.Linq.Expressions.ParameterExpression, System.Linq.Expressions.Compiler.VariableStorageKind)
    System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(System.Linq.Expressions.ParameterExpression)
    System.Linq.Expressions.Compiler.VariableBinder.Visit(System.Linq.Expressions.Expression)
    System.Linq.Expressions.ExpressionVisitor.VisitBinary(System.Linq.Expressions.BinaryExpression)
    System.Linq.Expressions.Compiler.VariableBinder.Visit(System.Linq.Expressions.Expression)
    System.Linq.Expressions.ExpressionVisitor.Visit(System.Collections.ObjectModel.ReadOnlyCollection<System.Linq.Expressions.Expression>)
    System.Linq.Expressions.Compiler.VariableBinder.VisitLambda<T>(System.Linq.Expressions.Expression<T>)
    System.Linq.Expressions.Compiler.VariableBinder.Visit(System.Linq.Expressions.Expression)
    System.Linq.Expressions.Compiler.LambdaCompiler.Compile(System.Linq.Expressions.LambdaExpression, System.Runtime.CompilerServices.DebugInfoGenerator)
    System.Linq.Expressions.Expression<TDelegate>.Compile()
    ...
    [Call Stack Truncated]
z4kn4fein commented 4 years ago

Thanks for the fast feedback! I'm aware of this, the previous commit was only the first part of the whole fix, unfortunately, it'll take a bit more time, some skeletons fell out during the refactor, so I have to do some more investigation about what parts are also affected. This is another pack of the fix, could you please run a new test with it? Thanks! And also for your patience!

schuettecarsten commented 4 years ago

Thank you for the fix - it works now.