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?
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.
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!