Can someone help me convert this AutoFac registration to Windsor? - castle-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).

Related

Is possible with Castle Windsor Interceptor to intercept only a specific method in a class and ignore the other ones?

Dears,
I have an interface IJob that has a method called ExecuteAsync and i want to intercept this method only but my derived classes may have many methods and i found the interceptor intercept them also.
My question is,
Is this possible with Castle Windsor
and this is my registration
iocManager.IocContainer.Kernel.ComponentRegistered += (key, handler) =>
{
var implementationType = handler.ComponentModel.Implementation.GetTypeInfo();
if(ShouldIntercept(implementationType))
{
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(AuthenticateJobInterceptor)));
}
};
private static bool ShouldIntercept(Type type)
{
if (typeof(IJob).IsAssignableFrom(type))
{
return true;
}
return false;
}
Yes it is. There's a IProxyGenerationHook interface that you can implement to control what gets intercepted. The tutorial I wrote a decade ago still (for better or worse) seems to be the best resource about it.
There's a couple ways to set it up in Windsor.
Ideally, if possible, you'd do it during registration, in your IWindsorInstaller:
var yourHook = new YourHook();
container.Register(
Classes.FromThisAssembly()
.BasedOn<IJob>()
.LifestyleTransient()
.WithServiceBase()
.Configure(c =>
c.Interceptors<AuthenticateJobInterceptor>()
.Proxy.Hook(yourHook)));
Alternatively, if you want to keep your code similar to what it is now (I'd recommend wrapping in a ComponentModel construction contributor), you can do something like:
var options = handler.ComponentModel.ObtainProxyOptions();
options.Hook = yourHook; // InstanceReference(yourHook)

Casting JSON to complex types

I am trying to cast my http.get response to actual object -> in my specific case array of complex objects.
In a normal scenario, where you don't need any specific casting, you could do the following (simplified):
return this.httpClient.get(api, this._options_get)
.pipe(
map((response: any) => {
return response.value as NewProduct[];
})
);
As my need is to actually cast this to an object, I have created this static method which does that:
static toProduct(otherProduct: any): NewProduct {
let item = new NewProduct();
Object.keys(otherProduct).forEach(prop => {
if (typeof otherProduct[prop] === "object" && otherProduct[prop]) {
if (!item.hasOwnProperty(prop))
item[prop] = otherProduct[prop];
Object.assign(item[prop], otherProduct[prop]);
}
else
item[prop] = otherProduct[prop];
})
return item;
}
Under Object.assign I am taking already existing object which was initialized under first line and I am simply copying all the properties from the otherProduct to it. However I start to face problem when it comes to array of objects. Example (with simplified class):
export class Person {
name:string;
age:number;
addresses:Address[] = [];
}
export class Address {
street:string;
city:string;
fullAddress() : string { return this.street + this.city; }
}
As soon as I have this sort of array, I don't have any initial object in item. This means that there is no initial constructor of a class which results in simple Object. This is no error for JavaScript or TypeScript; however when I am trying to access internal method of a class (in our simplified case fullAddress(), I won't be able to.
The reason why I need that is that I am overriding toString() method on my sub-classes, which is necessary for MatTableDataSource when you use the filter method (which works with strings).
Is there a way how to retrieve elements from http.get() and properly map results to typed objects?
You're being too generic. You're creating objects of objects, not objects of Product with children of Addresses.
If you want to create a new product you're going to have to understand the relationship between the api's results and the data you want in the UI.
Because you're using classes and not interfaces and want to inherit the functions, the only way to get new Addresses into the new object is with the new keyword.
And you'll have to loop through. You're not going to find a shortcut for this. You're going to need to loop through the data and transform it. If your api is giving you an ApiPerson then you'll want to do something like this:
const addresses = apiPerson.addresses.map((apiAddress) => {
const address = new Address();
// map properties of apiAddress to address...
return address;
});
Now that you have the addresses, you can map the apiPerson to a new Person()'s properties and then set the newPerson.addresses = address.

Should I have a layer in between my controller and model in Node-Express app?

I have an Express application where I use Sequelize to interact with my MySQL database. The models just represent the database tables with corresponding fields, without containing any additional logic. I want my controllers to not be fat and for that I think I should have a layer in between which contains all of the logic and which uses the model to interact with the database.
Is this a good practice and if it is what should I call this layer and what exactly should it contain?
Thanks, in advance!
First of all, great question.
Second of all, this is how I would do it:
in your models, say you have a user model, users.js.
In that model for your user/db interface, after your
const User = module.exports = <sql declaration and model reference>;
you can create other module exports like this:
module.exports.getUserById = function(id, callback){
<Sequelize logic goes here>
};
And this is essentially middleware/controller for your model class for handling routines.
You might use this by importing your user model and then calling your exported module:
const User = require("../models/users")
and then when it's time to call your function:
User.getUserById(id, function(err, user) {
<some logic with regard to your user>
});
You can extend your sequelize models and instances with methods and hooks, your controllers would ideally only make a few calls to these methods.
For instance, you could add something like this to your User model:
instanceMethods: {
encryptPassword: function(plainPassword) {
if(!this.salt){
this.salt = randomString.generate(10);
}
var cipher = crypto.createCipher('aes-256-cbc', this.salt);
cipher.update(plainPassword, 'utf8', 'base64');
var encryptedPassword = cipher.final('base64')
return encryptedPassword;
},
decryptPassword: function(){
var decipher = crypto.createDecipher('aes-256-cbc', this.salt);
decipher.update(this.password, 'base64', 'utf8');
var decryptedPassword = decipher.final('utf8');
return decryptedPassword;
}
}
And maybe even add a pre-save hook to check if the user is new and then encrypt the password before saving, you could create authentication methods to call this from your model and not your controller, and so on...

How do I use Castle Windsor to create a RavenDB session with client version > 3.0.3660?

I am using Castle Windsor v3.4.0 to create a RavenDB document session instance but when I use a RavenDB client version later than 3.0.3660 I get this error when calling the Store method:
Castle.MicroKernel.ComponentNotFoundException: 'No component for supporting the service System.Net.Http.HttpMessageHandler was found'
Here is the smallest piece code I can come up with that reproduces the error:
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Raven.Client;
using Raven.Client.Document;
public class Program
{
public static void Main()
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component
.For<IDocumentStore>()
.ImplementedBy<DocumentStore>()
.DependsOn(new { Url = "http://localhost:8081", DefaultDatabase = "Test" })
.OnCreate(x => x.Initialize())
.LifeStyle.Singleton,
Component
.For<IDocumentSession>()
.UsingFactoryMethod(x => x.Resolve<IDocumentStore>().OpenSession())
.LifeStyle.Transient);
using (var documentSession = container.Resolve<IDocumentSession>())
{
documentSession.Store(new object());
documentSession.SaveChanges();
}
}
}
Here's what I believe is happening. A change was made to the RavenDB client after v3.0.3660 that changed how the HttpMessageHandler is created in the HttpJsonRequest class:
https://github.com/ravendb/ravendb/commit/740ad10d42d50b1eff0fc89d1a6894fd57578984
I believe that this change in combination with my use of the TypedFactoryFacility in my Windsor container is causing RavenDB to request an instance of HttpJsonRequestFactory and it's dependencies from Windsor rather than using it's own internal one.
How I can change my code to avoid this problem so that I can use a more recent version of the RavenDB client?
Given your MVCE, Windsor is set up to inject object's properties. So, when creating the DocumentStore, Castle is trying to find a value for the HttpMessageHandlerFactory property and is failing since nothing is configured for that particular type.
I was able to get your example to work (at least, it got to inserting the data into my non-existing server) by just filtering out that property:
container.Register(
Component.For<IDocumentStore>()
.ImplementedBy<DocumentStore>()
.DependsOn(new { Url = "http://localhost:8081", DefaultDatabase = "Test" })
.OnCreate(x => x.Initialize())
.PropertiesIgnore(p => p.Name == nameof(DocumentStore.HttpMessageHandlerFactory))
.LifeStyle.Singleton);
Alternatively, if you have a value for it, you could add it to the object passed to DependsOn().

