Microsoft.Composition (Portable MEF): Scoping

2016/03/06

one comment

 

Microsoft.Composition (Portable MEF): Scoping

This post is part of mini series, which complete the full MEF series.

This post will go over Microsoft.Composition Scoping.

 

What is IoC scoping?

IoC scoping is the ability to control the lifetime of registration

per specific execution scope. whatever resolved in the scope can resolve

it’s item that register under the scope or at its parent container.

 

Classic example for such scope is request.

You may want to resolve the same instance again and again within

the request scope while keeping it completely isolated from other requests.

When supporting this kind of scope you can use the IoC safely without

potential side effects.

 

How to register scope with Portable MEF?

The topology for this sample will be a process which will resolve

request object (under a scope).

Each Request will resolve :

Global object (which register as singleton)

Items (which register under a scoped lifetime).

The Global object will have the same Id all over the execution (singleton)

while the Item will be the same under the request boundary, but different between requests.

 

The output of the sample I’m going to present will look like

the following snapshot (while the Id represent the instantiation):

let see some code.

Code Snippet
  1. var conventions = new ConventionBuilder();
  2. conventions.ForTypesDerivedFrom<IProcess>()
  3.     .Export<IProcess>()
  4.     .Shared();
  5. conventions.ForTypesDerivedFrom<IGlobal>()
  6.     .Export<IGlobal>()
  7.     .Shared();
  8. conventions.ForTypesDerivedFrom<IRequest>()
  9.     .Export<IRequest>()
  10.     .Shared("Request");
  11. conventions.ForTypesDerivedFrom<IItemPerRequest>()
  12.     .Export<IItemPerRequest>()
  13.     .Shared("Request");
  14.  
  15. var configuration = new ContainerConfiguration()
  16.         .WithAssembly(typeof(Program).Assembly, conventions);
  17.  
  18. using (var container = configuration.CreateContainer())
  19. {
  20.     var process = container.GetExport<IProcess>();
  21.     process.Execute();
  22. }

We register 4 different types, IProcess and IGlobal as shared (singleton).

IRequest and IItemPerRequest as Shared which get a scope name “Request” (lines 10, 13).

On lines (19-23) we resolve the process and calling Execute:

 

the following snippet is the Process class:

Code Snippet
  1. public class Process: IProcess
  2. {
  3.     [Import]
  4.     public IGlobal Global { get; set; }
  5.  
  6.     [Import]
  7.     [SharingBoundary("Request")]
  8.     public ExportFactory<IRequest> Request { get; set; }
  9.  
  10.     public void Execute()
  11.     {
  12.         Console.WriteLine($"Global = {Global.Id}");
  13.  
  14.         using (Export<IRequest> scopedRequest =
  15.                                 Request.CreateExport())
  16.         {
  17.             IRequest request = scopedRequest.Value;
  18.             request.Send();
  19.         }
  20.  
  21.         using (Export<IRequest> scopedRequest =
  22.                                 Request.CreateExport())
  23.         {
  24.             IRequest request = scopedRequest.Value;
  25.             request.Send();
  26.         }
  27.     }
  28. }

The scope (Request) must be declare as ExportFactory<T> (line 8) and it’s should be decorated

with the [SharingBoundary] attribute that match the registered scope (line 7).

This snippet is using 2 scoped request (lines 14-19 and 21-26).

Each request will resolve different instance (if the type registered for the scope).

 

The next snippet show the Request class.

Code Snippet
  1. public class Request : IRequest
  2. {
  3.     private static int _statId = 0;
  4.     public int Id { get; } = ++_statId;
  5.  
  6.     public void Send()
  7.     {
  8.         Console.WriteLine($"\tRequest Id = {Id}");
  9.         Console.WriteLine($"\t\tGlobal = {Global.Id}");
  10.         Console.WriteLine($"\t\tItem A: {ItemPerRequestA}");
  11.         Console.WriteLine($"\t\tItem B: {ItemPerRequestB}");
  12.     }
  13.  
  14.     [Import]
  15.     public IGlobal Global { get; set; }
  16.  
  17.     [Import]
  18.     public IItemPerRequest ItemPerRequestA { get; set; }
  19.     [Import]
  20.     public IItemPerRequest ItemPerRequestB { get; set; }
  21. }

Both properties, ItemPeeRequestA and ItemPeeRequestB will get the same instance lines 17-20).

 

finally the code for the ItemPerRequest is:

Code Snippet
  1. public class ItemPerRequest : IItemPerRequest
  2. {
  3.     private static int _statId = 100;
  4.     public int Id { get; } = _statId++;
  5.  
  6.     [Import]
  7.     public IRequest Request { get; set; }
  8.  
  9.     public override string ToString() =>
  10.         $"Id = {Id} — of request –> {Request.Id}";
  11. }

Because it’s sharing the same scope with the request object, it can resolve the request instance.

 

summary

this sample is fierily simplified but it suggesting the power of IoC scoping.

Other IoC also support scoping and I will write about it on future post.

 

The rest of the code:

Code Snippet
  1. public interface IProcess
  2. {
  3.     void Execute();
  4. }
  5. public interface IRequest
  6. {
  7.     int Id { get; }
  8.     void Send();
  9. }
  10. public interface IGlobal
  11. {
  12.     int Id { get; }
  13. }
  14. public interface IItemPerRequest
  15. {
  16.     int Id { get; }
  17. }

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. dor cohen2016/11/20 ב 12:45

    You have brought up a very great details , thanks for the post.

    Reply