Error while resolving type because of constructor? - singleton

I get an error:
Resolution of the dependency failed, type = "MyAppApp.ServiceAgents.IMyAppServiceAgent", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type Int32 cannot be constructed. You must configure the container to supply this value.
-----------------------------------------------
At the time of the exception, the container was:
Resolving MyAppApp.ServiceAgents.MyAppServiceAgent,(none) (mapped from MyAppApp.ServiceAgents.IMyAppServiceAgent, (none))
Resolving parameter "AuthHandlerId" of constructor MyAppApp.ServiceAgents.MyAppServiceAgent(System.Int32 AuthHandlerId, System.String AuthSessionGuid, System.ServiceModel.EndpointAddress ServiceEndPointAddress)
Resolving System.Int32,(none)
in the method which is below:
internal ServiceLocator()
{
services = new Dictionary<object, object>();
// fill the map
this.services.Add(typeof(IMyAppServiceAgent), _container.Resolve<IMyAppServiceAgent>());
}
This is how I call this method
I have a standard method in the ViewModelLocator (from MVVM Light Toolkit) method
public static void CreateShowroomLog()
{
if (_showroomLog == null)
{
_showroomLog = new ShowroomLogViewModel(ServiceLocator.Instance(_container).GetService<IMyAppServiceAgent>());
}
}
and constructor is
public ViewModelLocator()
{
_container=new UnityContainer();
_container.RegisterType<IMyAppServiceAgent, MyAppServiceAgent>();
}
The class of which I need an instance is:
protected static EndpointAddress ServiceEndPointAddress
{
get { return (App.Current as App).ServiceEndpointAddr; }
}
protected static string AuthSessionGuid
{
get { return (App.Current as App).W2OGuid; }
}
protected static int AuthHandlerId
{
get { return (App.Current as App).OriginalHandlerId; }
}
public MyAppServiceAgent(int AuthHandlerId, string AuthSessionGuid, System.ServiceModel.EndpointAddress ServiceEndPointAddress)
{
_proxy = new MyAppService.Service1Client(new BasicHttpMessageInspectorBinding(new SilverlightAuthMessageInspector(AuthHandlerId.ToString(), AuthSessionGuid)), ServiceEndPointAddress);
}
public MyAppServiceAgent()
: this(AuthHandlerId, AuthSessionGuid, ServiceEndPointAddress)
{
}
How can I resolve this problem with cosntructor?

When you register your type you didn't specify which constructor to call on MyAppServiceAgent. By default Unity will choose the constructor with the most parameters but you didn't specify how those parameters should be resolved.
You could try this and see if it will cause the the default constructor (paramaterless) of MyAppServiceAgent to be called when this type is resolved..
_container=new UnityContainer();
_container.RegisterType<IMyAppServiceAgent, MyAppServiceAgent>(new InjectionConstructor());
What I think would be even better is to remove the ServiceEndPointAddress, AuthSessionGuid and AuthHandlerId static properties from your MyAppServiceAgent class. Then register the type like this
_container=new UnityContainer();
_container.RegisterType<IMyAppServiceAgent, MyAppServiceAgent>(
new InjectionConstructor(
(App.Current as App).OriginalHandlerId,
(App.Current as App).W2OGuid,
(App.Current as App).ServiceEndpointAddr
));
Which should cause this constructor to be called.
public MyAppServiceAgent(int AuthHandlerId, string AuthSessionGuid, System.ServiceModel.EndpointAddress ServiceEndPointAddress)
{
_proxy = new MyAppService.Service1Client(new BasicHttpMessageInspectorBinding(new SilverlightAuthMessageInspector(AuthHandlerId.ToString(), AuthSessionGuid)), ServiceEndPointAddress);
}
That way your MyAppServiceAgent class is not dependent on the App class.

Related

JUnit 5 Parameterized test #ArgumentsSource parameters not loading

