Skip to content

Commit

Permalink
Merge pull request #6466 from Particular/fix-common-object-builder
Browse files Browse the repository at this point in the history
Fix common object builder uow scope
  • Loading branch information
danielmarbach authored Jul 20, 2022
2 parents c846d39 + 0875df4 commit 88b1e5f
Show file tree
Hide file tree
Showing 9 changed files with 515 additions and 83 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 @@ -19,9 +19,9 @@ public async Task Message_should_be_moved_to_error_because_handler_not_found()
.Done(c => c.FailedMessages.Any())
.Run();

Assert.True(context.Logs.Any(l => l.Level == LogLevel.Error && l.Message.Contains($"No handlers could be found for message type: { typeof(MyCommand).FullName}")), "No handlers could be found was not logged.");
Assert.False(context.Logs.Any(l => l.Level == LogLevel.Warn && l.Message.Contains($"Message header '{ typeof(MyCommand).FullName }' was mapped to type '{ typeof(MyCommand).FullName }' but that type was not found in the message registry, ensure the same message registration conventions are used in all endpoints, especially if using unobtrusive mode.")), "Message type could not be mapped.");
Assert.False(context.Logs.Any(l => l.Level == LogLevel.Warn && l.Message.Contains($"Could not determine message type from message header '{ typeof(MyCommand).FullName}'")), "Message type could not be mapped.");
Assert.True(context.Logs.Any(l => l.Level == LogLevel.Error && l.Message.Contains($"No handlers could be found for message type: {typeof(MyCommand).FullName}")), "No handlers could be found was not logged.");
Assert.False(context.Logs.Any(l => l.Level == LogLevel.Warn && l.Message.Contains($"Message header '{typeof(MyCommand).FullName}' was mapped to type '{typeof(MyCommand).FullName}' but that type was not found in the message registry, ensure the same message registration conventions are used in all endpoints, especially if using unobtrusive mode.")), "Message type could not be mapped.");
Assert.False(context.Logs.Any(l => l.Level == LogLevel.Warn && l.Message.Contains($"Could not determine message type from message header '{typeof(MyCommand).FullName}'")), "Message type could not be mapped.");
}

public class Context : ScenarioContext { }
Expand Down
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.

6 changes: 3 additions & 3 deletions src/NServiceBus.Core.Tests/StructConventionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ static bool InspectWhetherStructContainsReferenceTypes(Type type, List<string> v

if (fieldInfo.FieldType.IsClass || fieldInfo.FieldType.IsInterface)
{
mutabilityRules.Add($" - Field {fieldInfo.Name} of type { fieldInfo.FieldType } is a reference type.");
mutabilityRules.Add($" - Field {fieldInfo.Name} of type {fieldInfo.FieldType} is a reference type.");
}
}

Expand All @@ -108,7 +108,7 @@ static bool InspectWhetherStructContainsPublicFields(Type type, List<string> vio
{
if (!fieldInfo.IsInitOnly && !fieldInfo.IsLiteral)
{
mutabilityRules.Add($" - Field {fieldInfo.Name} of type { fieldInfo.FieldType } is public.");
mutabilityRules.Add($" - Field {fieldInfo.Name} of type {fieldInfo.FieldType} is public.");
}
}

Expand All @@ -132,7 +132,7 @@ static bool InspectWhetherStructContainsWritableProperties(Type type, List<strin
{
if (property.CanWrite)
{
mutabilityRules.Add($" - Property {property.Name} of type { property.PropertyType } can be written to.");
mutabilityRules.Add($" - Property {property.Name} of type {property.PropertyType} can be written to.");
}
}

Expand Down
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 88b1e5f

Please sign in to comment.