How can I use IWantToRunBeforeConfigurationIsFinalized , inject propertys into the handlers *and* continue using the Nservicebus test framework? - autofac

Nervicebus 4.6 Unit Testing issue
I recently discovered IWantToRunBeforeConfigurationIsFinalized to inject propertys into a handler, changed all my service code and now suddenly all my NserviceBus.Test fails.
public void run {
Configure.Instance.Configurer.ConfigureProperty<MyMessage_Handler>(h => h.MyProperty, "somevalue");
}
When i atttempt a unit test
var message = new MyMessage
{
...
...
};
Test.Handler<MyMessage_Handler>().WithExternalDependencies(m =>
{
m.PropertyA = aValue;
m.PropertyB = bValue;
})
.ExpectPublish<MyEvent>(m =>
{
Assert.That(...);
return true;
})
.OnMessage<MyMessage>(message);
I receive the following exception
TestFixture failed: SetUp : System.InvalidOperationException : Cannot
configure properties for a type that hasn't been configured yet:
at
NServiceBus.ObjectBuilder.Autofac.AutofacObjectBuilder.ConfigureProperty(Type
component, String property, Object value) in
y:\BuildAgent\work\31f8c64a6e8a2d7c\src\NServiceBus.Core\ObjectBuilder\Autofac\AutofacObjectBuilder.cs:line
114
However if i configure the bus like this, calling configurecomponent on the handler. The test framework works
This was my original method, it works fine but i prefer the approach of IWantToRunBeforeConfigurationIsFinalized to cleanly seperate the logic.
Configure.With()
.DefaultBuilder()
.PurgeOnStartup(false)
.RunCustomAction(() => Configure.Instance.Configurer.ConfigureComponent<MyMessage_Handler>(DependencyLifecycle.InstancePerUnitOfWork))
.RunCustomAction(() => {
Configure.Instance.Configurer.ConfigureProperty<MyMessage_Handler>(h => h.MyProperty, "myValue");
}
...
How can I use IWantToRunBeforeConfigurationIsFinalized , inject propertys into the handlers and continue using the Nservicebus test framework?

It seems odd to try to configure properties directly on handlers. If it's only needed by one handler, then it should be dealt with in that handler, perhaps in a static constructor in that handler class.
At any rate, you're putting yourself at the mercy of when types are loaded up and registered into the container, which isn't necessarily deterministic across different versions of NServiceBus, and clearly not when dealing with the testing framework.
Instead, what if you created a new class:
public class ReferenceValues
{
public string MyProperty { get; set; }
}
Then, you could register this type normally, from any point in the endpoint startup. Preferably, as an INeedInitialization since most of the extensibility points go away in NServiceBus 5.x and INeedInitialization is really the only game in town.
// V4 code
public class SetupReferenceValues : INeedInitialization
{
public void Init()
{
Configure.Component<ReferenceValues>(DependencyLifecycle.SingleInstance)
.ConfigureProperty(rv => rv.MyProperty, "myValue");
}
}
Then, in your handler:
public class MyHandler : IHandleMessages<MyMessage>
{
public ReferenceValues Values { get; set; }
public void Handle(MyMessage message)
{
Console.WriteLine(Values.MyProperty);
}
}
I believe this should work from the testing framework as well.

Related

Derived attribute with AsTajson call in the custom service

I use .net5 and .netStandard from nuget. The structure of the solution is simple - class library with ecomodel and webapi project.
I've registers custom service in the EcoSpace1.cs as it was advised by eco gurus long, long time ago.
public interface IMyService
{
int Class1Count();
}
public class MyServiceClass : IMyService
{
private IEcoServiceProvider ServiceProvider { get; set; }
public MyServiceClass(IEcoServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public int Class1Count()
{
var v = ServiceProvider.GetEcoService<IOclService>().Evaluate("Class1.allInstances->size");
return (int)v.AsObject;
}
}
The rule for a webapi project's controller- inherit from the Controller class - due to the Swashbuckle.AspNetCore (swagger) usage. Swagger shows errors if I inherit a controller from the ModelDrivenControllerBase
But the code above is OK, the GET works perfect:
public class MySecondEcoServiceController : Controller
{
[HttpGet]
public int Get()
{
using (EcoSpace1 es=new EcoSpace1())
{
es.Active = true;
int r = es.MyService.Class1Count();
return r;
}
}
}
It works till I tried to add another method into the IMyService and to get json. I tried AsTajson by adding derived attribute to the Class1 with DerivationOcl.
Class1.allInstances->first.AsTaJson( 'SampleViewModel', false )
or
self.AsTaJson( 'SampleViewModel', false )
In the MyServiceClass the implementation of the Get is:
Class1 v = (Class1)ServiceProvider.GetEcoService<IOclService>().Evaluate("Class1.allInstances->first").AsObject;
json = v.Attribute2;
If I tried GET this value - exception:
Eco.FrameworkImpl.Ocl.EBoldOCLError: 'bughuntinfo internaleval:84 es seems fine.Object reference not set to an instance of an object.'
What is the proper way (or EcoService?) to get values returned by TaJson?
Not sure but please check that you have initiated use of ViewModels once for your EcoSpace type:
ViewModelDefinitionsInApplication.Init(_es);

Autofac service not registered but it is

Lately I have some issues with Autofac.
I have an aggregate service like this:
public interface ICommonServices
{
IQueryBus QueryBus { get; }
ICommandBus CommandBus { get; }
INotificationBus NotificationBus { get; }
ILoggerFactory LoggerFactory { get; }
}
And I am registering it like this:
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAggregateService<ICommonServices>();
}
}
And when I do:
var services = container.Resolve<ICommonServices>();
I am getting this error:
"The requested service 'Infrastructure.ICommonServices' has not been registered.
To avoid this exception, either register a component to provide the service, check for service
registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency."
I have many assemblies and I use Autofac modules. If I put a breakpoint in the module I see that it is called and registered. Also if I inspect Container registrations, I see that there is a registration for ICommonServices:
Activator = Object (DelegateActivator), Services = [Infrastructure.ICommonServices],
Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope,
Pipeline = Autofac.Core.Pipeline.ResolvePipeline
If I move the registration out from AutofacModule to the main assembly, jst before the builder.Build() then all works and Autofac is abble to resolve it.
Why then the error if the service is registered? And this is not the only one.
The issue was Assembly scanning in .net core 3.1.
The old - non working way:
var assemblies= Assembly.GetExecutingAssembly()
.GetAllAssemblies()
.ToArray();
the new - WORKING one:
var l2Assemblies = Assembly.GetExecutingAssembly()
.GetAllAssembliesWithContext()
.ToArray();