I have created below JUnit5 parameterized test with ArgumentsSource for loading arguments for the test:
public class DemoModelValidationTest {
public ParamsProvider paramsProvider;
public DemoModelValidationTest () {
try {
paramsProvider = new ParamsProvider();
}
catch (Exception iaex) {
}
}
#ParameterizedTest
#ArgumentsSource(ParamsProvider.class)
void testAllConfigurations(int configIndex, String a) throws Exception {
paramsProvider.executeSimulation(configIndex);
}
}
and the ParamsProvider class looks like below:
public class ParamsProvider implements ArgumentsProvider {
public static final String modelPath = System.getProperty("user.dir") + File.separator + "demoModels";
YAMLDeserializer deserializedYAML;
MetaModelToValidationModel converter;
ValidationRunner runner;
List<Configuration> configurationList;
List<Arguments> listOfArguments;
public ParamsProvider() throws Exception {
configurationList = new ArrayList<>();
listOfArguments = new LinkedList<>();
deserializedYAML = new YAMLDeserializer(modelPath);
deserializedYAML.load();
converter = new MetaModelToValidationModel(deserializedYAML);
runner = converter.convert();
configurationList = runner.getConfigurations();
for (int i = 0; i < configurationList.size(); i++) {
listOfArguments.add(Arguments.of(i, configurationList.get(i).getName()));
}
}
public void executeSimulation(int configListIndex) throws Exception {
final Configuration config = runner.getConfigurations().get(configListIndex);
runner.run(config);
runner.getReporter().consolePrintReport();
}
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return listOfArguments.stream().map(Arguments::of);
// return Stream.of(Arguments.of(0, "Actuator Power"), Arguments.of(1, "Error Logging"));
}}
In the provideArguments() method, the commented out code is working fine, but the first line of code
listOfArguments.stream().map(Arguments::of)
is returning the following error:
org.junit.platform.commons.PreconditionViolationException: Configuration error: You must configure at least one set of arguments for this #ParameterizedTest
I am not sure whether I am having a casting problem for the stream in provideArguments() method, but I guess it somehow cannot map the elements of listOfArguments to the stream, which can finally take the form like below:
Stream.of(Arguments.of(0, "Actuator Power"), Arguments.of(1, "Error Logging"))
Am I missing a proper stream mapping of listOfArguments?
provideArguments(…) is called before your test is invoked.
Your ParamsProvider class is instantiated by JUnit. Whatever you’re doing in desiralizeAndCreateValidationRunnerInstance should be done in the ParamsProvider constructor.
Also you’re already wrapping the values fro deserialised configurations to Arguments and you’re double wrapping them in providesArguments.
Do this:
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return listOfArguments.stream();
}}

Unable to add mutator for an existing field of a class

