Register type factories with convention - castle-windsor

Can you register type factories by convention?
got this far and can't find a way to say .AsFactory()
container.AddFacility<TypedFactoryFacility>();
container.Register(Classes
.FromThisAssembly()
.InNamespace("Core.Factories", true)
);
This may not be doable but would prefer to not have register every factory by hand.
Thanks in advance

You can call Configure when using convention configuration; this lets you pass a lambda that is then called for each component matching the convention: just call AsFactory in the lambda
container.Register(
Classes
.FromThisAssembly()
.InNamespace("Core.Factories", true)
.Configure(c => c.AsFactory())
);

Related

How to serialize an abstract class in Flutter?

I have an abstract model class that has an abstract function that determines how a widget switches state and I would like to keep it.
I am currently trying to implement https://pub.dev/packages/json_serializable but it wants me to switch the field type to a dynamic type instead of my abstract one. You cant #JsonSerializeable abstract classes and when I switch to a dynamic type the print reads as Instance of 'class' even when the concrete implementations have a valid toJson method.
Should I just throw out OOP principle implementation or is there an alternative solution here?
<String, dynamic>{
'command': instance.command,
'questions': instance.questions.map((e) {return (e as Question).toJson();}).toList(),
};
Solved by writing my own serializer for my abstract class

CakePHP static name of controller

I'm probably missing something really obvious here, but is there a function in CakePHP (I'm on 3.8) that returns the name of a controller without creating an instance of the class?
An instanced controller can call this function:
echo $this->name;
But what I'd like to be able to do, is avoid typing the controller name as a string in, say, an HTML->link(); ie a static call something like:
echo $this->Html->link(
'Dashboard',
['controller' => DashboardsController::name, 'action' => 'index']
);
The reason is that I'm refactoring a couple of controllers and am having to find and replace all of those strings by hand. I come from a .Net background and CakePHP is pretty new to me, so if there's a better (more cakeish) way to carry out the refactoring than the question I'm asking, then I'd be really glad to hear it.
Nothing in the documents is leaping out at me, but I've a feeling there should be a simple answer.
The namespace of a class can be retrieved using ::class property. Checkout the following example:
DashboardsController::class // Cake/Controllers/DashboardController
The name without the namespace can be retrieved with ReflectionClass:
$function = new \ReflectionClass(DashboardsController::class);
var_dump($function->inNamespace());
var_dump($function->getShortName());
Shortname can be used to get the class without namespace:
namespace App;
class Test {
public static function name(){
$function = new \ReflectionClass(self::class);
return $function->getShortName();
}
}
var_dump(Test::name());
Checkout the docs: https://www.php.net/manual/en/language.oop5.constants.php#example-186
Reflection: https://www.php.net/manual/en/reflectionclass.getname.php

Interceptor not working, how to debug them

I'm trying to make Castle Windsor use the Interceptor I specify.
Here is my code:
container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(Castle.MicroKernel.Registration
.Types
.FromThisAssembly()
.BasedOn<IInterceptor>()
.Configure(x=>x.LifestyleTransient()));
container.Register(Castle.MicroKernel.Registration
.Types
.FromAssemblyInThisApplication()
.BasedOn<IImporter>()
.Configure(x => x.Interceptors<LoggingInterceptor>().LifeStyle.Is(LifestyleType.Transient)));
container.Register(Component
.For<IImporterFactory>()
.AsFactory(c => c.SelectedWith(new ImporterFactoryComponentSelector()))
.LifeStyle.Transient);
After setting up Castle Windsor, I get the IImporter implementation that I need with:
IImporterFactory importerFactory = container.Resolve<IImporterFactory>();
var test = importerFactory.Create(FileType.M3Availability);
test.ImportFile(fileName);
I'm expecting for the interceptor to be called before test.ImportFile(str) is executed but it isn't
Am I doing something wrong during the components registration?
Viewing the "container" object I can see that all my object have the right interceptor (see pic)
Am I doing something wrong during the components registration?
How can I debug this?
Windsor can only intercept virtual methods and interfaces. In your instance you are trying to resolve concrete types and windsor can't decorate them.
The solution is to register IImporter with a interface and name each instance the name of the concrete implementation.
Update
WithServiceAllInterfaces should register all interfaces instead of the class.
.Named(c.Implementation.Name) should register the interface with the name of the concrete type so you can use the name of the concrete type in your selector.
What you need to change in your registration:
container.Register(Castle.MicroKernel.Registration
.Classes
.FromAssemblyInThisApplication()
.BasedOn<IImporter>()
.WithServiceAllInterfaces()
.Configure(c =>
c.Interceptors<LoggingInterceptor>()
.LifeStyle.Is(LifestyleType.Transient).Named(c.Implementation.Name)));
I tried this on your code and this works.

