DCSIMG
October 2010 - Posts - Bnaya Eshet

Bnaya Eshet

Disclaimer

October 2010 - Posts

Exporting non Exportable types

Exporting non Exportable types

MEF, Compose, Import, Export, Poco this post extend Glenn Block's post about

"Poco, Mef, and custom type systems. Are you ready to take the red pill?" 

the post is adding a compile time attribute export model, Directory catalog

and migrate Glenn code to VS 2010.

It is very recommended to read Glenn Block's post before reading

this one.

 

the code sample for this post can be download from here.

 

Summarizing Glenn's post

in general Glenn show how to add attributes (like Export) to 3rd party type

which doesn't support it (it is cool technique which doesn't related directly to MEF).

Glenn show how to write catalog which can be aware of types extension.

 

What was added?

I took Glenn's source, fix it to run against the CLR 4.0,

then I added a directory catalog which aware of the

type extension and I also add a nice clean way of exposing the type

by using assembly level attribute.

 

the directory catalog is very straight forward so I wont cover it in the post

(you can download the source and see the implementation).

 

the following code show how to use the assembly level attribute:

Code Snippet
  1. [assembly: ComposablePartRegistry(typeof(MyNoneExportedType), typeof(IMyNoneExportedType))]

as you can see, all you have to do is to declare the exported type followed by the exported contract.

 

the attribute snippet:

Code Snippet
  1. [AttributeUsage (AttributeTargets.Assembly)]
  2. public class ComposablePartRegistryAttribute : Attribute
  3. {
  4.     public ComposablePartRegistryAttribute(
  5.         Type implementation, Type contract,
  6.         CreationPolicy policy = CreationPolicy.Any)
  7.     {
  8.         Type t = typeof (ComposablePartType<,>);
  9.         t = t.MakeGenericType(new[] {implementation, contract});
  10.         Type = Activator.CreateInstance(t, policy) as TypeDelegator;
  11.     }
  12.  
  13.     public TypeDelegator Type { get; private set; }
  14. }

line 8-10: creating ComposablePartType type which later be used by the catalog.

 

Summary

this was small extension to Glenn's concept which can be very useful

in cases where you want to embrace 3rs party component into your MEF model.

 

kick it on DotNetKicks.com Shout it
Digg This

Testing and Debugging MEF, avoiding misconceptions - Part 3

Testing and Debugging MEF, avoiding misconceptions - Part 3

MEf, Import, Export, Compose, extension, extensibilitythis is the 3rd post of this series and it will

discuss common misconceptions which may lead unexpected behavior and long

debugging nights.

 

Initialization issues

the first misconception occurs when developer are trying to access

imported property at construction time.

NullReferenceException
  1. [Export]
  2. public class MyPlugin
  3. {
  4.     public MyPlugin()
  5.     {   // the logger import doesn't satisfied yet
  6.         // the next line will result with NullReferenceException
  7.         Logger.Write("MyPlugin initialized");
  8.     }
  9.  
  10.     [Import]
  11.     public ILogger Logger { get; set; }
  12. }

MyPlugin instantiation occur when the MEF container composed,

the composition process will create an instance and only then it will try

to satisfy its imports. which mean that at the construction time the imports does not yet satisfied.

the above code will result with NullReferenceException.

 

What can we do about it?

we can have a few solutions solving this issue:

  • we can use ImportingConstructor (but we should use it carefully because
    cross import constructor may lead to deadlock and rejection
    ).
ImportingConstructor
  1. [Export]
  2. public class MyPlugin
  3. {
  4.     // cross import constructor can lead to deadlock an rejection
  5.     [ImportingConstructor]
  6.     public MyPlugin(ILogger logger)
  7.     {  
  8.         logger.Write("MyPlugin initialized");
  9.     }
  10. }

this way the logger will be instantiate before the instantiation of MyPlugin.

  • we can implement IPartImportsSatisfiedNotification (which is my favorite technique because there is no dead lock risk).
    this way we can initialize our component right after all imports has been satisfied.

IPartImportsSatisfiedNotificat
  1. [Export]
  2. public class MyPlugin: IPartImportsSatisfiedNotification
  3. {
  4.     public void OnImportsSatisfied()
  5.     {
  6.         // happens just after the imports satisfaction
  7.         Logger.Write("MyPlugin initialized");
  8.     }
  9.  
  10.     [Import]
  11.     public ILogger Logger { get; set; }
  12. }

MEF infrastructure will call OnImportsSatisfied right after it done with the composition process.

 

None composed instance issue

developers are used to instantiate their own type using the new keyword (var plugin = new MyPlugin()).

doing so wouldn't satisfy any of the imported properties (because the type doesn't took part in any composition), because MEF does not aware of the instantiation.

How can we do it right?
  • we can introduce the instance to the composition by using ComposePart.
    the compose part will satisfy any of the instance imports.