I'm trying to add a mutator for an existing private final field. I can transform the field modifiers to remove the final specification and add an accessor method:
// accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}
...
// fragment of Java agent implementation
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
// .implement(UniqueIdMutator.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(Hooked.class);
}
})
.installOn(instrumentation);
...
Here's a method that uses reflection to check the modifiers of the target field and calls the accessor to get the value of the field.
private static void injectProxy(Description description) {
try {
Field bar = Description.class.getDeclaredField("fUniqueId");
System.out.println("isFinal: " + ((bar.getModifiers() & Modifier.FINAL) != 0));
} catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Serializable uniqueId = ((UniqueIdAccessor) description).getUniqueId();
System.out.println("uniqueId: " + uniqueId);
}
// isFinal: false
// uniqueId: <description-unique-id>
... but if I uncomment the second "implement" expression to add the mutator, the transform blows up:
// isFinal: true
// java.lang.ClassCastException:
// class org.junit.runner.Description cannot be cast to class com.nordstrom.automation.junit.UniqueIdAccessor
// (org.junit.runner.Description and com.nordstrom.automation.junit.UniqueIdAccessor
// are in unnamed module of loader 'app')
I could set the field value with reflection, but that defeats the purpose of using Byte Buddy in the first place!
The problem with this approach is that the field accessor considers the input type prior to the modification. Byte Buddy prohibits this as it does not consider the mutation to be legal, not knowing about the removed modifier. As a result, the transformation fails in its entirety and you get the error you are seeing. (Register a listener to see this error.)
To avoid this, you can implement a custom Implementation using FieldAccess (without or). You can have a look at the more convenient FieldAccessor to see how this is implemented, only that you need to drop the validity checks.
Thanks for pointing me in the right direction! I assemble the StackManipulation object that defines the mutator method with this:
final TypeDescription description = TypePool.Default.ofSystemLoader().describe("org.junit.runner.Description").resolve();
final Generic _void_ = TypeDescription.VOID.asGenericType();
final Generic serializable = TypePool.Default.ofSystemLoader().describe("java.io.Serializable").resolve().asGenericType();
final MethodDescription.Token setUniqueIdToken = new MethodDescription.Token("setUniqueId", Modifier.PUBLIC, _void_, Arrays.asList(serializable));
final MethodDescription setUniqueId = new MethodDescription.Latent(description, setUniqueIdToken);
final Token fUniqueIdToken = new FieldDescription.Token("fUniqueId", Modifier.PRIVATE, serializable);
final FieldDescription fUniqueId = new FieldDescription.Latent(description, fUniqueIdToken);
final StackManipulation setUniqueIdImpl = new StackManipulation.Compound(
MethodVariableAccess.loadThis(),
MethodVariableAccess.load(setUniqueId.getParameters().get(0)),
Assigner.DEFAULT.assign(serializable, serializable, Typing.STATIC),
FieldAccess.forField(fUniqueId).write(),
MethodReturn.VOID
);
... and I transform the target class with this:
return new AgentBuilder.Default()
.type(hasSuperType(named("org.junit.runner.Description")))
.transform(new Transformer() {
#Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
return builder.field(named("fUniqueId")).transform(ForField.withModifiers(FieldManifestation.PLAIN))
.implement(AnnotationsAccessor.class).intercept(FieldAccessor.ofField("fAnnotations"))
.implement(UniqueIdAccessor.class).intercept(FieldAccessor.ofField("fUniqueId"))
.implement(UniqueIdMutator.class).intercept(new Implementation.Simple(setUniqueIdImpl));
}
})
.installOn(instrumentation);
Here are the definitions of the three interfaces used in the transform:
// annotations accessor interface
public interface AnnotationsAccessor {
Annotation[] annotations();
}
// unique ID accessor interface
public interface UniqueIdAccessor {
Serializable getUniqueId();
}
// unique ID mutator interface
public interface UniqueIdMutator {
void setUniqueId(Serializable uniqueId);
}

Spring security custom FilterInvocationSecurityMetadataSource implementation 403 forbidden issue