Registering component in autofac

I'm new to autofac(using 2.1.14.854),and im still trying to put my head around in trying to understand
I have an interface and there are one or more implementations to this interface, and the implementation(s) should be fired in a specific sequence.
For example:
public IPipeline
{
void execute();
}
public MyPipeLine_1:IPipeline
{
public void execute(){}
}
public MyPipeLine_2:IPipeline
{
public void execute(){}
}
foreach(IPipeline pipeline in pipelines)
pipeline.execute();
The order execution of IPipeline should be MyPipleLine_2,MyPipleLine_1, etc
I have two questions
1) how to register all the components, that implements IPipeLine interface in a assembly and place them in a List
2) can i define the order of the execution of these components whilst registering
Thanks in advance.
[A quick note: You're using a really old version of Autofac. You may need to update to get the features I'm talking about.]
The first part is easy - Autofac implicitly supports IEnumerable<T>. Just register all the types and resolve:
var builder = new ContainerBuilder();
builder.RegisterType<MyPipeLine_1>().As<IPipeline>();
builder.RegisterType<MyPipeLine_2>().As<IPipeline>();
var container = builder.Build();
var containsAllPipelineComponents = container.Resolve<IEnumerable<IPipeline>>();
It'd be better if you can take it as an IEnumerable<T> rather than a list, but if you have to have a list, you could add a registration for it:
builder
.Register(c => new List<IPipeline>(c.Resolve<IEnumerable<IPipeline>>()))
.As<IList<IPipeline>>();
The second part isn't as easy. Autofac doesn't necessarily guarantee the order of the items in the list. If you need to order them, you'll need to put some sort of ordering metadata on them - attributes, properties, something that you can use to order the pipeline after the fact.
Alternatively, if your pipeline has "stages" or "events" where different components are applicable, look at the design of your pipeline and have a different pipeline interface per event. Within the event it shouldn't matter what order each item executes in. (This is similar to how event handlers in .NET work now. You'd want to mimic that behavior - different events for different stages in the overall lifecycle, but within each specific stage the order of execution of handlers doesn't matter.)
An example might look like:
public interface IFirstStage
{
void Execute();
}
public interface ISecondStage
{
void Execute();
}
public interface IThirdStage
{
void Execute();
}
public class PipelineExecutor
{
public IEnumerable<IFirstStage> FirstHandlers { get; private set; }
public IEnumerable<ISecondStage> SecondHandlers { get; private set; }
public IEnumerable<IThirdStage> ThirdHandlers { get; private set; }
public PipelineExecutor(
IEnumerable<IFirstStage> first,
IEnumerable<ISecondStage> second,
IEnumerable<IThirdStage> third)
{
this.FirstHandlers = first;
this.SecondHandlers = second;
this.ThirdHandlers = third;
}
public void ExecutePipeline()
{
this.ExecuteFirst();
this.ExecuteSecond();
this.ExecuteThird();
}
public void ExecuteFirst()
{
foreach(var handler in this.FirstHandlers)
{
handler.Execute();
}
}
// ExecuteSecond and ExecuteThird look just
// like ExecuteFirst, but with the appropriate
// set of handlers.
}
Then when you register your handlers it's simple:
var builder = new ContainerBuilder();
builder.RegisterType<SomeHandler>().As<IFirstStage>();
builder.RegisterType<OtherHandler>().As<IFirstStage>();
builder.RegisterType<AnotherHandler>().As<ISecondStage>();
// You can have any number of handlers for any stage in the pipeline.
// When you're done, make sure you register the executor, too:
builder.RegisterType<PipelineExecutor>();
And when you need to run the pipeline, resolve and run.
var executor = container.Resolve<PipelineExecutor>();
executor.ExecutePipeline();
This is just like event handlers but not using delegates. You have a fixed order of pipeline "events" or "stages" but the handlers inside each stage aren't guaranteed order.
If you need to modify the pipeline to have more stages, yes, you'll need to modify code. Just like if you had a new event you wanted to expose. However, to add, remove, or change handlers, you just modify your Autofac registrations.
I suggest you to use Metadata feature.
It gives you an advantage to define the order on registration stage.
Here is an example:
internal class Program
{
private static void Main(string[] args)
{
var builder = new ContainerBuilder();
var s1 = "First";
var s2 = "Second";
var s3 = "Third";
builder.RegisterInstance(s1).As<string>().WithMetadata<Order>(c => c.For(order => order.OrderNumber, 1));
builder.RegisterInstance(s2).As<string>().WithMetadata<Order>(c => c.For(order => order.OrderNumber, 2));
builder.RegisterInstance(s3).As<string>().WithMetadata<Order>(c => c.For(order => order.OrderNumber, 3));
using (var container = builder.Build())
{
var strings = container.Resolve<IEnumerable<Meta<string, Order>>>();
foreach (var s in strings.OrderBy(meta => meta.Metadata.OrderNumber))
{
Console.WriteLine(s.Value);
}
}
Console.ReadKey();
}
public class Order
{
public int OrderNumber { get; set; }
}
}

