Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the MEF method to get an existing exported value / object (not get Or Create)?

Tags:

c#-4.0

mef

Here's how I would want it work

container.GetButDoNotCreate<T>(); // should throw (or can return null) if container doesn't contain an instance matching the contract

var x = container.GetExportedValue<T>();

var y = container.GetButDoNotCreate<T>(); // should return the object created in previous step

Assert.That(x).IsSameAs(y);

The difference is that this method won't create an instance if the container doesn't contain it. It is a pure-get i.e. get me this object if it exists in the container. I need it for my tests where I do not want the test code to create objects in the production container (if they are not created) just use existing ones. Only production code should add/remove objects to the container.

Posted to MEF codeplex forum but no responses. So hoping someone on SO might have an answer. Also if I'd need to write that function as an extension method... that'd be fine as an answer too.

like image 713
Gishu Avatar asked Nov 14 '22 08:11

Gishu


1 Answers

Here is my solution. At first I tried creating an extension method. I tried several things, read the doc and explored the available properties, events and methods on the container and catalog, but I couldn't make anything work.

After thinking about the problem, the only way I can come up with a solution is to create a derived container that is based on CompositionContainer and implements the GetButDoNotCreate method.

Update: I realized after posting it that the solution only works in the simple example you have posted where only GetExportedValue is used to retrieve parts simple parts. Unless you use the container as a simple service locator for parts with no [Import]s that will not do when parts with [Import]s are created.

Here is the implementation:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;

namespace GetButNotCreate
{
    public class CustomContainer : CompositionContainer
    {
        private List<Type> composedTypes = new List<Type>();

        public CustomContainer(ComposablePartCatalog catalog)
            : base(catalog) 
        {
        }

        public new T GetExportedValue<T>()
        {
            if (!composedTypes.Contains(typeof(T)))
                composedTypes.Add(typeof(T));

            return base.GetExportedValue<T>();
        }

        public T GetButDoNotCreate<T>()
        {
            if (composedTypes.Contains(typeof(T)))
            {
                return base.GetExportedValue<T>();
            }

            throw new Exception("Type has not been composed yet.");
        }
    }
}

It works by overriding the GetExportedValue method to keep track of the types that have been composed thus far and then using this to check for type composition in GetButNotCreate. I throwed an exception like you mentioned in your question.

Of course, you might need to override the overloads of GetExportedValue (unless you don't use them, but even so, I would override them anyway for safety) and maybe add other constructors and stuff if you use the class. In this example, I did the minimum to get it to work.

Here are the unit tests that test the new method:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace GetButNotCreate
{
    public interface IInterface { }

    [Export(typeof(IInterface))]
    public class MyClass : IInterface 
    {
    }

    [TestClass]
    public class UnitTest1
    {      
        [TestMethod]
        [ExpectedException(typeof(Exception), "Type has not been composed yet.")]
        public void GetButNotCreate_will_throw_exception_if_type_not_composed_yet()
        {
            var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly);
            CustomContainer container = new CustomContainer(catalog);
            container.ComposeParts(this);

            var test = container.GetButDoNotCreate<IInterface>();
        }

        [TestMethod]
        public void GetButNotCreate_will_return_type_if_it_as_been_composed()
        {
            var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly);
            CustomContainer container = new CustomContainer(catalog);
            container.ComposeParts(this);

            var x = container.GetExportedValue<IInterface>();
            var y = container.GetButDoNotCreate<IInterface>();

            Assert.IsNotNull(y);
            Assert.AreEqual(x, y);
        }
    }
}

It shows that the GetButNotCreate will throw an exception if the type has never been exported and that it will return this type if it has already been imported.

I couldn't find any hooks anywhere to check (without resorting to reflection) to see if MEF has composed a part, so this CustomContainer solution would be my best bet.

like image 145
Gilles Avatar answered Dec 26 '22 01:12

Gilles