To make things short I'm trying to implement a custom FilterInvocationSecurityMetadataSource in order to secure/authorize certain parts/URL endpoints dynamically in my web app using spring security 5.0.6 and Spring Boot 2.0.3.
The issue is that no matter what Role I use it always gives me the forbidden page.
I have tried several things with different role names and (believe me) I have searched the whole internet even on spring security 5.0.6 books but nothing seems to work.
This issue may be similar to this: Spring Security issue with securing URLs dynamically
Below the relevant parts of the custom FilterInvocationSecurityMetadataSource
public class DbFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
FilterInvocation fi=(FilterInvocation)object;
String url=fi.getRequestUrl();
System.out.println("URL requested: " + url);
String[] stockArr = new String[]{"ROLE_ADMIN"};
return SecurityConfig.createList(stockArr);
}
Below the relevant parts of the custom implementation of securitywebconfigAdapter
#Configuration
public class Security extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
FilterInvocationSecurityMetadataSource newSource = new DbFilterInvocationSecurityMetadataSource();
fsi.setSecurityMetadataSource(newSource);
return fsi;
}
})
.and()
.formLogin()
.permitAll();
}
Below the relevant parts for custom userDetails authorities.
The user has the role: ROLE_ADMIN in database.
public class CustomUserDetails extends User implements UserDetails {
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<String> dbRoles=new ArrayList<String>();
for (Role userRole : super.getRoles()) {
dbRoles.add(userRole.getType());
}
List<SimpleGrantedAuthority> authorities=new ArrayList<SimpleGrantedAuthority>();
for (String role : dbRoles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
What am I doing wrong??
If more code is needed just comment below.
If you have even good books where I can learn this dynamic part of Spring security authorization comment below.
Thanks!
I managed to get into the security flow by debugging and it seems that by creating ConfigAttributes of this SecurityConfig class is the 'culprit'
return SecurityConfig.createList(stockArr);
public static List<ConfigAttribute> createList(String... attributeNames) {
Assert.notNull(attributeNames, "You must supply an array of attribute names");
List<ConfigAttribute> attributes = new ArrayList(attributeNames.length);
String[] var2 = attributeNames;
int var3 = attributeNames.length;
for(int var4 = 0; var4 < var3; ++var4) {
String attribute = var2[var4];
attributes.add(new SecurityConfig(attribute.trim()));
}
return attributes;
}
Above is the actual implementation of the method where you can see
attributes.add(new SecurityConfig(attribute.trim()));
And this always creates an instance of SecurityConfig type.
And below you can actually see where and how the decision is being made.
private WebExpressionConfigAttribute findConfigAttribute(Collection<ConfigAttribute> attributes) {
Iterator var2 = attributes.iterator();
ConfigAttribute attribute;
do {
if (!var2.hasNext()) {
return null;
}
attribute = (ConfigAttribute)var2.next();
} while(!(attribute instanceof WebExpressionConfigAttribute));
return (WebExpressionConfigAttribute)attribute;
}
So in order for it to actually return a configattribute for checking it must be of type WebExpressionConfigAttribute which is never going to be the case because of this
attributes.add(new SecurityConfig(attribute.trim()));
So the way I fixed it is to create my own accessDecisionManager the following way
public class MyAccessDecisionManager implements AccessDecisionManager {
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null){
return ;
}
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while(ite.hasNext()){
ConfigAttribute ca = ite.next();
String needRole = ((SecurityConfig)ca).getAttribute();
for(GrantedAuthority grantedAuthority : authentication.getAuthorities()){
if(needRole.trim().equals(grantedAuthority.getAuthority().trim())){
return;
}
}
}
throw new AccessDeniedException("Access is denied");
}
And registering as above now setting the accessdecisionManager with my custom one
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
FilterInvocationSecurityMetadataSource newSource = new DbFilterInvocationSecurityMetadataSource();
fsi.setSecurityMetadataSource(newSource);
fsi.setAccessDecisionManager(new MyAccessDecisionManager());
return fsi;
}

Ideablade's Cocktail Composition Container for WCF projects