ComposeParts
  1. [TestMethod]
  2. public void GetInstanceFromContainerTest()
  3. {
  4.     var catalog = new AssemblyCatalog(typeof(ILogger).Assembly);
  5.     var container = new CompositionContainer(catalog);
  6.  
  7.     var plugin = new MyPlugin(); ;
  8.     container.ComposeParts(plugin);
  9.  
  10.     Assert.IsNotNull(plugin.Logger);
  11. }
  12.  
  13. public interface ILogger
  14. {
  15.     void Write(string content);
  16. }
  17.  
  18. [Export(typeof(ILogger))]
  19. public class MyLogger:ILogger
  20. {
  21.     public void Write(string content)
  22.     {
  23.         Debug.WriteLine(content);
  24.     }
  25. }
  26.  
  27. [Export]
  28. public class MyPlugin
  29. {
  30.     [Import]
  31.     public ILogger Logger { get; set; }
  32. }
  • other solution is getting our instance dynamically from
    the container (for example by using GetExportedValue)
GetExportedValue
  1. [TestMethod]
  2. public void GetInstanceFromContainerTest()
  3. {
  4.     var catalog = new AssemblyCatalog(typeof(ILogger).Assembly);
  5.     var container = new CompositionContainer(catalog);
  6.     container.Compose(new CompositionBatch());
  7.  
  8.     MyPlugin plugin = container.GetExportedValue<MyPlugin>();
  9.  
  10.     Assert.IsNotNull(plugin);
  11.     Assert.IsNotNull(plugin.Logger);
  12. }
  13.  
  14. public interface ILogger
  15. {
  16.     void Write(string content);
  17. }
  18.  
  19. [Export(typeof(ILogger))]
  20. public class MyLogger:ILogger
  21. {
  22.     public void Write(string content)
  23.     {
  24.         Debug.WriteLine(content);
  25.     }
  26. }
  27.  
  28. [Export]
  29. public class MyPlugin
  30. {
  31.     [Import]
  32.     public ILogger Logger { get; set; }
  33. }
  • using Silverlight we can initialize our container
    by using CompositionHost.Initialize(catalog). and we can add the
    following line to the constructor:
    CompositionInitializer.SatisfyImports(this), which mean that
    each time we are creating new instance it will try to satisfy it's own imports.
    (be aware that you must use CompositionHost.Initialize(catalog) before using
    CompositionInitializer.SatisfyImports(this))

SatisfyImports
  1. public class MyPlugin
  2. {
  3.     public MyPlugin()
  4.     {
  5.         CompositionInitializer.SatisfyImports(this);
  6.     }
  7.  
  8.     [Import]
  9.     public ILogger Logger { get; set; }
  10. }

if you want to use the same technique for none Silverlight components,

you can download the code for System.ComponentModel.Composition.Initialization.dll

which hold CompositionHost.Initialize and CompositionInitializer.SatisfyImports

from here.

Exporting the wrong type issue

last issue is to avoid exporting or importing the wrong type.

MEF does not goes through the inheritance list,

MEF contract are explicit and doesn't aware of the inheritance chain.

the following code snippet export the MyLogger (not the ILogger).

Code Snippet
  1. [Export]
  2. public class MyLogger:ILogger {
  3.     ...
  4. }

it is equivalent to:

Code Snippet
  1. [Export(typeof(MyLogger))]
  2. public class MyLogger:ILogger {
  3.     ...
  4. }

while what you may really wanted is:

Code Snippet
  1. [Export(typeof(ILogger))]
  2. public class MyLogger:ILogger {
  3.     ...
  4. }

the following import won't get the first 2 snippet because of the explicit contract comparison:

Code Snippet
  1. [Import]
  2. public ILogger Logger { get; set; }

 

Summary

MEF instantiation occurs at runtime and sometime it is not

so easy or straight forward for debug.

avoiding common misconceptions may save us long debugging hours.

 

Read more

I strongly recommend the following post which discuss other points of failure:

 

kick it on DotNetKicks.com Shout it
Digg This

The hidden assumption of EF 4 self-tracking entity

The hidden assumption of EF 4 self-tracking entity

EF, Entity Framework, Self tracking

recently I was diving into the Entity Framework self-tracking entity T4 and its generated code.

doing so I was finding some interesting assumptions made about the entity behaviors.

this post will illuminate some of those assumptions.

 

Background

EF self tracking entity is one of the major enhancement of EF 4.0,

it is a disconnected model (similar to the old DataSet) which enable tracking

the entity state (Added, Modified, Deleted and Unchanged) while the entity

is detached from the EF ObjectContext.

the code snippet of this post will refer the following very simple edmx:

EF, Entity Framework, Self tracking 

Assumptions
1st assumption

by default the tracking is disable (ChangeTrackingEnabled = false).

 

2nd assumption

trying to change the entity state while the tracking is disable

