From 9b40b92eecaf996371bae4b8862f35f3eef5db3b Mon Sep 17 00:00:00 2001 From: cneuwirt Date: Sat, 15 Mar 2008 03:42:05 +0000 Subject: [PATCH] Add unit tests to demonstrate resolving client dependencies automatically and supplying overrides when resolving clients. git-svn-id: https://svn.castleproject.org/svn/castle/trunk@4895 73e77b4c-caa6-f847-a29a-24ab75ae54b6 --- ...e.Facilities.WcfIntegration.Tests-vs2008.csproj | 1 + .../ClassNeedingService.cs | 31 +++++++++ .../WcfClientFixture.cs | 45 ++++++++++++- .../Castle.Facilities.WcfIntegration-vs2008.csproj | 1 + .../WcfClientActivator.cs | 34 +++++++++- .../WcfClientModel.cs | 50 +++++++++++++- .../WcfClientResolver.cs | 44 ++++++------- .../WcfFacility.cs | 42 ++++++------ .../Castle.Facilities.WcfIntegration/WcfUtils.cs | 77 ++++++++++++++++++++++ 9 files changed, 276 insertions(+), 49 deletions(-) create mode 100644 Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/ClassNeedingService.cs create mode 100644 Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfUtils.cs diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests-vs2008.csproj b/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests-vs2008.csproj index e46e3b721..f13d6a86f 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests-vs2008.csproj +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/Castle.Facilities.WcfIntegration.Tests-vs2008.csproj @@ -78,6 +78,7 @@ Code + diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/ClassNeedingService.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/ClassNeedingService.cs new file mode 100644 index 000000000..80c89b487 --- /dev/null +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/ClassNeedingService.cs @@ -0,0 +1,31 @@ +// Copyright 2004-2008 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.Facilities.WcfIntegration.Tests +{ + public class ClassNeedingService + { + private readonly IOperations operations; + + public ClassNeedingService(IOperations operations) + { + this.operations = operations; + } + + public IOperations Operations + { + get { return operations; } + } + } +} diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/WcfClientFixture.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/WcfClientFixture.cs index 8073c81fa..02d2e8a40 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/WcfClientFixture.cs +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration.Tests/WcfClientFixture.cs @@ -31,7 +31,12 @@ namespace Castle.Facilities.WcfIntegration.Tests public void TestInitialize() { windsorContainer = new WindsorContainer() - .AddFacility("wcf_facility", new WcfFacility()) + .AddFacility("wcf_facility", new WcfFacility( + new WcfClientModel() + { + Binding = new NetTcpBinding(), + Address = "net.tcp://localhost/Operations" + })) .Register( Component.For().ImplementedBy() .CustomDependencies(new @@ -96,5 +101,43 @@ namespace Castle.Facilities.WcfIntegration.Tests IAmUsingWindsor client = windsorContainer.Resolve("usingWindsor"); Assert.AreEqual(42, client.GetValueFromWindsorConfig()); } + + [Test] + public void CanResolveClientWhenListedInTheFacility() + { + windsorContainer.Register(Component.For()); + ClassNeedingService component = windsorContainer.Resolve(); + Assert.IsNotNull(component.Operations); + Assert.AreEqual(42, component.Operations.GetValueFromConstructor()); + } + + [Test] + public void CanResolveClientAssociatedWithChannelWithContextOverride() + { + windsorContainer.Register( + Component.For() + .Named("operations") + .CustomDependencies(new + { + clientModel = new WcfClientModel() + { + Binding = new BasicHttpBinding(), + Address = "http://localhost/BadOperations" + } + }) + ); + + IOperations client = windsorContainer.Resolve( + "operations", new + { + clientModel = new WcfClientModel() + { + Binding = new NetTcpBinding(), + Address = "net.tcp://localhost/Operations" + } + }); + + Assert.AreEqual(42, client.GetValueFromConstructor()); + } } } \ No newline at end of file diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration/Castle.Facilities.WcfIntegration-vs2008.csproj b/Facilities/Wcf/Castle.Facilities.WcfIntegration/Castle.Facilities.WcfIntegration-vs2008.csproj index b771b72eb..27fa6abf1 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration/Castle.Facilities.WcfIntegration-vs2008.csproj +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration/Castle.Facilities.WcfIntegration-vs2008.csproj @@ -78,6 +78,7 @@ + diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientActivator.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientActivator.cs index 5ccd5a2d9..30243b583 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientActivator.cs +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientActivator.cs @@ -31,17 +31,47 @@ namespace Castle.Facilities.WcfIntegration { WcfClientModel clientModel = (WcfClientModel) model.ExtendedProperties[WcfConstants.ClientModelKey]; - createChannel = clientModel.GetChannelBuilder(); + createChannel = clientModel.GetChannelBuilder(model.Service); } protected override object InternalCreate(CreationContext context) { - return createChannel(); + WcfClientModel clientModelOverride = GetClientModelOverride(context); + + if (clientModelOverride != null) + { + return clientModelOverride.GetChannelBuilder(Model.Service)(); + } + else + { + return createChannel(); + } } protected override void InternalDestroy(object instance) { } + + private WcfClientModel GetClientModelOverride(CreationContext context) + { + WcfClientModel clientModel = null; + Predicate contractMatch = delegate(WcfClientModel candidate) + { + return candidate.Contract == null || + (Model.Service == candidate.Contract); + }; + + if (context != null && context.HasAdditionalParameters) + { + if (WcfUtils.FindDependency( + context.AdditionalParameters, contractMatch, + out clientModel)) + { + return clientModel; + } + } + return clientModel; + } } } diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientModel.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientModel.cs index 572ab2edb..d4f8e5dc3 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientModel.cs +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientModel.cs @@ -1,3 +1,17 @@ +// Copyright 2004-2008 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + namespace Castle.Facilities.WcfIntegration { using System; @@ -10,15 +24,33 @@ namespace Castle.Facilities.WcfIntegration { } + public WcfClientModel(Type contract) + : base(contract) + { + } + public WcfClientModel(string endpointName) { EndpointName = endpointName; } - internal CreateChannel GetChannelBuilder() + public WcfClientModel(Type contract, string endpointName) + : this(contract) + { + EndpointName = endpointName; + } + + + internal CreateChannel GetChannelBuilder() + { + return GetChannelBuilder(Contract); + } + + internal CreateChannel GetChannelBuilder(Type contract) { object target; - Type type = typeof(ChannelFactory<>).MakeGenericType(new Type[] { Contract }); + contract = contract ?? Contract; + Type type = typeof(ChannelFactory<>).MakeGenericType(new Type[] { contract }); if (!string.IsNullOrEmpty(EndpointName)) { @@ -33,9 +65,23 @@ namespace Castle.Facilities.WcfIntegration } target = Activator.CreateInstance(type, new object[] { Binding, address }); } + MethodInfo methodInfo = type.GetMethod("CreateChannel", new Type[0]); return (CreateChannel) Delegate.CreateDelegate(typeof(CreateChannel), target, methodInfo); } } + + public class WcfClientModel : WcfClientModel + { + public WcfClientModel() + : base(typeof(Contract)) + { + } + + public WcfClientModel(string endpointName) : this() + { + EndpointName = endpointName; + } + } } diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientResolver.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientResolver.cs index b9529d970..e07085090 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientResolver.cs +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfClientResolver.cs @@ -56,47 +56,41 @@ namespace Castle.Facilities.WcfIntegration private WcfClientModel ResolveClientModel(Type contract, ComponentModel model, CreationContext context) { + WcfClientModel clientModel; + Predicate contractMatch = delegate(WcfClientModel candidate) + { + return contract == candidate.Contract; + }; + // First, try the contexrt overrides. - if (context.HasAdditionalParameters) + if (context != null && context.HasAdditionalParameters) { - foreach (object dependency in context.AdditionalParameters.Values) + if (WcfUtils.FindDependency( + context.AdditionalParameters, contractMatch, + out clientModel)) { - if (dependency is WcfClientModel) - { - WcfClientModel clientModel = (WcfClientModel)dependency; - if (clientModel.Contract == contract) - { - return clientModel; - } - } + return clientModel; } } // Then try the component overrides. if (model != null) { - foreach (object dependency in model.CustomDependencies.Values) + if (WcfUtils.FindDependency( + model.CustomDependencies, contractMatch, + out clientModel)) { - if (dependency is WcfClientModel) - { - WcfClientModel clientModel = (WcfClientModel)dependency; - if (clientModel.Contract == contract) - { - return clientModel; - } - } + return clientModel; } } // Finally, try the client models. - foreach (WcfClientModel clientModel in clientModels) + if (WcfUtils.FindDependency( + clientModels, contractMatch, out clientModel)) { - if (clientModel.Contract == contract) - { - return clientModel; - } + return clientModel; } - + return null; } diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfFacility.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfFacility.cs index 57f14c24a..adcc43891 100644 --- a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfFacility.cs +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfFacility.cs @@ -29,7 +29,7 @@ namespace Castle.Facilities.WcfIntegration { } - public WcfFacility(WcfClientModel[] clientModels) + public WcfFacility(params WcfClientModel[] clientModels) { foreach (WcfClientModel clientModel in clientModels) { @@ -43,6 +43,11 @@ namespace Castle.Facilities.WcfIntegration Kernel.ComponentRegistered += Kernel_ComponentRegistered; Kernel.ComponentUnregistered += Kernel_ComponentUnregistered; Kernel.ComponentModelCreated += Kernel_ComponentModelCreated; + + if (clientModels != null && clientModels.Length > 0) + { + Kernel.Resolver.AddSubResolver(new WcfClientResolver(clientModels)); + } } private void Kernel_ComponentModelCreated(ComponentModel componentModel) @@ -83,36 +88,35 @@ namespace Castle.Facilities.WcfIntegration private WcfClientModel ResolveClientModel(ComponentModel componentModel) { + WcfClientModel clientModel = null; + if (componentModel.Service.IsInterface) { - foreach (object dependency in componentModel.CustomDependencies.Values) + if (WcfUtils.FindDependency( + componentModel.CustomDependencies, out clientModel)) { - if (dependency is WcfClientModel) - { - WcfClientModel clientModel = (WcfClientModel)dependency; - ValidateClientModel(clientModel, componentModel); - return clientModel; - } + ValidateClientModel(clientModel, componentModel); } } - return null; + + return clientModel; } private WcfServiceModel ResolveServiceModel(ComponentModel componentModel) { - if (componentModel.Implementation.IsClass && !componentModel.Implementation.IsAbstract) + WcfServiceModel serviceModel = null; + + if (componentModel.Implementation.IsClass && + !componentModel.Implementation.IsAbstract) { - foreach (object dependency in componentModel.CustomDependencies.Values) + if (WcfUtils.FindDependency( + componentModel.CustomDependencies, out serviceModel)) { - if (dependency is WcfServiceModel) - { - WcfServiceModel serviceModel = (WcfServiceModel)dependency; - ValidateServiceModel(serviceModel, componentModel); - return serviceModel; - } + ValidateServiceModel(serviceModel, componentModel); } - } - return null; + } + + return serviceModel; } diff --git a/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfUtils.cs b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfUtils.cs new file mode 100644 index 000000000..2e2aba87e --- /dev/null +++ b/Facilities/Wcf/Castle.Facilities.WcfIntegration/WcfUtils.cs @@ -0,0 +1,77 @@ +// Copyright 2004-2008 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.; + +namespace Castle.Facilities.WcfIntegration +{ + using System; + using System.Collections; + using System.Collections.Generic; + + internal static class WcfUtils + { + public static bool FindDependency(IDictionary dependencies, + out T match) + { + return FindDependency(dependencies, t => true, out match); + } + + public static bool FindDependency(IDictionary dependencies, + Predicate test, out T match) + { + match = default(T); + + foreach (object dependency in dependencies.Values) + { + if (dependency is T) + { + T candidate = (T)dependency; + + if (test(candidate)) + { + match = candidate; + return true; + } + } + } + return false; + } + + public static bool FindDependency(ICollection dependencies, + out T match) + { + return FindDependency(dependencies, t => true, out match); + } + + public static bool FindDependency(ICollection dependencies, + Predicate test, out T match) + { + match = default(T); + + foreach (object dependency in dependencies) + { + if (dependency is T) + { + T candidate = (T)dependency; + + if (test(candidate)) + { + match = candidate; + return true; + } + } + } + return false; + } + } +} -- 2.11.4.GIT