I recently upgraded an application I am working on from Cocktail 1.4 to Cocktail 2.6 (Punch). I have adjusted my bootstrapper class for the wpf project which now loads with no issues. However, on my WCF / Web projects, I am receiving a runtime exception with the following error when attempting to call Composition.GetInstance:
"You must first set a valid CompositionProvider by using Composition.SetProvider."
After digging into the issue a bit, it appears the composition container is automatically configured when your bootstrapper inherits from CocktailMefBootstrapper. I currently do not have bootstrapper classes at all for non-wpf projects. Prior to the upgrade, all I had to do was call the configure method on the Composition class to configure the composition container, but it appears that it has been deprecated:
Composition.Configure();
I noticed that you can also call Composition.SetProvider(), however I am a little unsure on how to satisfy the method signature exactly. The DevForce Punch documentation states that the generic type for the bootstrapper class should be a viewmodel, and there are no views / view models in a service project. This leaves me in limbo on what to do as I don't want to rip cocktail out of these WCF projects. Is there still a way to use Cocktail's composition container without a bootstrapper for a project in Cocktail (Punch) 2.6?
UPDATE
I found this on the DevForce forums. So it appears that I ought to learn how to configure a multi threaded ICompositionProvider and call Composition.SetProvider() as mentioned above. Any recommended articles to achieving this?
After digging through Punch's source code and looking at Ideablade's MefCompositionContainer, which implements ICompositionProvider, I created my own thread safe implementation of ICompositionProvider. Below is the code I used. Basically, it's the same code for Ideablade's MefCompositionContainer which can be found here in their repository. The only change is that I am passing a bool flag of true into the CompositionContainer's constructor. MSDN lists the pros and cons of making the container thread safe
internal partial class ThreadSafeCompositionProvider : ICompositionProvider
{
static ThreadSafeCompositionProvider()
{
CompositionHost.IgnorePatterns.Add("Caliburn.Micro*");
CompositionHost.IgnorePatterns.Add("Windows.UI.Interactivity*");
CompositionHost.IgnorePatterns.Add("Cocktail.Utils*");
CompositionHost.IgnorePatterns.Add("Cocktail.Compat*");
CompositionHost.IgnorePatterns.Add("Cocktail.dll");
CompositionHost.IgnorePatterns.Add("Cocktail.SL.dll");
CompositionHost.IgnorePatterns.Add("Cocktail.WinRT.dll");
}
public IEnumerable<Assembly> GetProbeAssemblies()
{
IEnumerable<Assembly> probeAssemblies = CompositionHost.Instance.ProbeAssemblies;
var t = GetType();
// Add Cocktail assembly
probeAssemblies = probeAssemblies.Concat(GetType().GetAssembly());
return probeAssemblies.Distinct(x => x);
}
private List<Assembly> _probeAssemblies;
private AggregateCatalog _defaultCatalog;
private ComposablePartCatalog _catalog;
private CompositionContainer _container;
public ComposablePartCatalog Catalog
{
get { return _catalog ?? DefaultCatalog; }
}
public ComposablePartCatalog DefaultCatalog
{
get
{
if (_defaultCatalog == null)
{
_probeAssemblies = GetProbeAssemblies().ToList();
var mainCatalog = new AggregateCatalog(_probeAssemblies.Select(x => new AssemblyCatalog(x)));
_defaultCatalog = new AggregateCatalog(mainCatalog);
CompositionHost.Recomposed += new EventHandler<RecomposedEventArgs>(OnRecomposed)
.MakeWeak(x => CompositionHost.Recomposed -= x);
}
return _defaultCatalog;
}
}
internal void OnRecomposed(object sender, RecomposedEventArgs args)
{
if (args.HasError) return;
var newAssemblies = GetProbeAssemblies()
.Where(x => !_probeAssemblies.Contains(x))
.ToList();
if (newAssemblies.Any())
{
var catalog = new AggregateCatalog(newAssemblies.Select(x => new AssemblyCatalog(x)));
_defaultCatalog.Catalogs.Add(catalog);
_probeAssemblies.AddRange(newAssemblies);
}
// Notify clients of the recomposition
var handlers = Recomposed;
if (handlers != null)
handlers(sender, args);
}
public CompositionContainer Container
{
get { return _container ?? (_container = new CompositionContainer(Catalog, true)); }
}
public Lazy<T> GetInstance<T>() where T : class
{
var exports = GetExportsCore(typeof(T), null).ToList();
if (!exports.Any())
throw new Exception(string.Format("Could Not Locate Any Instances Of Contract", typeof(T).FullName));
return new Lazy<T>(() => (T)exports.First().Value);
}
public T TryGetInstance<T>() where T : class
{
if (!IsTypeRegistered<T>())
return null;
return GetInstance<T>().Value;
}
public IEnumerable<T> GetInstances<T>() where T : class
{
var exports = GetExportsCore(typeof(T), null);
return exports.Select(x => (T)x.Value);
}
public Lazy<object> GetInstance(Type serviceType, string contractName)
{
var exports = GetExportsCore(serviceType, contractName).ToList();
if (!exports.Any())
throw new Exception(string.Format("Could Not Locate Any Instances Of Contract",
serviceType != null ? serviceType.ToString() : contractName));
return new Lazy<object>(() => exports.First().Value);
}
public object TryGetInstance(Type serviceType, string contractName)
{
var exports = GetExportsCore(serviceType, contractName).ToList();
if (!exports.Any())
return null;
return exports.First().Value;
}
public IEnumerable<object> GetInstances(Type serviceType, string contractName)
{
var exports = GetExportsCore(serviceType, contractName);
return exports.Select(x => x.Value);
}
public ICompositionFactory<T> GetInstanceFactory<T>() where T : class
{
var factory = new ThreadSafeCompositionFactory<T>();
Container.SatisfyImportsOnce(factory);
if (factory.ExportFactory == null)
throw new CompositionException(string.Format("No export found.", typeof(T)));
return factory;
}
public ICompositionFactory<T> TryGetInstanceFactory<T>() where T : class
{
var factory = new ThreadSafeCompositionFactory<T>();
Container.SatisfyImportsOnce(factory);
if (factory.ExportFactory == null)
return null;
return factory;
}
public void BuildUp(object instance)
{
// Skip if in design mode.
if (DesignTime.InDesignMode())
return;
Container.SatisfyImportsOnce(instance);
}
public bool IsRecomposing { get; internal set; }
public event EventHandler<RecomposedEventArgs> Recomposed;
internal bool IsTypeRegistered<T>() where T : class
{
return Container.GetExports<T>().Any();
}
public void Configure(CompositionBatch compositionBatch = null, ComposablePartCatalog catalog = null)
{
_catalog = catalog;
var batch = compositionBatch ?? new CompositionBatch();
if (!IsTypeRegistered<IEventAggregator>())
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
Compose(batch);
}
public void Compose(CompositionBatch compositionBatch)
{
if (compositionBatch == null)
throw new ArgumentNullException("compositionBatch");
Container.Compose(compositionBatch);
}
private IEnumerable<Lazy<object>> GetExportsCore(Type serviceType, string key)
{
return Container.GetExports(serviceType, null, key);
}
}
After setting up that class, I added a configuration during startup to instantiate my new thread safe composition provider and to set it as the provider for Punch's Composition class:
if (createThreadSafeCompositionContainer)
{
var threadSafeContainer = new ThreadSafeCompositionProvider();
Composition.SetProvider(threadSafeContainer);
}
Seems to be working like a charm!

