I've got services (in the Castle Windsor sense) which are marked with the [ServiceContract] attribute. Some of them are WCF hosted, others run locally.
I want my installer to be as generic as possibly. The logic I'm looking for is this:
- Look for services in the application's bin directory.
- Anything you find an implementation for, use locally (this also has to work with decorators).
- Anything you don't find, assume that shall be called via WCF.
The services are hosted in a web application, the Application_Start method sets up everything and hosts the services via WCF. In this web application, accessing other services via WCF also works without any further logic.
However, I also have an ASP.NET MVC application, and I can't get that to call services via WCF. I always end up with an error message that says:
Type IMyService is abstract. As such, it is not possible to instansiate it as implementation of service IMyService
And, when I register an interceptor, it says
This is a DynamicProxy2 error: The interceptor attempted to 'Proceed' for method 'MyDataContract FooMethod(System.String)' which has no target. When calling method without target there is no implementation to 'proceed' to and it is the responsibility of the interceptor to mimic the implementation (set return value, out arguments etc)
Here is my latest attempt (in other words, it is the "ConfigureAsWcfClient" part that doesn't work):
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Classes.FromAssemblyInDirectory(Constants.MyAssemblyFilter)
.Where(ImplementsServiceContract)
.WithServiceSelect((x, y) => GetServices(x))
#if DECORATORS_PRESENT
.ConfigureIf(IsDecorator, c => c
.IsDefault(y => IsDecorating(c, y))
.Named(GetNameOfServiceContract(c)))
#else
// TODO: FIXME: Set name for services without decorator
.ConfigureIf(c => string.IsNullOrEmpty(c.Name),
c => c.Named(GetNameOfServiceContract(c)))
#endif
);
container.Register(Types.FromAssemblyInDirectory(Constants.BikubeAssemblyFilter)
.Where(x => IsServiceContract(x) && !container.Kernel.HasComponent(x))
.WithServiceSelf()
.Configure(ConfigureAsWcfClient));
}
private static void ConfigureAsWcfClient(ComponentRegistration c)
{
c.Named(c.Implementation.Name).AsWcfClient(WcfEndpoint.FromConfiguration("*"));
}
private static string GetNameOfServiceContract(ComponentRegistration c)
{
var name = GetServices(c.Implementation).First().FullName;
Debug.WriteLine("CW register sevice contract: " + name);
return name;
}
private static bool ImplementsServiceContract(Type type)
{
return GetServices(type).Any();
}
private static IEnumerable<Type> GetServices(Type type)
{
return type.GetInterfaces().Where(IsServiceContract);
}
private static bool IsServiceContract(Type type)
{
var ns = type.Namespace;
return ns != null && ns.StartsWith(Constants.NamespacePrefix) && Attribute.IsDefined(type, typeof(ServiceContractAttribute));
}
private static bool IsDecorator(ComponentRegistration c)
{
Type component = c.Implementation;
var isDecorator = GetServices(component).Any(x => IsDecorating(c, x));
return isDecorator;
}
private static bool IsDecorating(ComponentRegistration c, Type service)
{
Type component = c.Implementation;
return !component.Assembly.GetName().Name.EndsWith(".Impl", StringComparison.InvariantCultureIgnoreCase);
}
I would of course also like to get rid of the preprocessor directive. The system should detect automatically if decorators are present. "Local" implementations are in assemblies named .Impl.dll, decorators are in assemblies that are called *.ServiceProxy.*.dll.
Oh, and if I remove the special treatment of the non-local services (those that shall be called via WCF) I always get the error message "the client model requires and endpoint."
Any help is highly appreciated.