1

I'm new to MEF and have some trouble figuring it out. I want to create and ASP.NET MVC application that supports several user stores dynamically. I thought I could use MEF for this. I defined the following contract.

public interface IUserProvider
{
    List<ConfigurationOption> SupportedConfigurationKeys { get; }

    void Start(List<ConfigurationOption> configuration);

    List<UserInfo> GetUsers(UserInfo userInfo);

    UserInfo GetUser(string id);

    UserInfo Save(UserInfo userInfo);

    bool Delete(UserInfo userInfo);

    List<UserProperty> SupportedUserProperties { get; }
}

I implemented this two times. Creating a catalog and composing like so

[ImportMany(typeof(IUserProvider))]
public IEnumerable<Lazy<IUserProvider, IDictionary<string, object>>> UserProviders { get; set; }
ApplicationCatalog userProviderCatalog = new ApplicationCatalog();
CompositionContainer container new CompositionContainer(userProviderCatalog);
container.ComposeParts(this);

I can create an instance like so:

UserProviders.Where(x => x.Metadata.ContainsKey("SystemName") && x.Metadata["SystemName"].ToString() == "ActiveDirectory").FirstOrDefault();

After creation I would configure the provider and use it. But I do not want to repeat these steps for every request. So the question arises.

How do you make the catalog container and all that available application wide? All I can find are controller examples where the references are kept by controllerfactories, but that is not relevant this time. Another use case would be content plugins for working with different file types. I would not want to compose them for every request too. I would want to do this at application_start and keep them around. I thought about a static class but how to combine that with container.compose ?

tereško
  • 58,060
  • 25
  • 98
  • 150
zu1b
  • 422
  • 5
  • 11

1 Answers1

0

I'm also in the process of learning MEF, and your question intrigued me enough to mess around with some sample code. I made myself a Console app and a Dll project like so:

Dll Project:

namespace ClassLibrary1
{
    [Export(typeof(IPlugIn))]
    [ExportMetadata("Name", "TestPlugIn")]
    public class PlugIn : IPlugIn
    {
        private string _myStringVal;

        public string GetStringVal()
        {
            return _myStringVal;
        }

        public void SetStringVal(string val)
        {
            _myStringVal = val;
        }
    }
}

Console App:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var holder1 = new PlugInHolder();
            var plugInInstance1 = holder1.PlugIns.FirstOrDefault().Value;
            plugInInstance1.SetStringVal("blarg");

            var holder2 = new PlugInHolder();
            var plugInInstance2 = holder2.PlugIns.FirstOrDefault().Value;
            var stringVal = plugInInstance2.GetStringVal();
        }
    }

    public class PlugInHolder
    {
        [ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)]
        public IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns;

        private static CompositionContainer _container;

        private void ComposeMe()
        {
            if (_container == null)
            {
                var catalog = new AggregateCatalog();
                catalog.Catalogs.Add(new DirectoryCatalog(System.AppDomain.CurrentDomain.BaseDirectory));
                _container = new CompositionContainer(catalog);
            }

            _container.ComposeParts(this);
        }

        public PlugInHolder()
        {
            ComposeMe();
        }
    }

    public interface IPlugIn
    {
        string GetStringVal();
        void SetStringVal(string val);
    }

    public interface IPlugInMetadata
    {
        string Name { get; }
    }
}

When I run this, both of the "holder" objects end up holding the same instance of PlugIn, because the same container was used to make both, and the CreationPolicy is set to Shared, which means it always uses a single instance when composing the class. This means that I could in theory "set up" the PlugIn in one holder class and use it again in another (as you can see, I'm using dorky string getter/setters as a stand in for setting up a membership database connection or whatever you might want to do).

I read in another post (Thread safety and the MEF CompositionContainer) that the containers aren't threadsafe, so I tried this solution with a static catalog, and a per-instance CompositionContainer, but that resulted in separate instances of the PlugIn between "holder" class instances. That's to be expected, I assume, since the containers wouldn't be connected in any way, despite using the same catalog.

It seems to me, though, that you could architect a controller base class with a static CompositionContainer that you've made threadsafe with some type of locking mechanism. This container could then be used to compose the individual controllers with the same instance of a membership service via the shared creation policy.

I have no idea if this violates one or more tenets of MEF architecture of course, so people should feel free to correct me.

Community
  • 1
  • 1
RedBrogdon
  • 5,113
  • 2
  • 24
  • 31
  • Your observation is interesting, but does not hold a solution for me. The problem is that I'm using a nonshared creationpolicy. I do that because I need to have several instances of a single plugin with multiple configurations/setting. As in an application that controls several locks of different types, but some (door)locks might be of the same type but with different locations. For now I just use a singleton class that gets initialized at application start. Seems to work for now. – zu1b Feb 14 '13 at 15:42