Skip to content

Commit

Permalink
Fix common object builder uow scope (#6458)
Browse files Browse the repository at this point in the history
* Add a number of tests for the common object builder behavior

* Fix instance per uow resolved via builder

* Remove local version of async local used in acceptance testing since we use InternalsVisibleTo

* Remove the static

* Move things into a property

* Add a test that uses Task.Run to fetch within the same scope

* Resolve the current builder per instance to make it possible to resolve the correct IBuilder instance (#6460)

* Resolve the current builder per instance to make it possible to resolve the correct IBuilder instance

* InstancePerCall

* Rename to parent and move fields
  • Loading branch information
danielmarbach committed Jul 20, 2022
1 parent 2e16591 commit 7f819fc
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ NServiceBus.Hosting.HostInformation - SingleInstance
NServiceBus.MessageInterfaces.IMessageMapper - SingleInstance
NServiceBus.NoOpCanceling - SingleInstance
NServiceBus.Notifications - SingleInstance
NServiceBus.ObjectBuilder.IBuilder - SingleInstance
NServiceBus.ObjectBuilder.IBuilder - InstancePerCall
NServiceBus.Pipeline.LogicalMessageFactory - SingleInstance
NServiceBus.Settings.ReadOnlySettings - SingleInstance
NServiceBus.Unicast.Messages.MessageMetadataRegistry - SingleInstance
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using EndpointTemplates;
using NServiceBus.Pipeline;
using NUnit.Framework;
using ObjectBuilder;

public class When_using_per_uow_component_in_the_pipeline : NServiceBusAcceptanceTest
{
Expand Down Expand Up @@ -93,21 +94,23 @@ class UnitOfWorkComponent

class Handler : IHandleMessages<Message>
{
public Handler(Context testContext, UnitOfWorkComponent component)
public Handler(Context testContext, UnitOfWorkComponent component, IBuilder builder)
{
this.testContext = testContext;
this.component = component;
anotherComponent = builder.Build<UnitOfWorkComponent>();
}

public Task Handle(Message message, IMessageHandlerContext context)
{
testContext.ValueEmpty |= component.ValueFromHeader == null;
testContext.ValueEmpty |= component.ValueFromHeader == null || anotherComponent.ValueFromHeader == null;
testContext.OnMessageProcessed();
return Task.FromResult(0);
}

Context testContext;
UnitOfWorkComponent component;
readonly Context testContext;
readonly UnitOfWorkComponent component;
readonly UnitOfWorkComponent anotherComponent;
}
}

Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/NServiceBus.Core/Hosting/HostCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static async Task<IStartableEndpoint> CreateWithInternallyManagedContaine
IBuilder internalBuilder = commonObjectBuilder;

//for backwards compatibility we need to make the IBuilder available in the container
internalContainer.ConfigureComponent(_ => internalBuilder, DependencyLifecycle.SingleInstance);
internalContainer.ConfigureComponent(_ => commonObjectBuilder.Current, DependencyLifecycle.InstancePerCall);

var hostingConfiguration = HostingComponent.PrepareConfiguration(settings.Get<HostingComponent.Settings>(), assemblyScanningComponent, internalContainer);

Expand Down
86 changes: 27 additions & 59 deletions src/NServiceBus.Core/ObjectBuilder/Common/CommonObjectBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,72 @@ namespace NServiceBus
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ObjectBuilder;
using ObjectBuilder.Common;

class CommonObjectBuilder : IBuilder, IConfigureComponents
sealed class CommonObjectBuilder : IBuilder, IConfigureComponents
{
public CommonObjectBuilder(IContainer container)
public CommonObjectBuilder(IContainer container, AsyncLocal<IBuilder> scope = null)
{
this.container = container;
currentBuilder = scope ?? new AsyncLocal<IBuilder>();
parent = currentBuilder.Value;
currentBuilder.Value = this;
}

public IBuilder CreateChildBuilder()
{
return new CommonObjectBuilder(container.BuildChildContainer());
}
// returns the current builder or the root
public IBuilder Current => currentBuilder.Value ?? this;

public IBuilder CreateChildBuilder() => new CommonObjectBuilder(container.BuildChildContainer(), currentBuilder);

public void Dispose()
{
//Injected at compile time
}

public T Build<T>()
{
return (T)container.Build(typeof(T));
}
public T Build<T>() => (T)container.Build(typeof(T));

public object Build(Type typeToBuild)
{
return container.Build(typeToBuild);
}
public object Build(Type typeToBuild) => container.Build(typeToBuild);

IEnumerable<object> IBuilder.BuildAll(Type typeToBuild)
{
return container.BuildAll(typeToBuild);
}
IEnumerable<object> IBuilder.BuildAll(Type typeToBuild) => container.BuildAll(typeToBuild);

void IBuilder.Release(object instance)
{
container.Release(instance);
}
void IBuilder.Release(object instance) => container.Release(instance);

public IEnumerable<T> BuildAll<T>()
{
return container.BuildAll(typeof(T)).Cast<T>();
}
public IEnumerable<T> BuildAll<T>() => container.BuildAll(typeof(T)).Cast<T>();

public void BuildAndDispatch(Type typeToBuild, Action<object> action)
{
var o = container.Build(typeToBuild);
action(o);
}

public void ConfigureComponent(Type concreteComponent, DependencyLifecycle instanceLifecycle)
{
container.Configure(concreteComponent, instanceLifecycle);
}
public void ConfigureComponent(Type concreteComponent, DependencyLifecycle instanceLifecycle) => container.Configure(concreteComponent, instanceLifecycle);

public void ConfigureComponent<T>(DependencyLifecycle instanceLifecycle)
{
container.Configure(typeof(T), instanceLifecycle);
}
public void ConfigureComponent<T>(DependencyLifecycle instanceLifecycle) => container.Configure(typeof(T), instanceLifecycle);

public void ConfigureComponent<T>(Func<T> componentFactory, DependencyLifecycle instanceLifecycle)
{
container.Configure(componentFactory, instanceLifecycle);
}
public void ConfigureComponent<T>(Func<T> componentFactory, DependencyLifecycle instanceLifecycle) => container.Configure(componentFactory, instanceLifecycle);

public void ConfigureComponent<T>(Func<IBuilder, T> componentFactory, DependencyLifecycle instanceLifecycle)
{
container.Configure(() => componentFactory(this), instanceLifecycle);
}
public void ConfigureComponent<T>(Func<IBuilder, T> componentFactory, DependencyLifecycle instanceLifecycle) => container.Configure(() => componentFactory(Current), instanceLifecycle);

void IConfigureComponents.RegisterSingleton(Type lookupType, object instance)
{
container.RegisterSingleton(lookupType, instance);
}
void IConfigureComponents.RegisterSingleton(Type lookupType, object instance) => container.RegisterSingleton(lookupType, instance);

public void RegisterSingleton<T>(T instance)
{
container.RegisterSingleton(typeof(T), instance);
}
public void RegisterSingleton<T>(T instance) => container.RegisterSingleton(typeof(T), instance);

public bool HasComponent<T>()
{
return container.HasComponent(typeof(T));
}
public bool HasComponent<T>() => container.HasComponent(typeof(T));

public bool HasComponent(Type componentType)
{
return container.HasComponent(componentType);
}
public bool HasComponent(Type componentType) => container.HasComponent(componentType);

#pragma warning disable IDE0051 // Remove unused private members
void DisposeManaged()
#pragma warning restore IDE0051 // Remove unused private members
{
currentBuilder.Value = parent;
container?.Dispose();
}

IContainer container;
readonly IContainer container;
readonly AsyncLocal<IBuilder> currentBuilder;
readonly IBuilder parent;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#if NETFRAMEWORK
namespace System.Threading
{
using Runtime.Remoting.Messaging;
using static Runtime.Remoting.Messaging.CallContext;

// Provides a polyfill of AsyncLocal in .NET 4.5.2
sealed class AsyncLocal<T>
Expand All @@ -13,24 +13,17 @@ public T Value
{
get
{
var localValue = CallContext.LogicalGetData(id);
var localValue = LogicalGetData(id);
if (localValue != null)
{
return (T)localValue;
}
return default;
}
set
{
// ReSharper disable once ArrangeAccessorOwnerBody
CallContext.LogicalSetData(id, value);
}
set => LogicalSetData(id, value);
}

public AsyncLocal()
{
id = Guid.NewGuid().ToString();
}
public AsyncLocal() => id = Guid.NewGuid().ToString();
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ NServiceBus.InMemoryTransactionalSynchronizedStorageAdapter - SingleInstance
NServiceBus.MessageInterfaces.IMessageMapper - SingleInstance
NServiceBus.NoOpCanceling - SingleInstance
NServiceBus.Notifications - SingleInstance
NServiceBus.ObjectBuilder.IBuilder - SingleInstance
NServiceBus.ObjectBuilder.IBuilder - InstancePerCall
NServiceBus.Pipeline.LogicalMessageFactory - SingleInstance
NServiceBus.Settings.ReadOnlySettings - SingleInstance

0 comments on commit 7f819fc

Please sign in to comment.