0
votes

I have two interfaces that says if a service have to be singleton or transient:

public interface ITransient {}
public interface ISingleton {}

I implement this interfaces in other interfaces and classes:

public interface ISession : ISingleton
{
    int? UserId {get;set;}
}

public class Session : ISession
{
    public int? UserId {get;set;}
}

Then I inject Session in others services:

public interface IBookService : ITransient
{
    ...
}

public class BookService : IBookService
{
    public BookService(ISession session) { ... }
    ...
}

How to configure StructureMap to make that all instance requests of types that implements ISingleton have to create with Singleton lifecycle??

I have tried it:

Container.Configure(conf => {
    conf.For<ITransient>().Transient();
    conf.For<ISingleton>().Singleton();
}

But nothing ... don't work, create a Session object as Transient.

I have tried it too:

        Container.Configure(conf =>
        {
            conf.Scan(s =>
            {
                s.Assembly(assembly);
                s.LookForRegistries();

                s.AddAllTypesOf<ISingletonDependency>();
                s.AddAllTypesOf<ITransientDependency>();
            });

            conf.For<ITransientDependency>().Transient();
            conf.For<ISingletonDependency>().Singleton();
        });

And nothing ...

I have seen how to do it using Windsor Castle:

        context.IocContainer.Register(
            Classes.FromAssembly(context.Assembly)
                .IncludeNonPublicTypes()
                .BasedOn<ITransient>()
                .WithService.Self()
                .WithService.DefaultInterfaces()
                .LifestyleTransient()
        );

        //Singleton
        context.IocContainer.Register(
            Classes.FromAssembly(context.Assembly)
                .IncludeNonPublicTypes()
                .BasedOn<ISingleton>()
                .WithService.Self()
                .WithService.DefaultInterfaces()
                .LifestyleSingleton()

But I don't know how to do using StructureMap ...

Other posibility is using conventions (IRegistrationConvention), but I don't know how to do, example:

    public class LifecycleConvention : IRegistrationConvention
    {
        public void Process(Type type, Registry registry)
        {
            if (type.GetInterface(typeOf(ISingleton) != null)
                **???? what to do ??!!**
        }
    }

Somebody can help me please?

UPDATE

I have build a convention:

public class BasicConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (!type.IsAbstract && typeof(ISingleton).IsAssignableFrom(type))
        {
            registry.For(type, new SingletonLifecycle());
        }
        if (!type.IsAbstract && typeof(ITransient).IsAssignableFrom(type))
        {
            registry.For(type, new TransientLifecycle());
        }
    }
}

And that seem work but it register each class as plugin type, in this case:

Session => Session [Singleton] BookService => BookService [Transient]

But if I inject the Session as ISession ... don't found the instance due to ISession is not registered ... But I can use default convetions ... and then work BUT retrieve the instance as transient ...

Calling WhatDoIHave() I can see it:

===============================================================================================================================================================================================================================================================================
PluginType                  Namespace                          Lifecycle     Description                                                                                                                                               Name                                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
....
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ISession                     Paf.Application.Session            Transient     Paf.Application.Session ('Paf.Application.Session, Paf.Modules.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null')                        Paf.Application.Session,... (Default)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
.....
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Session                     Paf.Application                    Singleton     Paf.Application.Session                                                                                                                                (Default)                               
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...
===============================================================================================================================================================================================================================================================================

Can I solve this it?

1
You are currently the abstraction with a lifestyle, but I think this is the wrong approach, since it is the implementation that determines its lifestyle, not the abstraction. In other words: your abstraction leaks which is a violation of the Dependency Inversion Principle.Steven
True, true, ... then is "Session" (the implementation) who should implement "ISingleton" instead of "ISession", true?Javier Ros
Yep, or you mark it with an attribute (which seems more natural to me in this case). btw, why does BookService depend on Session instead of ISession. Is that an error? I think that is an accidental Short Circuited Dependency.Steven
Ok, I think using the attribute is a good way. Thanks!! Yes, it should be ISession, I'm going to edit my question.Javier Ros

1 Answers

0
votes

Ok, I have gone to StructureMap sources to see how to work the default contention.

I found DefaultConventionScanner class:

public class DefaultConventionScanner : ConfigurableRegistrationConvention
{
    public override void Process(Type type, Registry registry)
    {
        if (!type.IsConcrete()) return;

        var pluginType = FindPluginType(type);
        if (pluginType != null && type.HasConstructors())
        {
            registry.AddType(pluginType, type);
            ConfigureFamily(registry.For(pluginType));
        }
    }

    public virtual Type FindPluginType(Type concreteType)
    {
        var interfaceName = "I" + concreteType.Name;
        return concreteType.GetInterfaces().FirstOrDefault(t => t.Name == interfaceName);
    }
}

I can deduce that I could change the plugin type registration (registry.AddType(pluginType, type) line of code) and write it:

            if(typeof(ISingleton).IsAssignableFrom(type))
                registry.For(pluginType).Use(type).Singleton();
            else if (typeof(ITransient).IsAssignableFrom(type))
                registry.For(pluginType).Use(type).Transient();
            else
            {
                registry.AddType(pluginType, type);
            }
            ConfigureFamily(registry.For(pluginType));

Ergo, if the pluginType (interface type) is ISingleton or ITransient I registry the new plugin type as Singleton or Transient otherwise registry the pluginType as allwais.

I have try and know work!! yeah!

Only one consideration, I don't know if exists some diference between:

            registry.AddType(pluginType, type);

and: registry.For(pluginType).Use(type);

I have compare the results of WhaDoIHave() before and after, and I can see a only one difference.

Old result:

===============================================================================================================================================================================================================================================================================
PluginType                  Namespace                          Lifecycle     Description                                                                                                                                               Name                                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ISession                    Paf.Application.Session            Transient     Paf.Application.Session ('Paf.Application.Session, Paf.Modules.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null')                              Paf.Application.Session,... (Default)
===============================================================================================================================================================================================================================================================================

New result:

===============================================================================================================================================================================================================================================================================
PluginType                  Namespace                          Lifecycle     Description                                                                                                                                               Name                                    
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ISession                    Paf.Application.Session            Singleton     Paf.Application.Session                                                                                                                                (Default)                               
===============================================================================================================================================================================================================================================================================

The new result is Ok, is Singleton, the diference is only in description, I think is no important think.

UPDATE

According to conversation with Steve in the comments of my question, I have decide not use ITransient and ISingleton interfaces. I have decided to use Attributes in the implementation classes.

I've created 2 attributes:

[AttributeUsage(AttributeTargets.Class)]
public class SingletonLifecycleAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
public class TransientLifecycleAttribute : Attribute
{
}

And I've assiged its to my classes:

public interface ISession   {       int? UserId {get;set;}  }

[SingletonLifecycle]
public class Session : ISession {       public int? UserId {get;set;}   }   

public interface IBookService { ... }

[TransientLifecycle]
public class BookService : IBookService { public BookService(ISession session) { ... }  }

Then I have modified my convention:

public class BasicConvention : ConfigurableRegistrationConvention
{
    public override void Process(Type type, Registry registry)
    {
        if (!type.IsConcrete()) return;

        var pluginType = FindPluginType(type);
        if (pluginType != null && type.HasConstructors())
        {
            var ci = registry.For(pluginType).Use(type);
            if (type.GetCustomAttributes(true).FirstOrDefault(a => a is TransientLifecycleAttribute) != null)
                ci.Transient();
            if (type.GetCustomAttributes(true).FirstOrDefault(a => a is SingletonLifecycleAttribute) != null)
                ci.Singleton();             
            ConfigureFamily(registry.For(pluginType));
        }
    }

    public virtual Type FindPluginType(Type concreteType)
    {
        var interfaceName = "I" + concreteType.Name;
        return concreteType.GetInterfaces().FirstOrDefault(t => t.Name == interfaceName);
    }
}

I think all is now fine and better ;) Thanks Steve!