How do I bind generic types with inheritance using Ninject Conventions extensions

How can I bind InitializerForXXX (non-generic implementation) to IInitializer<XXX> (generic interface) using Ninject Conventions so that requests for an IInitializer<T> resolve a non-generic implementation whose name starts with InitializerFor and end with typeof(T).Name like:
initializerFactory.CreateFor<Blue>(); //resolves InitializerOfBlue
initializerFactory.CreateFor<ShadeOfBlue>(); //resolves InitializerOfShadeOfBlue
where no non-abstract class directly implement IInitializer<T>, and some implementations inherit from other implementations:
InitializerForShadeOfBlue inherits from InitializerForBlue
InitializerForBlue inherits from abstract Initializer<Blue>
abstract Initializer<T> directly implements IInitializer<T>
I'm hoping I can use a .EndsWith(typeof(T).Name) for a given IInitializer<T> convention I can use, because there are literally hundreds of initializers in the ShadeOfxxx vein. If I have to map all of them, I'm better off finding a way to resolve with reflection at runtime.
Given the following:
UPDATE: bindings with custom binding generator (see my answer below for implementation)
void Bootstrap(IBindingRoot kernel)
{
kernel.Bind<IInitializerFactory>()
.To<InitializerFactory>()
.InSingletonScope();
kernel.Bind(scanner =>
scanner.FromThisAssembly().SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof(IComplexContent))
.BindAllInterfaces());
kernel.Bind(scanner =>
scanner.FromThisAssembly().SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof(IInitializer<>))
.BindWith<FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator>());
}
main method
void Main(IEnumerable<string> values)
{
// setup bindings
var kernel = new StandardKernel();
Bootstrap(kernel);
IInitializerFactory initializerFactory =
kernel.Get<IInitializerFactory>();
IInitializer<ShadeOfBlueComplexContent> initializer =
initializerFactory.CreateFor<ShadeOfBlueComplexContent>();
initializer.Initialize(values);
}
initializer factory
interface IInitializerFactory
{
IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new();
}
class InitializerFactory : IInitializerFactory
{
public IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new()
{
return MagicallyGetInitializer<T>();
}
//behind the curtain, whirring noises are heard as 't' is resolved...
private static IInitializer<T> MagicallyGetInitializer<T>()
where T : class, IComplexContent, new()
{
IInitializer<T> i = null;
return i;
}
}
initializers
interface IInitializer<out T> where T : IComplexContent
{
T Initialize(IEnumerable<string> values);
}
abstract class Initializer<T> : IInitializer<T> where T : IComplexContent
{
public abstract T Initialize(IEnumerable<string> values);
}
class InitializerOfBlue : Initializer<Blue>
{
private readonly Blue _content;
public InitializerOfBlue(Blue content) {_content = content;}
public override Blue Initialize(IEnumerable<string> values)
{
_content.BlueSpecificProperty = values.ElementAt(0);
//... populate other blue-specific properties like this
return _content;
}
}
class InitializerOfShadeOfBlue : InitializerOfBlue
{
public InitializerOfShadeOfBlue(ShadeOfBlue content) : base(content){}
}
content models
interface IComplexContent
{
string OneBasicProperty { get; set; }
// other properties are specific to implementation
string UniqueOperation();
}
abstract class BaseComplexContent : IComplexContent
{
public string OneBasicProperty { get; set; }
public abstract string UniqueOperation();
}
class Blue : BaseComplexContent
{
// initializer sets this
public string PropertyForAllKindsOfBlue { get; set; }
// initializer doesn't interact with this
public override string UniqueOperation() {return "I'm plain.";}
}
class ShadeOfBlue : Blue
{
// initializer doesn't interact with this
public override string UniqueOperation() {return "I'm fabulous!";}
}
You are over specifying the class selection
kernel.Bind(scanner =>
scanner.FromThisAssembly().SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof (IInitializer<>))
This is already enough. What you need to do though is to add a custom Binding Generator. That selects IInitializer<Blue> for InitializerForBlue and IInitializer<ShadeOfBlue> for InitializerForShadeOfBlue
https://github.com/ninject/ninject.extensions.conventions/wiki/Projecting-Services-to-Bind
BEGIN SOLUTION CANDIDATE - custom binding generator:
custom binding generator
Thanks for the advice, #RemoGloor and #RubenBartelink. I'm stumped though - the problem is that I wind up binding the IInitializer<Blue> to InitializerOfShadeOfBlue. I need to be able to somehow change the generic type argument from Blue to ShadeOfBlue in the IInitializer<Blue> binding candidate, since IInitializer<ShadeOfBlue> is what will be requested from the factory method at runtime.
Is there a way to modify the generic type argument list of the binding candidate? Or am I barking up the wrong implementation? Any edit suggestions to my OP or this answer are appreciated.
/// <summary>Creates bindings on open generic types where bound implementations'
/// names end with the name of the generic type argument</summary>
public class FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type == null) throw new ArgumentNullException("type");
if (bindingRoot == null) throw new ArgumentNullException("bindingRoot");
// only consider concrete, non-abstract classes
if (type.IsInterface || type.IsAbstract) yield break;
var bindingType = GetBindingType(type);
if (bindingType != null)
yield return bindingRoot.Bind(bindingType).To(type);
// ARGH! bindingType == IInitializer`1[[Blue]] but I want
// IInitializer`1[[ShadeOfBlue]] for type == ShadeOfBlue
}
private static Type GetBindingType(Type type)
{
Type goodMatch = null;
foreach (var candidate in type.GetInterfaces())
{
// skip non-generic interfaces
if (!candidate.IsGenericType) continue;
// assumption: using argument in first position
var firstArg = candidate.GetGenericArguments().First();
if (!type.Name.EndsWith(firstArg.Name)) continue;
// IInitializer<XXX> matches InitializerOfXXX
goodMatch = candidate;
break;
}
if (goodMatch == null)
{
// if no match on interfaces, walk through the ancestor types
foreach (var candidate in type.GetAllAncestors())
{
goodMatch = GetBindingType(candidate);
if (goodMatch != null) break;
}
}
return goodMatch;
}
Type Extension helper
public static class TypeExtensions
{
// returns all ancestor types starting with the parent
public static IEnumerable<Type> GetAllAncestors(this Type type)
{
for (var current = type.BaseType; current != null; current = current.BaseType)
yield return current;
}
}
END SOLUTION CANDIDATE - custom binding generator