带有MEF的ASP.NET MVC中的应用程序范围的部分



我是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();

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

如何使目录容器和所有可用的应用程序范围?我所能找到的只是控制器示例,其中引用由controlleractories保存,但这次与此无关。另一个用例是用于处理不同文件类型的内容插件。我不想为每一个请求都编写它们。我想在application_start上做这件事,并把它们留在身边。我想过一个静态类,但如何将其与container.compose结合起来?

我也在学习MEF,你的问题引起了我的兴趣,以至于我对一些示例代码很感兴趣。我给自己做了一个控制台应用程序和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,这意味着它在编写类时总是使用一个实例。这意味着,理论上我可以在一个holder类中"设置"PlugIn,并在另一个类中再次使用它(正如你所看到的,我正在使用愚蠢的字符串getter/setters作为建立成员数据库连接或任何你想做的事的替代)。

我在另一篇文章(线程安全和MEF CompositionContainer)中读到,这些容器是不线程安全的,所以我尝试了这个解决方案,使用静态目录和每个实例的CompositionContainer,但这导致了"holder"类实例之间的插件的单独实例。我想这是意料之中的事,因为尽管使用了相同的目录,但容器不会以任何方式连接。

不过,在我看来,你可以用一个静态CompositionContainer来构建一个控制器基类,你已经用某种类型的锁定机制实现了线程安全。然后,可以使用该容器通过共享创建策略将各个控制器与成员服务的相同实例组合在一起。

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

最新更新