Can someone help me convert this AutoFac registration to Windsor?

containerBuilder
.Register<IGraphClient>(context =>
{
var graphClient = new GraphClient(new Uri("http://localhost:9999/db/data"));
graphClient.Connect(); // Particularly this line
return graphClient;
})
.SingleInstance();
While I can figure out how to register interfaces to concrete classes, this particular class needs to be a single instance (I'm pretty sure this is LifeStyle.Singleton) and also call the graphClient.Connect() method. That's the main part I'm stuck on.
Based on JeffN825's answer I did this:
container.Register(
Component.For(
typeof (IGraphClient))
.ImplementedBy(typeof (GraphClient))
.LifeStyle.Singleton.UsingFactoryMethod(() =>
{
var graphClient = new GraphClient(new Uri("http://localhost:7474/db/data"));
graphClient.Connect();
return graphClient;
}));
You can use the ComponentRegistration<T>.UsingFactoryMethod<T> method which takes a delegate (Func) if you want to control the instance creation yourself (which would also give you the chance to call Connect).

Castle Windsor: Using convention registration along with specific implementations

Assume we have IFoo implemented by Foo and IBar implemented by FirstBar and SecondBar.
Using this convention registration:
container.Register(
AllTypes.FromThisAssembly().Pick()
.WithService.DefaultInterface())
We'll have three entries in the container:
IFoo = Foo
IBar = FirstBar
IBar = SecondBar
Now, how can we tweak this registration to be able to tell the container that for IBar we want SecondBar registered only? Sort of:
container.Register(
AllTypes.FromThisAssembly().Pick()
.WithService.DefaultInterface()
.For<IBar>().Select<SecondBar>())
Use case: we have lots of services in our app all registered by conventions. However, some of the service interfaces have two or more implementations (e.g. real implementation, fake implementation and test implementation). Convention registration will register them all under the same interface and while resolving the interface we'll get the first implementation (in nondeterministic order). We want to be able to select one specific implementation for those services while registering. How can we do that?
Tighten your convention. It is obviously to wide.
container.Register(
AllTypes.FromThisAssembly()
.Where(t => t.Namespace != "Acme.Tests")
.WithService.DefaultInterface())
This is what made the work done:
container.Register(
AllTypes.FromThisAssembly().Pick()
.WithService.DefaultInterface())
.ConfigureFor<IBar>(c =>
c.If((k, m) => m.Implementation == typeof(SecondBar)));
This effectively registers only SecondBar impl for IBar service. This way, if there is more than one implementation for given service, we can tell the conventional scanner which impl we want.
We went ahead and created nice little extension methods for this purpose:
public static BasedOnDescriptor Select<TService, TImpl>(this BasedOnDescriptor desc)
{
return desc.ConfigureFor<TService>(c => c.If((k, m) => m.Implementation == typeof(TImpl)));
}
public static BasedOnDescriptor Ignore<TService>(this BasedOnDescriptor desc)
{
return desc.ConfigureFor<TService>(c => c.If((k, m) => false));
}
We can now use it like this:
container.Register(
AllTypes.FromThisAssembly().Pick()
.WithService.DefaultInterface())
.Select<IBar, SecondBar>()
.Ignore<ISomeService>()
All in all this works nicely. I believe those two methods could be in the Castle.Windsor proper. #Krzysztof Koźmic: where do I submit a patch? :)