问题
I am using StructureMap 4.6 as my IoC Container. I am a bit confused about its lifecycles. As I have read in its documentation, Transient will create a single instance of the object per container. Supported Lifecycles
I am checking this scenario by creating a simple Console Application project. My code is as below:
Program.cs
class Program
{
private static IContainer _Container;
static void Main(string[] args)
{
_Container = Container.For<ConsoleRegistry>();
var serv1 = _Container.GetInstance<IFileService>();
Console.WriteLine($"Name: {_Container.Name}");
Console.WriteLine(serv1.GetUniqueID());
var serv2 = _Container.GetInstance<IFileService>();
Console.WriteLine($"Name: {_Container.Name}");
Console.WriteLine(serv2.GetUniqueID());
Console.ReadKey();
}
}
ConsoleRegistry.cs
public class ConsoleRegistry : Registry
{
public ConsoleRegistry()
{
Scan(_ =>
{
_.TheCallingAssembly();
_.WithDefaultConventions();
});
}
}
IFileSerivce.cs
public interface IFileService
{
string Read(string path);
void Write(string path, string content);
bool FileExists(string path);
string GetUniqueID();
}
FileService.cs
public class FileService : IFileService
{
private static int instanceCounter;
private readonly int instanceId;
public FileService()
{
this.instanceId = ++instanceCounter;
Console.WriteLine("File Service is Created.");
}
public int UniqueID
{
get { return this.instanceId; }
}
public string GetUniqueID()
{
return UniqueID.ToString();
}
public string Read(string path)
{
return File.ReadAllText(path);
}
public void Write(string path, string content)
{
File.WriteAllText(path, content);
}
public bool FileExists(string path)
{
return File.Exists(path);
}
}
When I run the application the result is:
My question is when I resolve an instance of IFileService
, I have expected to get a single instance of FileService
per container. But, as you can see it gives two different instances. Why is that the case?
回答1:
Your understanding of the documentation is incorrect.
- Transient -- The default lifecycle. A new object is created for each logical request to resolve an object graph from the container.
- Singleton -- Only one object instance will be created for the container and any children or nested containers created by that container
You are using Transient, which means you get an instance every time Resolve
is called.
But the behavior you are describing is for Singleton, which means to create the instance only the first time Resolve
is called.
To get the behavior you want, you have to change the registration type to Singleton.
public class ConsoleRegistry : Registry
{
public ConsoleRegistry()
{
Scan(_ =>
{
_.TheCallingAssembly();
_.With(new SingletonConvention<IFileService>());
_.WithDefaultConventions();
});
}
}
internal class SingletonConvention<TPluginFamily> : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (!type.IsConcrete() || !type.CanBeCreated() || !type.AllInterfaces().Contains(typeof(TPluginFamily))) return;
registry.For(typeof(TPluginFamily)).Singleton().Use(type);
}
}
Reference: How can I configure Structuremap to auto scan type in Assembly and Cache by Singleton?
Singleton VS Transient Example
The easiest way to think about this is by showing an example without using a DI container.
Services
Here we have a service and an application service. The only real difference here is that the application service is intended to be the entire application graph.
public class Service
{
public string Name { get; private set; } = Guid.NewGuid().ToString();
}
public class Application
{
private readonly Service singleton;
private readonly Service transient;
public Application(Service singleton, Service transient)
{
this.singleton = singleton;
this.transient = transient;
}
public Service Singleton { get { return singleton; } }
public Service Transient { get { return transient; } }
}
Container
In our container, we register 2 instances of Service
, one singleton and one transient. The singleton is only instantiated once per container instance. The transient is instantiated every time Resolve
is called.
public class MyContainer
{
private readonly Service singleton = new Service();
public Application Resolve()
{
return new Application(
singleton: this.singleton,
transient: new Service());
}
}
Usage
In a real world application, there would only be one instance of Application
. However, we are showing two Application
instances to demonstrate that a service registered as Singleton
will be the same instance for the same container instance. A transient will be created each time Resolve
is called.
class Program
{
static void Main(string[] args)
{
var container = new MyContainer();
var application1 = container.Resolve();
var application2 = container.Resolve();
Console.WriteLine($"application1.Transient.Name: {application1.Transient.Name}");
Console.WriteLine($"application2.Transient.Name: {application2.Transient.Name}");
Console.WriteLine();
Console.WriteLine($"application1.Singleton.Name: {application1.Singleton.Name}");
Console.WriteLine($"application2.Singleton.Name: {application2.Singleton.Name}");
Console.ReadKey();
}
}
Output
application1.Transient.Name: dc134d4d-75c8-4f6a-a1a5-367156506671 application2.Transient.Name: f3012ea2-4955-4cfa-8257-8e03a00b1e99
application1.Singleton.Name: 86d06d7d-a611-4f57-be98-036464797a41 application2.Singleton.Name: 86d06d7d-a611-4f57-be98-036464797a41
来源:https://stackoverflow.com/questions/49156716/confusion-with-structuremap-4-6-transient-lifecycle