How do I find the output model type in a behavior?

With FubuMVC, I'm not sure what the best way is to determine the current action's output model type. I see different objects that I could get the current request's URL from. But that doesn't lead to a very good solution.
What's the easiest way to get the current action's output model type from the behavior?
If this isn't a good practice, what's a better way?
First, I'm assuming you've already got your settings object(s) set up in StructureMap and have the ISettingsProvider stuff already wired up.
The best, simplest thing to do would be just to pull the settings in the view, like this:
<%: Get<YourSettingsObject>().SomeSettingProperty %>
If you insist on having these be a property on your output model, then continue reading:
Let's say you had a settings object like this:
public class OutputModelSettings
{
public string FavoriteAnimalName { get; set; }
public string BestSimpsonsCharacter { get; set; }
}
Then you had an output model like this:
public class OutputModelWithSettings
{
public string SomeOtherProperty { get; set; }
public OutputModelSettings Settings { get; set; }
}
You'll need to do a few things:
Wire up StructureMap so that it will do setter injection for Settings objects (so it will automatically inject the OutputModelSettings into your output model's "Settings" property.
Set up a setter injection policy in your StructureMap initialization code (a Registry, Global ASAX, your Bootstrapper, etc -- wherever you set up your container).
x.SetAllProperties(s => s.Matching(p => p.Name.EndsWith("Settings")));
Create your behavior to call StructureMap's "BuildUp()" on the output model to trigger the setter injection. The behavior will be an open type (i.e. on the end) so that it can support any kind of output model
public class OutputModelSettingBehavior<TOutputModel> : BasicBehavior
where TOutputModel : class
{
private readonly IFubuRequest _request;
private readonly IContainer _container;
public OutputModelSettingBehavior(IFubuRequest request, IContainer container)
: base(PartialBehavior.Executes)
{
_request = request;
_container = container;
}
protected override DoNext performInvoke()
{
BindSettingsProperties();
return DoNext.Continue;
}
public void BindSettingsProperties()
{
var viewModel = _request.Find<TOutputModel>().First();
_container.BuildUp(viewModel);
}
}
Create a convention to wire up the behavior
public class OutputModelSettingBehaviorConfiguration : IConfigurationAction
{
public void Configure(BehaviorGraph graph)
{
graph.Actions()
.Where(x => x.HasOutput &&
x.OutputType().GetProperties()
.Any(p => p.Name.EndsWith("Settings")))
.Each(x => x.AddAfter(new Wrapper(
typeof (OutputModelSettingBehavior<>)
.MakeGenericType(x.OutputType()))));
}
}
Wire the convention into your FubuRegistry after the Routes section:
ApplyConvention<OutputModelSettingBehaviorConfiguration>();
In your view, use the new settings object:
<%: Model.Settings.BestSimpsonsCharacter %>
NOTE: I have committed this as a working sample in the FubuMVC.HelloWorld project in the Fubu source. See this commit: https://github.com/DarthFubuMVC/fubumvc/commit/2e7ea30391eac0053300ec0f6f63136503b16cca

NServiceBus Xml Serialization issue with messages that have an IEnumerable<T> property

I'm trying to send a message with an IEnumerable property, am i correct that the NServiceBus Xml serializer cannot support this ?
If i switch to using an array rather than IEnumerable it will work, if i use the binary serializer it also works
My message look like this
[Serializable]
public class Parent : IMessage
{
public string Identifier { get; private set; }
public IEnumerable<Child> Children { get; private set; }
public Parent(string identifier, IEnumerable<Child> children)
{
this.Identifier = identifier;
this.Children = children;
}
}
[Serializable]
public class Child
{
public string Identifier { get; private set; }
}
If the default Xml serializer cannot cater for this, is there any way to configure an alternative Xml serializer such as the BCL's DataContractSerializer ?
Thanks in advance
Pat
First, a note that XML serialization in NServiceBus is not the same thing as .NET XML Serialization. The .NET variant is all about being able to tailor the resultant XML with attributes to produce specific XML schemas, potentially for interoperability with other languages. The NServiceBus XML serializer is an extremely small subset of functionality tailored to transfer predefined message schemas to and from XML as efficiently as possible.
While the result of NServiceBus serialization is readable (which is really nice when inspecting error queues) it doesn't support all types or all formatting options. It does what it does and it does it pretty well.
That said, the problem with an IEnumerable is that it could be so many things. It could, in reality, just be an array, but it could just as easily be a complex Linq-to-SQL expression that will invoke a database query. In order to serialize the IEnumerable, you'd have to represent it as a collection (a list or array) anyway, so you have to enumerate the items. When exactly would you do that? What problems with transactions might that raise? That's why the performance-conscious NServiceBus XML serializer doesn't bother.
An NServiceBus message is just a contract to pass message data. I would suggest just using an array. It's easy enough to convert an IEnumerable to an array (with the ToArray() extension method) and back (with the AsEnumerable() extension method) so why is it important to have it as an IEnumerable?
To fully answer your question, it should be possible to swap out the serializer by writing your own class that implements IMessageSerializer and configuring the dependency injection framework to use it, but I have not tried this myself. It would be quite an undertaking, since every single endpoint would have to utilize this same serializer, and you'd also have to make modifications in order to use the Distributor, TimeoutManager, Gateway, etc.
Edit: Noticed this question was cross-posted on NSB group at http://tech.groups.yahoo.com/group/nservicebus/message/8838
is there any way to configure an alternative Xml serializer such as the BCL's DataContractSerializer ?
Yes, that is certainly possible. We use the DataContractSerializer for some of our services. To get this working, you need to implement the IMessageSerialzer interface, which does the work, then register that serializer with NServiceBus during the NServiceBus.Configure method chain.
Here's the code for the message serializer. It is pretty straightforward.
public class WcfMessageSerializer : IMessageSerializer
{
private readonly IList<Type> knownTypes = new List<Type>();
public IList<Type> MessageTypes
{
get { return knownTypes; }
set
{
knownTypes.Clear();
foreach (var type in value)
{
if (!type.IsInterface && typeof(IMessage).IsAssignableFrom(type)
&& !knownTypes.Contains(type))
{
knownTypes.Add(type);
}
}
}
}
public void Serialize(IMessage[] messages, Stream stream)
{
var xws = new XmlWriterSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (var xmlWriter = XmlWriter.Create(stream, xws))
{
var dcs = new DataContractSerializer(typeof(IMessage), knownTypes);
foreach (var message in messages)
{
dcs.WriteObject(xmlWriter, message);
}
}
}
public IMessage[] Deserialize(Stream stream)
{
var xrs = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (var xmlReader = XmlReader.Create(stream, xrs))
{
var dcs = new DataContractSerializer(typeof(IMessage), knownTypes);
var messages = new List<IMessage>();
while (false == xmlReader.EOF)
{
var message = (IMessage)dcs.ReadObject(xmlReader);
messages.Add(message);
}
return messages.ToArray();
}
}
}
In order to plug this in, you could use an extension method such as the following:
public static class ConfigureWcfSerializer
{
public static Configure WcfSerializer(this Configure config)
{
var messageTypes = Configure.TypesToScan
.Where(t => typeof(IMessage).IsAssignableFrom(t))
.ToList();
config.Configurer
.ConfigureComponent<WcfMessageSerializer>(ComponentCallModelEnum.Singleton)
.ConfigureProperty(ms => ms.MessageTypes, messageTypes);
return config;
}
}
This would be invoked when you configure NServiceBus like so:
NServiceBus.Configure
// Other configuration...
.WcfSerializer()
// Other configuration...
.CreateBus()
.Start();