How to automaticly register components using a Generic FactoryMethod with Castle Windsor

I´m trying to work out a problem with registering my configuration classes. I have the following in my Installer:
First I register my open generic factory
container.AddFacility<FactorySupportFacility>()
.Register(Component.For(typeof (IConfigurationProvider<>))
.ImplementedBy(typeof (AppSettingsConfigurationProvider<>)));
Then I´m trying to register all concrete impl. of IConfiguration and I need to use my registered impl. IConfigurationProvider to resolve them.
Problem is that my factory looks like this:
public class AppSettingsConfigurationProvider<TConfiguration>
where TConfiguration : class, IConfiguration, new()
{
public TConfiguration Build()
{
var config = new TConfiguration();
var properties = typeof(TConfiguration).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var p in properties)
{
if (!p.CanWrite)
continue;
if (p.GetSetMethod(false) == null)
continue;
var settingsKey = string.Format("{0}.{1}", p.ReflectedType.FullName, p.Name);
p.SetValue(config, Convert.ChangeType(ConfigurationManager.AppSettings[settingsKey], p.PropertyType), null);
}
return config;
}
}
So I need to set the generic type.
Is there a way to get away with this, so that I dont need to register each configuration component one by one like this:
container
.Register(
Component.For<DummyConfiguration>()
.ImplementedBy<DummyConfiguration>()
.UsingFactoryMethod(kernel => kernel.Resolve<IConfigurationProvider<DummyConfiguration>>().Build()));
I would prefer a more automatic way to register my configuration componets.
Would something similar to what Ben Hall described, using DictionaryAdapter work for you? Notice that if you're using Windsor 2.5 you already have DictionaryAdapter (it is part of Castle.Core.dll now).