Code Snippet
  1. var p = new Person();
  2. p.ChangeTracker.State = ObjectState.Unchanged;

will have no affect on the entity state (the state will remain Added which is the default

state for new entities).

the following snippet shows the code of the State property:

T4 generated code
  1. public ObjectState State
  2. {
  3.     get { return _objectState; }
  4.     set
  5.     {
  6.         if (_isDeserializing || _changeTrackingEnabled)
  7.         {
  8.             OnObjectStateChanging(value);
  9.             _objectState = value;
  10.         }
  11.     }
  12. }
 
3rd assumption

the entity enable the tracking after deserialization.

T4 generated code
  1. [OnDeserialized]
  2. public void OnDeserializedMethod(StreamingContext context)
  3. {
  4.     IsDeserializing = false;
  5.     ChangeTracker.ChangeTrackingEnabled = true;
  6. }

 

4th assumption

MarkAsAdded, MarkAsModified, MarkAsDeleted and MarkAsUnchanged all enable the trucking.

T4 generated code
  1. public static T MarkAsAdded<T>(this T trackingItem) where T : IObjectWithChangeTracker
  2. {    
  3.     trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
  4.     trackingItem.ChangeTracker.State = ObjectState.Added;
  5.     return trackingItem;
  6. }

 

5th assumption

State = ObjectState.Unchanged, MarkAsUnchanged and AcceptChanges

has different behaviors:

in general State property: is only changing when tracking is enabled,

MarckAsUnchanged: enable the tracking and

AcceptChanges: clear the original values, enable the tracking and

reset the relationship tracking (added and removed related entities).

State: T4 generated code
  1. public ObjectState State
  2. {
  3.     get { return _objectState; }
  4.     set
  5.     {
  6.         if (_isDeserializing || _changeTrackingEnabled)
  7.         {
  8.             OnObjectStateChanging(value);
  9.             _objectState = value;
  10.         }
  11.     }
  12. }

 

MarkAsUnchanged: T4
  1. public static T MarkAsUnchanged<T>(this T trackingItem) where T : IObjectWithChangeTracker
  2. {    
  3.     trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
  4.     trackingItem.ChangeTracker.State = ObjectState.Unchanged;
  5.     return trackingItem;
  6. }

 

AcceptChang: T4 generated code
  1. public void AcceptChanges()
  2. {
  3.     OnObjectStateChanging(ObjectState.Unchanged);
  4.     OriginalValues.Clear();
  5.     ObjectsAddedToCollectionProperties.Clear();
  6.     ObjectsRemovedFromCollectionProperties.Clear();
  7.     ChangeTrackingEnabled = true;
  8.     _objectState = ObjectState.Unchanged;
  9. }

 

Summary

the self tracking entity is a fairly young concept in the EF world

and we can expect that it will keep changing.

as I was reading in one of the Harry Potter's book

"do not trust anything you cannot understand", so be aware of that

assumption and use it well.

 

kick it on DotNetKicks.com Shout it
Digg This

Testing and Debugging MEF, Tips - Part 2

Testing and Debugging MEF, Tips - Part 2

this is the second post of a series that will offer some tips

about testing and debugging your MEF-able component and application.

 

in this post we will focus about debugging the most common, and most confusing,

MEF failure.

 

Assembly Loading Failure

the most common MEF runtime failure occurs because of missing assembly

which contain the MEF parts (Import, Export) or

the parts dependencies (like 3rd party components).

 

under the hood MEF is using reflection, which mean that MEF loading

behavior is similar to the reflection dynamic loading.

 

Tracing Loading Failure?

I will suggest 2 option for tracing the failure:

 

1. we can register to assembly resolve event (AppDomain.CurrentDomain.AssemblyResolve)

Code Snippet
  1. AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolveHandler;
  2. static Assembly OnAssemblyResolveHandler(object sender, ResolveEventArgs args)
  3. {
  4.     Logger.WriteLine("Assembly Resolve: " + args.Name);
  5.  
  6.     return args.RequestingAssembly;
  7. }

the problem with that approach is that we may get some smoke events which

actually succeed (because of the .NET probing).

 

2. catching ReflectionTypeLoadException exception

Code Snippet
  1. try
  2. {
  3.     container.ComposeParts(p);
  4. }
  5. catch (ReflectionTypeLoadException ex)
  6. {
  7.     foreach (Exception exc in ex.LoaderExceptions)
  8.     {
  9.         Logger.WriteLine(exc.Message);
  10.     }
  11. }

we can wrap the composition using try catch scope.

and catch ReflectionTypeLoadException. this will

give us pretty good idea what's got wrong.  

 

Summary

my suggestion is whenever possible, wrapping any composition and catch the ReflectionTypeLoadException, in other cases we can use the first technique.

 

Credits

Ohad Schneider should have credit for part of the code in this post.

kick it on DotNetKicks.com Shout it
Digg This