使用MEF在ASP.NET MVC中应用程序作用域部分

时间:2013-02-12 13:22:07

标签: asp.net-mvc mef

我是MEF的新手并且在搞清楚方面遇到了一些麻烦。我想创建和动态支持多个用户存储的ASP.NET MVC应用程序。我以为我可以使用MEF。我定义了以下合同。

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; }
}

我实施了两次。创建目录并像这样编写

[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);

我可以像这样创建一个实例:

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

创建后,我将配置提供程序并使用它。但我不想为每个请求重复这些步骤。所以问题出现了。

如何制作目录容器和所有可用的应用程序?我能找到的只是控制器示例,其中引用由控制器实例保存,但这次不相关。另一个用例是用于处理不同文件类型的内容插件。我也不想为每个请求撰写它们。我想在application_start中执行此操作并保留它们。我想过一个静态类,但是如何将它与container.compose结合起来?

1 个答案:

答案 0 :(得分:0)

我也在学习MEF,你的问题引起了我的兴趣,足以搞砸一些示例代码。我自己创建了一个Console应用程序和一个Dll项目:

Dll项目:

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;
        }
    }
}

控制台应用

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; }
    }
}

当我运行它时,两个“holder”对象最终都持有相同的PlugIn实例,因为同一个容器用于制作两者,而CreationPolicy设置为Shared,这意味着它总是使用单个实例在编写课程时。这意味着我理论上可以在一个持有者类中“设置”PlugIn并在另一个持有者类中再次使用它(正如你所看到的,我使用dorky字符串getter / setter作为建立成员资格数据库连接的支持或者无论你想做什么)。

我在另一篇文章(Thread safety and the MEF CompositionContainer)中读到容器不是线程安全的,所以我尝试使用静态目录和每个实例的CompositionContainer来解决这个问题,但结果是在“holder”类实例之间的PlugIn的单独实例中。我认为,这是可以预期的,因为尽管使用相同的目录,容器也不会以任何方式连接。

但在我看来,您可以使用静态CompositionContainer构建一个控制器基类,并使用某种类型的锁定机制创建线程安全。然后,可以使用此容器通过共享创建策略将具有相同成员资格服务实例的各个控制器组合在一起。

我当然不知道这是否违反了MEF架构的一个或多个原则,所以人们应该随意纠正我。