MEF and ASP.NET MVC

帅比萌擦擦* 提交于 2019-12-03 13:24:56

Your technique here is pretty solid, and will work even in partial trust. The nerd dinner MEF example has extensions that will allow you to deal with discovering controllers by convention and making them into MEF exports automatically, without flagging them with MEF attributes. But managing part catalogs directly doesn't work in partial trust, so the nerd dinner MEF techniques don't work in partial trust.

If you are working in full trust, and want convention based discovery with your controllers start with the Nerd Dinner MEF example, but you should probably also read about a couple major issues with the nerd dinner MEF example that will come up if your own application's model is in a separate class a library project. I blogged about those cases and suggested some fixes.

If you aren't all that interested in the convention based discovery stuff, then nerd dinner sample is a tad over-engineered. Your solution is probably fine as is... and works in partial trust too, which is always a bonus.

[update] I did spot one potential problem with your technique:

var controllerExport = controllers.Where(x => x.Value.GetType() == 
controllerType).FirstOrDefault();

In the where clause here, you are calling .Value on each export in the collection of parts... that is actually going to cause each of those exports to be composed and instantiated in order to be evaluated. That could be a nasty performance issue.

You might consider decorating your controllers with named export contracts like this:

[Export("Home", typeof(IController))]

Then using a controler factory like this instead:

public class MefControllerFactory: IControllerFactory
{
    private CompositionContainer _Container;

    public MefControllerFactory(Assembly assembly)
    {
        _Container = new CompositionContainer(new AssemblyCatalog(assembly));
    }

    #region IControllerFactory Members

    public IController CreateController(RequestContext requestContext, string controllerName)
    {

        var controller = _Container.GetExportedValue<IController>(controllerName);

        if (controller == null)
        {
            throw new HttpException(404, "Not found");
        }

        return controller;
    }

    public void ReleaseController(IController controller)
    {
       // nothing to do
    }

    #endregion
}

I decided to return to Unity.

I've create a custom attribute instead of MEF's ExportAttribute

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ImplementsAttribute : Attribute
{
    public ImplementsAttribute(Type contractType)
    {
        ContractType = contractType;
    }

    public Type ContractType
    {
        get; 
        private set;
    }
}

Example:

[Implements(typeof(ICustomerEmailService))]
public class CustomerEmailService : ICustomerEmailService
{...}

And a custom controller factory:

public class MyControllerFactory : DefaultControllerFactory
{
    private readonly IUnityContainer _container;

    public MyControllerFactory()
    {
        _container = new UnityContainer();

        Func<Type, bool> isController =
            (type) => typeof(IController).IsAssignableFrom(type)
                    && (type.IsAbstract || type.IsInterface 
                        || type.GetCustomAttributes(typeof(GeneratedCodeAttribute), true).Any()) != true;



        foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
        {
            try
            {
                var types = assembly.GetTypes();

                // Also register all controllers
                var controllerTypes = from t in types where isController(t) 
                                        select t;


                foreach (Type t in controllerTypes)
                {
                    _container.RegisterType(t);
                }


                // register all providers
                var providers =
                    from t in types
                    from export in t.GetCustomAttributes(typeof(ImplementsAttribute), true).OfType<ImplementsAttribute>()
                    select new { export.ContractType, Provider = t };

                foreach (var item in providers)
                {
                    if (item.ContractType != null)
                    {
                        _container.RegisterType(item.ContractType, item.Provider);
                    }
                    else
                    {
                        _container.RegisterType(item.Provider);
                    }
                }
            }
            catch 
            {
            }
        }
    }



    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType != null)
        {
            var controller = _container.Resolve(controllerType) as IController;

            if (controller == null)
            {
                return base.GetControllerInstance(requestContext, controllerType);
            }

            return controller;
        }


        throw new HttpException((Int32)HttpStatusCode.NotFound,
            String.Format(
                "The controller for path '{0}' could not be found or it does not implement IController.",
                requestContext.HttpContext.Request.Path)
        );
    }
}

It was too difficult for me to fix all problems for MEF's controller factory :(

I've been working with MEF/MVC a lot recently and have been blogging about my revised MEF+MVC design. I'm hoping to push it onto CodePlex soon, but for now, see if any of this helps you out:

  1. Modular ASP.NET MVC using the Managed Extensibility Framework (MEF), Part One
  2. Modular ASP.NET MVC using the Managed Extensibility Framework (MEF), Part Two

Thanks.

I did spot one potential problem with your technique:

var controllerExport = controllers.Where(x => x.Value.GetType() == controllerType).FirstOrDefault();

Yes, it is true.

After reading this (http://codepaste.net/yadusn) I understood how NerdDinner with MEF was done.

I used the conventional catalog for MEF and created my MEFed controller factory (without Export attribute on controllers).

 public static IController GetController(CompositionContainer container, Type controllerType)
    {
        var controllers = container.GetExports<IController, IDictionary<string, object>>();

        if (controllers == null) return null;

        var controllerExport = controllers
            .Where(exp => ExportMetadataContainsGuid(exp.Metadata, controllerType.GUID))
            .FirstOrDefault();

        return (controllerExport == null) ? null : controllerExport.Value;
    }

ExportMetadataContainsGuid method:

public static bool ExportMetadataContainsGuid(IDictionary<string, object> metaData, Guid guid)
    {
        return metaData.ContainsKey(MetadataGuidKey) && guid == (Guid)metaData[MetadataGuidKey];
    }

I use metadata to store type's GUID and use it to find right controller.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!