Interceptor not working, how to debug them - castle-windsor

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.

Related

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().

Registering a WcfClient in the container when the URI not yet known

At the time I am registering a new WCF endpoint I do not know what the URI is...
public void Install(IWindsorContainer container, IConfigurationStore store)
{
var defaultClientModel = new DefaultClientModel
{
Endpoint = WcfEndpoint
.ForContract<IMyService>()
.BoundTo(new WSHttpBinding(SecurityMode.None))
.At( URI??? )
};
container.Register(WcfClient.ForChannels(defaultClientModel));
}
Is there some way I can retrieve the URI from the container at the time the IMyService instance is requested (this is when it is known)?
Is there a factory method/dynamic parameter sort of thing that could be used?
It looks like you're able to do so using the following syntax in Windsor 3.1:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IMyService>()
.AsWcfClient()
.DependsOn((k, d) =>
d["EndPoint"] = WcfEndpoint.BoundTo(new WSHttpBinding(SecurityMode.None)).At( URI??? )));
}
Windsor will attempt to resolve the endpoint using the given dynamic resolution delegate at the point when an IMyService is first resolved.
I think you want to use the UsingFactoryMethod to create your services.
I would register a custom UriResolver with the container
Resolve an instance of the said UriResolver in the factory method (which doesn't get called until the service needs to be resolved)
Create the service in the factory method
Search through the Castle Windsor doco and swear a lot.
The following links may be of use
ravendb, castle IoC ,Wcf facility - doc session liefstyle
Using Castle Windsor WcfFacility to create client endpoints
http://www.mail-archive.com/castle-project-users#googlegroups.com/msg09012.html (this one looks to have similar code to what you need)
https://stackoverflow.com/questions/10250077/problems-using-castle-windsor-factory-method
Passing parameters to UsingFactoryMethod in Castle Windsor
Castle Windsor: UsingFactoryMethod can't instantiate with a weird error
http://docs.castleproject.org/Default.aspx?Page=Typed-Factory-Facility-interface-based-factories&NS=Windsor&AspxAutoDetectCookieSupport=1

Using Castle Windsor to inject dependencies when the classes inherit from the same base class

I have two classes that inherit from the same base class.
public class UserDetailValidator : BaseValidator<UserDetail>{
public UserDetailValidator(IRepository<Person, Guid> userRepository, AddressValidator addressValidator)
{
RuleFor(x => x.FirstName).Length(1, 10);
}
}
public class AddressValidator : BaseValidator<Address>
When I try and get the UserDetailValidator from WindsorServiceLocator I get the error
Missing dependency.
Component UserDetailValidator has a dependency on AddressValidator, which could not be resolved.
Make sure the dependency is correctly registered in the container as a service, or provided as inline argument. I'm using the following in my ValidationInstaller.
container.Register(
AllTypes.FromAssemblyNamed("Validation")
.IncludeNonPublicTypes()
.BasedOn(typeof(IValidator<>))
.WithService.AllInterfaces()
.LifestyleTransient()
The IRepository component is being injected with no problems. It's only the AddressValidator that does not come in. What am I not doing properly? I'm using Castle Windsor 3.0
I changed the type of the Argument being passed
to UserDetailValidator from AddressValidator to IValidator.
I still have to use AllInterfaces (at least in my testing so far) but
it now works.

Castle Windsor resolve named instance and unnamed instance incorrect

I have following testing code trying to get one instance for generic and other for special purpose.
[TestMethod]
public void Test_Name_And_Named_Instances()
{
//MyClass implemented IMyClass
MyClass genericInstance = new MyClass("generic");
MyClass specialInstance = new MyClass("special");
IWindsorContainer container = new WindsorContainer();
container.Register(Component.For(IMyClass).Instance(genericInstance));
container.Register(Component.For(IMyClass).Instance(specialInstance).Named("special"));
IMyClass genericOne = container.Resolve<IMyClass>();
IMyClass specialOne = container.Resolve<IMyClass>("special");
Assert.AreSame(genericOne, genericInstance); //true
Assert.AreNotSame(genericOne, specialOne); //false
}
I expect to get two different instances, but the result is both genericOne and specialOne points to same objec genericInstance.
Any idea?
This doesn't compile:
container.Register(Component.For(IMyClass).Instance(genericInstance));
Should be:
container.Register(Component.For<IMyClass>().Instance(genericInstance));
Other than that, the test passes for me (Windsor 2.5.2)
EDIT:
If you flip the registrations, the test fails. This is by design. When you resolve without an explicit name, you're saying "give me the default component for this service", which in Windsor is the first registered component for that service type, by default.
If you need different components under the same service type, assign explicit names to all of them when registering and resolving.

How to register a uri dependency to return HttpContext.Current.Request.Url using Castle Windsor?

I'm new to Castle Windsor, so go easy!!
I am developing an MVC web app and one of my controllers has a dependency on knowing the current request Url.
So in my Application_Start I initialise a WindsorContainer (container below), register my controllers and then try the following...
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<Uri>().LifeStyle.PerWebRequest.UsingFactoryMethod(() => HttpContext.Current.Request.Url));
However when I run up my web app I get an exception that my controller...
is waiting for the following dependencies:
Keys (components with specific keys)
- uri which was not registered.
The controller it is trying to instantiate has the following signature:
public MyController(Uri uri)
For some reason it is not running my factory method?
However if I change the controller signature to:
public MyController(HttpContext httpContext)
and change the registration to:
container.Register(Component.For<HttpContext>().LifeStyle.PerWebRequest.UsingFactoryMethod(() => HttpContext.Current));
Then everything works a treat!!
What am I missing when trying to register a Uri type? Its seems exactly the same concept to me? I must be missing something!?
Updated:
I have done some more debugging and have registered both the Uri and the HttpContext using the factory methods shown above. I have added both types as parameters on my Controller constructor.
So to clarify I have a both Uri and HttpContext types registered and both using the FactoryMethods to return the relevant types from the current HttpContext at runtime. I also have registered my controller that has a dependency on these types.
I have then added a breakpoint after I have registration and have taken a look at the GraphNodes on the kernal as it looks like it stores all the dependencies. Here it is:
[0]: {EveryPage.Web.Controllers.BaseController} / {EveryPage.Web.Controllers.BaseController}
[1]: {EveryPage.Web.Controllers.WebpagesController} / {EveryPage.Web.Controllers.WebpagesController}
[2]: {System.Web.HttpContext} / {System.Web.HttpContext}
[3]: {Castle.MicroKernel.Registration.GenericFactory1[System.Web.HttpContext]} / {Castle.MicroKernel.Registration.GenericFactory1[System.Web.HttpContext]}
[4]: {System.Uri} / {System.Uri}
[5]: {Castle.MicroKernel.Registration.GenericFactory1[System.Uri]} / {Castle.MicroKernel.Registration.GenericFactory1[System.Uri]}
It looks as though it has registered my Controller and both the types, plus it has the Factories. Cool.
Now if I drill into the WebpagesController and take a look at its dependencies it only has 1 registered:
[0]: {System.Web.HttpContext} / {System.Web.HttpContext}
Now shouldn't this have 2 registered dependencies as it takes a HttpContext and Uri on its constructor??
Any ideas? Am I barking up the wrong tree?
UPDATE3:
There's new extension point in Windsor trunk now that you can use easily for that.
UPDATE2:
Turns out that I was right from the start (well kind of). Uri is a class, but Windsor treats it as a primitive. There are still at least two quick solutions to this:
Wrap the Uri in some kind of IHasUri or something and take dependency on that interface in your controller
public class FooController
{
public IHasUri CurrentUri { get; set; }
public void SomeAction()
{
var currentUri = CurrentUri.GetCurrentUri();
// do something with the uri
}
}
Tell the Windsor you don't want it to treat Uris like some primitive (but like a lady).
You need a IContributeComponentModelConstruction implementation for that:
public class UriIsAServiceNotAParameter:IContributeComponentModelConstruction
{
public void ProcessModel(IKernel kernel, ComponentModel model)
{
if (model.Service != typeof(UsesUri)) // your controller type here
return;
foreach (var constructor in model.Constructors)
{
foreach (var dependency in constructor.Dependencies)
{
if(dependency.TargetType ==typeof(Uri))
{
dependency.DependencyType = DependencyType.Service;
}
}
}
}
}
and add it to the container:
container.Kernel.ComponentModelBuilder.AddContributor(new UriIsAServiceNotAParameter());
There's also the most correct way of doing this, which means telling Windsor not to register Uris as primitives in the first place, rather than fixing this afterwards, but this would require reaching into the deepest guts of the kernel, and the result is far more code (though a straightforwad one) than the workarounds outlined above.