I'm in the process of integrating a messaging broker within our existing application using MassTransit.
We had already implemented a kind of command handler that had generic implementations, like this:
public class MyCommandHandler: CommandHandlerBase<MyCommand>
Now it was relatively easy to make a generic Consumer that would do some boiler plating and would hand off the work to the ready command handler, requested from the DI container.
public class CommandConsumer<TCommand> : IConsumer<TCommand>
Which I could then easily register through the Microsoft DI thusly:
cfg.AddConsumer<CommandConsumer<MyCommand>>(x => some configuration...);
This all worked great, so I moved on to the next step, namely to extract the consumer registration(s) to a common helper method and this is where I'm a bit stumped. The method (currently) looks a bit like this
public static IServiceCollection ConfigureMassTransit(this IServiceCollection services, params Type[] consumerTypes)
{
return
services.AddMassTransit(cfg =>
{
foreach (var consumerType in consumerTypes)
{
cfg.AddConsumer(consumerType);
}
// or cfg.AddConsumers(consumerTypes);
cfg.AddBus(context => Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host("localhost", "/",
h =>
{
h.Username("guest");
h.Password("guest");
});
config.ConfigureEndpoints(context);
}));
});
}
which would be called as services.ConfigureMassTransit(typeof(CommandConsumer<MyCommand>));
This again works, but what I can't figure out is how to add the additional configuration to the registration; the overload that takes an Action is only available when using the generic signature, which you can't use directly when you only have the Type available. I tried adding a marker class CommandConsumer: IConsumer to the CommandConsumer<TCommand> and making CommandConsumerDefinition : ConsumerDefinition<CommandConsumer>, and changing the above to cfg.AddConsumer(consumerType, typeof(CommandConsumerDefinition));, but that doesn't work as the ConfigureConsumer override is never hit.
How am I supposed to add additional configuration to a Consumer for which I don't know the type at compile time?
Chris' answer put me on the path to a working solution. Making the CommandConsumerDefinition generic allowed me to use reflection to construct both types in the same way at runtime. This allowed MassTransit to wire up the configuration in the expected manner.
In the end, I also used a "marker" attribute that would hold the type of the command contract, so they could be discovered rather than having to be entered as a parameter at startup.
public static IServiceCollectionConfigurator ConfigureMassTransitConsumers(this IServiceCollectionConfigurator serviceConfigurator, Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var attributes = type.GetCustomAttributes(typeof(RegisterCommandConsumerAttribute), false);
if (attributes.Length <= 0) continue;
foreach (var attribute in attributes)
{
if (attribute is RegisterCommandConsumerAttribute registerCommandConsumerAttribute)
{
Type consumerType = typeof(CommandConsumer<>).MakeGenericType(registerCommandConsumerAttribute.CommandType);
Type consumerDefinitionType = typeof(CommandConsumerDefinition<>).MakeGenericType(registerCommandConsumerAttribute.CommandType);
serviceConfigurator.AddConsumer(consumerType, consumerDefinitionType);
}
}
}
return serviceConfigurator;
}
Because of the automatic discovery, we were already in the realm of reflection, so this seemed like an acceptable solution. This way we can have generic consumers and definitions without having to add a new class for every command contract we have.
Related
In Dot Net Core convention for configuring services is as follows.
Method that Requires Action<> As Parameter
public static IServiceCollection AddAsResourceServer(this IServiceCollection services, Action<AuthMiddlewareOptions> action = null)
{
if(action != null)
{
AuthMiddlewareOptions authOptions = new AuthMiddlewareOptions();
action.Invoke(authOptions);
}
...
return services
}
This allows us to configure our service in startup as follows.
Startup ConfigureServices Method
services.AddAsResourceServer((a) =>
{
a.Audience = "Long Audience";
a.Issuer = "provider.com/oauthprovider";
});
This is really cool! I like this pattern. It works very well for allowing anyone to overwrite base configuration options.
However this seems very cumbersome when you already have an instance of the object. How would I create an instance of an object, convert it to an action, and pass that to the .AddAsResourceServer method?
I have tried:
Action<AuthMiddleWareOptions> auth = new Action<AuthMiddleWareOptions>()
/// The above way still requires a "target" in constructor so it doesnt work.
///so i still need to pass each
///property into it.
My consumers of the service may have just created an instance of AuthMiddleWareOptions and populated through appsettings!
They may have done something like this.
AuthMiddlewareOptions myoptions = new AuthMddlewareOptions();
Configuration.Bind("AuthMiddlewareOptions", myoptions);
Now that they have 'myoptions'. Is there a quick way to use that with the Action Parameter. Maybe like this?
AuthMiddlewareOptions myoptions = new AuthMddlewareOptions();
Configuration.Bind("AuthMiddlewareOptions", myoptions);
services.AddAsResourceServer((a) => SpecialThing(myoptions));
You can simply bind your configuration to the options instance provided by the method.
services.AddAsResourceServer(options =>
{
Configuration.Bind("AuthMiddlewareOptions", options);
});
You may register multiple configuration delegates for a single option class:
services.Configure<AuthMiddlewareOptions>(Configuration.GetSection("AuthMiddlewareOptions"));
services.AddAsResourceServer(options => SpecialThing(options));
And AddAsResourceServer should call services.Configure, as well:
public static IServiceCollection AddAsResourceServer(this IServiceCollection services, Action<AuthMiddlewareOptions> action = null)
{
if (action != null)
services.Configure(action);
// ...
return services;
}
Having this setup, AuthMiddlewareOptions is populated from the configuration section "AuthMiddlewareOptions", then the customization delegate (SpecialThing) has a chance to change the options filled at the previous step.
It's important that the order matters! If you swap the two lines, SpecialThing's changes will be overwritten by the values coming from the configuration.
You need to do it the other way arround.
I use for example the AddAuthorization since I do not find your method:
services.AddAuthorization(c =>
{
c.DefaultPolicy = null;
});
This can be configured using the following method:
public static void Config(AuthorizationOptions configure, IConfiguration configuration)
{
configure.DefaultPolicy = null;
//Configuration.Bind("AuthMiddlewareOptions", myoptions);
}
That way you can do:
services.AddAuthorization(c => Config(c, Configuration));
The problem is, that in these Action<T> methods you get the object T, which is already instantiated. So you need to copy the custom created object to it. With above we change the API, so it is configured on the correct object.
I tried many patterns, it wasn't until I hovered over a in the method to realize that it wasnt of type Action<AuthMiddlewareOptions>, but it was in fact AuthMiddleWareOptions. so this very simple assignment worked!
services.AddAsResourceServer(a => a = myoptions);
Quick answer to your question "Passing an Instance of an Object to a Method that takes an Action<> as Parameter" is "no, you can't"
But as a workaround, you could simply make a deepclone of your object, to swap the properties of the "inner" options.
Something like this :
static class Copy
{
public static void ObjectToAnother<T>(T source, T target)
{
foreach (var sourceProp in source.GetType().GetProperties())
{
var targetProp = target.GetType().GetProperty(sourceProp.Name);
targetProp.SetValue(target, sourceProp.GetValue(source, null), null);
}
}
}
And you could use that like this :
var obj = new Obj();
Foo.Bar((a) =>
{
Copy.ObjectToAnother(a, obj);
});
It should work
If I put Lazy in constructor of my object, and X is not registered in container I got dependency resolution exception.
Why am I getting this exception? I dislike it, because I cannot choose component at runtime. Example usecase:
class Controller
{
public Controller(Lazy<A> a, Lazy<B> b) { /* (...) */ }
Lazy<A> a;
Lazy<B> b;
public IActionResult Get(){
if(someConfig)
return Json(a.Value.Execute());
else
return Json(b.Value.Execute());
}
}
To do so I need to register both components A an B. My program fails even if B is never used. I would like to have B be optional, and still managed by autofac.
This is even bigger issue if I have list of components, and want only one to be used. For example:
class Controller
{
Controller(IEnumerable<Component> components) { /* (...) */ }
IActionResult Get()
{
return components.First(n => n.Name == configuredComponent).Execute();
}
}
I am no longer getting exception is something is not registered, however still everything is constructed. Also it would be awkward to use.
If you add a reference to a Lazy<T> component, Autofac has to know (basically) how to create the internal function that will run should you want to resolve it, even if you don't resolve it.
Basically, it needs to be able to create this in memory:
var lazy = new Lazy<T>(() => scope.Resolve<T>());
Autofac requires all of the things you want to resolve to be registered. It doesn't let you register things on the fly - it must be explicit. So the thing you're trying to do won't work (as you saw).
Instead, use a single interface and two different implementations of that interface. Change the registration based on your configuration value.
var builder = new ContainerBuilder();
if(someConfig)
{
builder.RegisterType<A>().As<IService>();
}
else
{
builder.RegisterType<B>().As<IService>();
}
Then in your controller, inject the interface rather than the concrete class.
public MyController(Lazy<IService> service)
There are also other options you could do, like use metadata for components or keyed services. For example, you could add some metadata based on your configuration and resolve using that.
builder.RegisterType<A>()
.As<IService>()
.WithMetadata("Name", "a");
builder.RegisterType<B>()
.As<IService>()
.WithMetadata("Name", "b");
In the controller, you'd get a dictionary of them:
public MyController(IEnumerable<Meta<IService>> services)
{
var service = services.First(s => s.Metadata["Name"].Equals(someConfig);
}
That's a very short example, but the docs show a lot more.
In any case, the interface is really going to be your key to success. If you're just using concrete classes, they'll have to be registered whether you use them or not.
I want to configure MassTransit at one point in my code (using WebActivator) and configure the message handlers in another (a Ninject module). Is there a way I can achieve this? The documentation here shows how to perform what I need in one step, but to do anyhting else, it looks like I need to get an instance of a ServiceBusConfigurator, which doesn't seem to be available from the preexisting IServiceBus
Configuration and Creation of the IServiceBus cannot be separated.
That means, the only option you have is to gather the configuration information some more time before creating the bus.
As the doc you linked states, the meta data information made available by ninject is not sufficient to create the subscriptions. This basically means that you've got to create your own metadata model. Let's make an example, which can be used with single registrations but also with convention based registrations:
Hint: You should treat the following code snippets as psuedo code as i've written them from memory. It's highly likely that it won't compile.
Metadata Model
public class SubscriptionMetadata
{
public SubscriptionMetadata(Type consumer)
{
if(!typeof(IConsumer).IsAssignableFrom(consumer))
{
string message = string.Format(
"{0} does not implement {1}",
typeof(IConsumer).Name,
consumer.Name);
throw new ArgumentOutOfRangeException("consumer", message);
}
this.ConsumerType = consumer;
}
public Type ConsumerType { get; private set; }
}
Registration of Metadata
Now this can be used like this in a Ninject module:
Bind<SubscriptionMetadata>()
.ToConstant(new SubscriptionMetadata(typeof(FooConsumer));
If you're going to use it a lot i'd recommend writing an extension method:
public static class SubscriptionBindingExtensions
{
public static void BindConsumer<T>(this IBindingRoot bindingRoot)
where T : IConsumer
{
Bind<SubscriptionMetadata>()
.ToConstant(new SubscriptionMetadata(typeof(T));
}
}
and usage (#Module):
BindConsumer<FooConsumer>();
IServiceBus creation
Now you would adapt the IServiceBus creation as follows:
var kernel = new StandardKernel();
// 2nd Step left out: load all IModule`s ..
var bus = ServiceBusFactory.New(sbc =>
{
//other configuration options
foreach(var metadata in kernel.GetAll<SubscriptionMetadata>())
{
sbc.Subscribe(subs =>
{
subs.Consumer(metadata.ConsumerType, kernel)
});
}
});
Convention based binding of Consumers
It can also be used in conjunction with conventions by leveraging the IBindingCreator interface. If you wish, i can post an example.
I've been struggling to find anything online, so I thought I'd see if anyone else knows how to sort this little issue I'm having.
I've got a scenario where I want to create a proxy object so that various other interfaces can be added to the same object. So far, I've not had any issues with this. One of my other requirements is to be able to set an attribute on the proxy-generated class.
I've been able to do this successfully using Castle DynmaicProxy manually, using something along the lines of:
var serviceOptions = new ProxyGenerationOptions();
// Create MyAttribute
var args = new object[] { "SomeName" };
var constructorTypes = new[] { typeof(String) };
var constructorInfo = typeof(MyAttribute).GetConstructor(constructorTypes);
var attributeBuilder = new CustomAttributeBuilder(constructorInfo, args);
serviceOptions.AdditionalAttributes.Add(attributeBuilder);
However, I'm using windsor to resolve my dependencies through injection. Windsor does provide some proxy options, such as:
configurer.Proxy.AdditionalInterfaces(interfaces);
configurer.Proxy.MixIns(r => r.Component(type));
But it does not seem to offer options for custom attributes. Does anyone know how this can be achieved? Many thanks.
In the end, I found an alternative solution from taking Phil's idea and from scanning the Castle source.
I created a custom ProxyFactory class that extends the DefaultProxyFactory from the Castle.Windsor assembly. The only method I implemented was the CustomizeOptions method, which by default, is empty.
From here, I hook into the ExtendedProperties of ComponentModel to get a collection of CustomAttributeBuilder instances I add when registering my components.
Full code below:
internal const string PROXY_ATTRIBUTES_PROPERTY_KEY = "custom.proxy.attributes";
protected override void CustomizeOptions(ProxyGenerationOptions options, IKernel kernel, ComponentModel model, object[] arguments)
{
if (model.ExtendedProperties.Contains(PROXY_ATTRIBUTES_PROPERTY_KEY))
{
var proxyAttributes = (IEnumerable<CustomAttributeBuilder>)model.ExtendedProperties[PROXY_ATTRIBUTES_PROPERTY_KEY];
foreach (var attribute in proxyAttributes)
{
options.AdditionalAttributes.Add(attribute);
}
}
base.CustomizeOptions(options, kernel, model, arguments);
}
configurer.ExtendedProperties(new Property(CustomProxyFactory.PROXY_ATTRIBUTES_PROPERTY_KEY, configurator.ProxySettings.CustomAttributes));
The standard ProxyGroup provides access to a subset of the proxy generation options. However, it is relatively straight forward to create your own descriptor to modify other options and add it to the component registration. The trick is to use an extension method to retrieve the proxy options used by the built-in ProxyGroup registration helper.
public class ProxyCustomAttributeBuilderDescriptor : IComponentModelDescriptor
{
public void BuildComponentModel(IKernel kernel, ComponentModel model)
{
var options = model.ObtainProxyOptions();
// ... do whatever you need to customise the proxy generation options
}
public void ConfigureComponentModel(IKernel kernel, ComponentModel model)
{
}
}
Then when you register your component simply add this descriptor:
configurer.AddDescriptor(new ProxyCustomAttributeBuilderDescriptor());
I'm using a third-party library that has a setup structure like this:
IEngine engine = /* singleton provided elsewhere */
var server = new FooServer();
server.AddService("Data1", () => new Data1(engine));
server.AddService("Data2", () => new Data2(engine));
server.Start();
...
server.Dispose();
(The lambda is essentially a factory method; it will internally invoke that whenever it wants a new instance for its own purposes.)
Except that a further complication is that instead of adding the services directly, I'm using reflection to find and register them, so that they just need to be defined to work instead of needing to be explicitly listed out. Originally I wanted to do this completely self-contained, but constructing generic lambda methods based on reflected types just seemed too complicated, so for the moment I've settled with a Register method provided by each type:
class Data1 : DataProvider
{
public static void Register(FooServer server, IEngine engine)
{
server.AddService("Data1", () => new Data1(engine));
}
... (constructor, Dispose, other stuff)
}
var server = new FooServer();
foreach (var type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
var method = type.GetMethod("Register", new[] { typeof(FooServer), typeof(IEngine) });
if (method != null)
{
method.Invoke(null, new object[] { server, engine });
}
// a more ideal approach would be to construct the needed lambda and call
// AddService directly instead of using Register, but my brain fails me.
}
server.Start();
...
server.Dispose();
Needless to say, this is a bit ugly and I'm sure there's a better way to do it. One other thing is that I'm already using Castle Windsor to create the IEngine and a few other things that use it, and I was wondering how to better integrate with that. (Currently I'm just Resolveing the engine at the point where this code needs it -- it's a singleton so lifetimes aren't thorny.)
What I'd really love is a way to use method parameter or constructor injection so that each DataProvider could have a different set of parameters based on their actual dependencies (instead of the union of all dependencies), just like you'd do when everything was under Windsor's control. But again, I'm not sure where to even start. I haven't really used Windsor much beyond the basics.
Note that FooServer, DataProvider and the AddService<T>(string name, Func<T> factory) where T: DataProvider method are in external code and I can't change them. The rest (including the engine) is my code. And again note that I do not create the Data1 instances in my code at all, just a factory lambda that tells the external server how to create them when it wants one.
Following qujck's answer with a few necessary edits resulted in the following code, for posterity:
var container = ...;
var server = new FooServer();
foreach (var type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
var t = type; // necessary due to the lambda capturing
container.Register(Component.For(t).LifestyleTransient());
server.AddService(t.Name, () => {
var service = (DataProvider) container.Resolve(t);
service.Closed += (s, e) => container.Release(service);
return service;
});
}
server.Start();
...
server.Dispose();
This behaves as desired, though I'm still interested in methods to improve it further. (I was curious if there was some way to use Castle's own Classes.FromAssembly... etc syntax to tidy up the discovery and registration of the services, but haven't had much luck working that out.)
You could define lambda's that resolve from the container. This offers the benefits of managing all of your services and their related lifetimes in one place (the container).
You would need some way of establishing the name of each registration - in the example I have registered each service as the name of the type:
[Fact]
public void Configure1()
{
IWindsorContainer container = new WindsorContainer();
var server = new MockFooServer();
container.Register(Component.For<IEngine>().ImplementedBy<Engine>());
foreach (Type type in Utils.GetConcreteTypesWithBase<DataProvider>())
{
container.Register(Component.For(type));
server.AddService(type.Name, () => container.Resolve(type) as DataProvider);
}
var service1 = server.services[typeof(Service1).Name]();
Assert.IsType<Service1>(service1);
}
With a Mock FooServer for the test:
public class MockFooServer
{
public Dictionary<string, Func<DataProvider>> services =
new Dictionary<string, Func<DataProvider>>();
public void AddService<T>(string key, Func<T> factory) where T : DataProvider
{
this.services.Add(key, factory as Func<DataProvider>);
}
}