MEF and MVC - Limitations and workarounds for partial trust environments

by Stephen M. Redd 8. July 2010 21:20

A while back I wrote about using MEF in MVC environments with extensions provided by Hammet for the Nerd Dinner MEF sample application. Those extensions deal with dynamic discovery of parts based on MVC conventions (instead of attributes), as well as per-request composition containers. The extensions work great, after a few modifications that I talked about in the last post... but in partial trust environments it blows up in your face!

BOOM!

I spent hours and hours digging through the code, reading about CAS, trust policies, transparent code and whole mess of other junk that I'd really rather not have rattling around my skull. Long story short -- MEF isn't friendly with partially trusted asp.net environments.

Now, you could write your custom MEF code in a class library, flag the assembly with the APTCA attribute, sign-it, and install it to the GAC if you want. That will get around these limitations neatly, but if you are running in partial trust you probably don't have the luxury of installing things to the GAC either.

The first major limitation is that you cannot access the parts collection within catalogs or containers. If you try it, your get an exception like this:

Attempt by method 'DynamicClass.lambda_method(System.Runtime.CompilerServices.Closure)' 
to access type 'System.ComponentModel.Composition.Hosting.ComposablePartCatalogCollection' failed.
  
  

The easiest way to reproduce the problem is to simply add the trust element to web.config like this:

<trust level="High"/>
  
  

Then add this to application startup in global.asax:

	var cat = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
	var parts = cat.Parts.ToArray();
  
   

In the Nerd Dinner MEF sample, this limitation effectively kills the mechanisms that slit up parts into per-request containers vs. application wide containers.

If you've done any reading about MEF online, you've likely run across code for a FilteredCatalog class. This thing is so commonly cited on the net that it seems beyond retarded that it wasn't built-in with MEF. But these limitations from partial trust kills FilteredCatalog; which the Nerd Dinner MEF sample uses heavily.

The other major area of limitation is that you cannot use ReflectionModelServices, which is needed in order to dynamically create export/import definitions programmatically. This kills the Nerd Dinner MEF sample's auto-discovery of controllers.

Despite these limitations, you can still use MEF in medium trust, but only if you are careful to keep it simple and straight forward.

Honestly, I recommend that you just use Ninject or a similar IoC/DI framework until the next version of MEF or MVC (hopefully) fixes these issues.

In my case though, I really wanted to be able to support medium trust environments and I'm too damned stubborn to give up on MEF that easy.

I'm OK with having to use the MEF attributes to decorate my controllers, so losing auto-discovery isn't much of a problem. Hammet's extensions are brilliant, but the auto-discovery mechanism is a lot of VERY complicated experimental code.

Now, the simplest thing you can do is just use a custom MVC ControllerFactory that instantiates a new MEF container on each request. That works well, and is trivially easy to implement:

public class MefControllerFactory : IControllerFactory
{
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        var catalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
        var requestContainer = new CompositionContainer(catalog);
        var controller = requestContainer.GetExportedValue(controllerName);

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

Sure, this is fine, but it sort of undermines a lot of the power of MEF. MEF's default behavior uses a singleton pattern to reuse parts that have already been instantiated, but this mechanism eliminates ALL reuse, by recombobulating the entire container on each request. It also has an appreciable performance impact since reflection has to go over and build up the entire catalog each time too.

Another solution is to just create an application wide container, and just keep the controllers from being reused by setting the PartCreationPolicy attribute to NonShared. That's a better solution, and simple to achieve too. It looks something like this:

public static class ContainerManager
{
    private static CompositionContainer _container;
    public static CompositionContainer ApplicationContainer
    {
        get
        {
            if (_container == null)
            {
                var catalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
                _container = new CompositionContainer(catalog);
            }
            return _container;
        }
    }
}

Then your controller just uses the application container from this static class. Very simple, and allows you to control reuse using MEF's standard attributes.

I actually recommend the above approach, but it bothered me to mark controllers as NonShared. It isn't that controller instances cannot be reused, it's just that in MVC they can't be reused across multiple requests.

So I came up with a more ghetto solution that can sort-of mimic a FilteredCatalog even in medium trust. This allows for a pattern more similar to the Nerd Dinner MEF sample; you can have application scoped containers, and smaller per-request containers for just for the controllers too.

It looks a little something like this:

First create a class derived from HttpApplication so you can boot-strap creating the MEF containers and catalogs on application startup:

public class MefHttpApplication : HttpApplication
{
    public static ComposablePartCatalog RootCatalog { get; private set; }
    public static CompositionContainer ApplicationContainer { get; private set; }
    public static ComposablePartCatalog ControllerCatalog { get; private set; }

    protected virtual void Application_Start()
    {
        if (RootCatalog == null){ RootCatalog = CreateRootCatalog(); }
        if (ApplicationContainer == null)
        {
            ApplicationContainer = new CompositionContainer(RootCatalog, false);
        }
        if (ControllerCatalog == null)
        {
            var controllerTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.GetInterfaces().Any(i => i == typeof(IController)));
            ControllerCatalog = new TypeCatalog(controllerTypes);
        }
        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory());
    }

    protected virtual void Application_End()
    {
        if (ApplicationContainer != null){ApplicationContainer.Dispose();}
    }

    protected virtual ComposablePartCatalog CreateRootCatalog()
    {
        return new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    }
}

On startup we create a master catalog of every part definition, and an application scoped container from that master catalog. But we also create a catalog containing just the controller parts by using a bit of reflection to pull out just controllers and shoving them into a TypeCatalog (which is built-in with MEF)... the poor man's filtered catalog!

Now just doctor up Global.asax to inherit the MefHttpApplication class:

public class MvcApplication : MefHttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
       //normal route stuff
    }

    protected override void Application_Start()
    {
        base.Application_Start();
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }
}

And finally, we need our ControllerFactory:

public class MefControllerFactory : IControllerFactory
{
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        var requestContainer = GetRequestControllerContainer(requestContext.HttpContext.Items);
        var controller = requestContainer.GetExportedValue(controllerName);

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

        return controller;
    }

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

    public static CompositionContainer GetRequestControllerContainer(IDictionary contextItemsCollection)
    {
        var app = (MefHttpApplication)HttpContext.Current.ApplicationInstance;

        if (contextItemsCollection == null) throw new ArgumentNullException("dictionary");

        var container = (CompositionContainer)contextItemsCollection["MefRequestControllerContainer"];

        if (container == null)
        {
            container = new CompositionContainer(MefHttpApplication.ControllerCatalog, false, MefHttpApplication.ApplicationContainer);
            contextItemsCollection["MefRequestControllerContainer"] = container;
        }
        return container;
    }
}

As you can see, the overall technique here is similar to that used in the Nerd Dinner MEF sample. We have a static method that we can call to build a per-request container. It stuffs the entire container into context in case its needed later. The key to the container itself is that it is built from our catalog of just controller types, and uses the application scoped MEF container as an export provider for any other parts the controllers might need to import.

In the long-run, this is probably no better than just marking our controllers as NonShared and using an application wide container, but the general concept of this technique can be applied to other situations besides just dependency injection with controllers. While you can't truly filter catalogs and manipulate part in partial trust, you can still use reflection to create specialized catalogs and achieve similar results... for the simpler cases anyway.

Tags: , , ,

Filed Under: Code

Hooked on MEF - Using MEF in ASP.NET MVC, and the Nerd Dinner MEF sample fix

by Stephen M. Redd 22. June 2010 22:35

I'm about to embark on a major project in Silverlight 4. In advance of that project, I've been exploring some of the newer technologies such as WCF RIA Services and the Microsoft Managed Extensibility Framework (MEF); both of which shipped in .NET 4.0. While my usage for MEF in Silverlight is more about plug-in and modular architectures, I also have a good bit of interest in MEF as a basic IoC mechanism too.

To get a handle on MEF, I decided to plug it into my TicketDesk 2.0 project, which is being written on the ASP.NET MVC platform. TicketDesk 2.0 uses a class library for all the business and entity framework bits. I generally avoid the IoC design pattern though, preferring instead to just use overloaded constructors; one that takes dependencies for unit testing, and the other that supplies default dependencies to be used by the application at runtime. This technique is common, and is sometimes called a poor-man's IoC. Insults aside, it is simple, easy to code, and works. Traditional IoC implementations on the other-hand tend to add complexity that doesn't do much to advance the application's core functionality.

But MEF is interesting because it offers functionality that can improve how the application actually works, and it has the by-product of being a decent, and simple, IoC container too.

Anyway, once I started using MEF for IoC in TicketDesk, I ran into a slight problem. See, MEF was designed with persistent applications like Silverlight in mind. But in an MVC web environment you have multiple user requests coming in, and many of the objects cannot easily be shared across multiple request threads. So using MEF in this environment requires that you deal with the fact that some objects have to be instantiated on a per-request basis, while others might be scoped to the entire application.

Fortunately, there is this genius person named Hamilton Verissimo de Oliveira (Hammett). Hammet is apparently one of the core devs on the MEF project, and he's done a good bit of writing about MEF in MVC environments. According to his blog, he's even working with the MVC team to get MEF officially supported in the ASP.NET MVC 3 platform.

His most recent sample code is a modified MEF enabled version of the Nerd-Dinner MVC sample application. Scott Hanselman blogged about the MEF version of Nerd Dinner and hosts the downloadable version of the code.

The code in the MEF version of Nerd Dinner is basically a beta of an extended version of MEF for use in MVC scenarios. It includes two class libraries that extend MEF and MVC. These extensions do two things:

  1. Provide lazy MEF compositions on a per-request or per-application basis as appropriate. The per-request composition allows your application to handle compositions only for objects you need to service a request at runtime, while the per-application composition can be used for shared application scoped needs.
  2. Provide convention driven MEF design pattern. MEF is normally attribute driven, where you explicitly declare exports and imports by decorating your code with MEF attributes. But MVC applications are convention driven, so this feature set allows MEF to auto-discover composable parts based on similar conventions... for example, the nerd dinner application treats controllers as composable exports without you having to decorate them with the MEF attributes.

Overall, the code is a mostly complete MVC compatible set of MEF extensions. But the nerd dinner sample only really uses MEF for an IoC design pattern... the models are declared as MEF exports and the extensions handle supplying the appropriate dependencies to the controllers at runtime. But nothing about the way this same is setup should, in theory, prevent you from using MEF in other ways such as a plug-in extensibility mechanism (which is MEF's strong suit anyway).

But... once I got to using the extensions in TicketDesk, I ran into a couple of unusual problems.

First, code in my referenced class library wasn't being passed to my controllers. If I moved the code into the MVC app itself though, it worked like a charm. Second, code in my MVC application that was marked as exports using attributes weren't being passed into constructors for objects in my class library. The nerd dinner example contains the models and controllers both directly in the MVC application assembly itself, so these issues didn't occur there. But once you split your code into two assemblies, things didn't work too smooth. Well... this is just sample software.

So... to track down and fix these problems.

One of the more confusing bits about the nerd-dinner code sample is how the MEF catalogs are built when the app starts up. In global.asax.cs the sample code looks like this:

protected override ComposablePartCatalog CreateRootCatalog()
{
    var agg = new AggregateCatalog();

    foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
    {
        agg.Catalogs.Add(new AssemblyDiscoveryCatalog(assembly));
        agg.Catalogs.Add(new AssemblyCatalog(assembly));
    }

    return agg;
}

Basically this just loops through all the assemblies in the application and builds an Aggregate catalog of all the MEF composable parts from each assembly. What's odd though is that, for each assembly, it builds two separate catalogs and adds both to the aggregate catalog. One is a standard AssemblyCatalog, and the other is a custom type of catalog called an AssemblyDiscoveryCatalog which is part of the extended MEF code shipped with the Nerd Dinner example.

Now, the aggregate catalog is supposed to merge multiple catalogs, eliminating duplicates and all that... but why build two separate catalogs from each assembly in the first place? Shouldn't AssemblyDiscoveryCatalog alone contain all the parts from any one assembly?

Now... I have been unable to understand exactly what it is that causes the problems I was seeing. When I examine the catalogs in the debugger, and the way they are used, the aggregate catalog appears to contain all the composable parts it should. And when the controller factory is invoked, it finds the right controller, and uses a container that has all the right parts in it.

What I did find that was that by removing the duplicate catalog being created in global.asax.cs did fix the problem where my controllers weren't being given the imports when those parts were coming from the referenced class library.

I chose to remove the AssemblyCatalog from the aggregate, leaving just the AssemblyDiscoveryCatalog being added for each reference assembly.

protected override ComposablePartCatalog CreateRootCatalog()
{
    var agg = new AggregateCatalog();

    foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
    {
        agg.Catalogs.Add(new AssemblyDiscoveryCatalog(assembly));
        //agg.Catalogs.Add(new AssemblyCatalog(assembly)); 
    }

    return agg;
}

This worked fine, but then I ran into the second problem... if my class lib needed imports from the MVC application, they weren't getting them... basically the opposite problem.

Digging deeper into the second problem, I was able to find a cause. The AssemblyDiscoveryCatalog type has a minor bug:

private IEnumerable InspectAssembliesAndBuildPartDefinitions()
{
    var parts = new List();

    foreach(var assembly in this.Assemblies)
    {
        var attributes = assembly.GetCustomAttributes(typeof(DiscoveryAttribute), true);

        if (attributes.Length == 0)
        {
             parts.AddRange(new AssemblyCatalog(assembly).Parts);
        }
        else
        {
            foreach (DiscoveryAttribute discoveryAtt in attributes)
            {
                var discovery = discoveryAtt.DiscoveryMethod.New();
                var discoveredParts = discovery.BuildPartDefinitions(assembly.GetTypes());

                parts.AddRange(discoveredParts);
            }
        }
    }

    return parts;
}

First this code checks the assembly for a "DiscoveryAttribute". This attribute marks the entire assembly as one that contains classes for which the modified MEF system should "infer" composable parts based on conventions rather than by looking for the explicit MEF attributes. In the nerd dinner example, this attribute is declared at the top of the Conventions.cs class.

Notice how the code immediately afterwards works though. If the assembly isn't marked with the DiscoveryAttribute, it uses the standard MEF mechanisms to add all the parts to the catalog; those mechanisms do so by looking for the explicit MEF attributes in the code.

But when the assembly is marked with the DiscoveryAttribute, then the code does something quite different; it goes through the assembly looking for dynamically discoverable parts based on any defined conventions. It then adds those dynamically discovered parts to the catalog.

And that's the bug! The branch handling DiscoveryAttribute assemblies doesn't have any code that looks for traditional MEF parts declared by attributes!

So... all I had to do was modify this code so it adds both discoverable and inferred attributes both:

private IEnumerable InspectAssembliesAndBuildPartDefinitions()
{
    var parts = new List();

    foreach (var assembly in this.Assemblies)
    {
        var attributes = assembly.GetCustomAttributes(typeof(DiscoveryAttribute), true);

        parts.AddRange(new AssemblyCatalog(assembly).Parts); // add the standard MEF locatable parts
        if (attributes.Length > 0)
        {
            //add any convention inferred parts 
            foreach (DiscoveryAttribute discoveryAtt in attributes)
            {
                var discovery = discoveryAtt.DiscoveryMethod.New();
                var discoveredParts = discovery.BuildPartDefinitions(assembly.GetTypes());

                parts.AddRange(discoveredParts);
            }
        }
    }

    return parts;
}

Now we have a single kind of catalog that can locate both inferred and explicitly declared MEF parts in any assembly. And after this change, my MVC controllers are getting imports from the class lib, and the class lib is getting imports from the MVC app.

Everyone is happy...

Except that it still bugs me why having the two kinds of catalog added to the aggregate catalog keeps the controllers from getting imports from my class library. I suspect strongly though that this may be some kind of bug in the core MEF implementation, perhaps with how Lazy composition actually works.

But either way... fixing the bug in AssemblyDiscoveryCatalog allows you to handle everything in one kind of catalog and works around both problems.

Tags: , , ,

Filed Under: Code

Developing a custom web application with an eye towards a potential retail market too

by Stephen M. Redd 11. October 2008 12:17

I frequently take on custom software projects... you know, someone needs a web application to do X, Y, and Z and there just isn't anything already on the market that does the trick.

But the truth about software development is that it takes a lot of time and is very expensive. So it isn't unusual that I'm  asked to design the app in such a way that it can be re-sold to other people that might have similar needs.

In other words, one client will bankroll the costs of the custom software, but they'd like the option to recoup those up-front costs by re-selling later.

If you get asked to do software like this, here is my advise...

You have two choices.

  1. You can design for the potential mass-market first, then customize an instance of the application for the specific needs of your paying client when you are done.
      
  2. Write for your paying client's needs first, then extend and expand the initial design to meet the likely requirments of other clients later. 

Forget Option 1.

Look... If you are very experienced with reusable platform development, you have a lot of time for design before you start writing your code, and you have a large team then you might actually consider option 1. Otherwise you are about to embarke on a dissaster.

You only have one known customer and you are only guessing that there could be a wider market out there for the product... a product that doesn't yet even exist. You can make some guesses about your phantom market's needs, but stop kidding yourself...

It is hard enough to get decent requirments out of known customers. Expecting to find usable requirments from a phantom market is just moronic.

I know, your paying client really belives there is a market for the product. They might even be right. But just nod your head and say you'll write something they can re-sell. Then forget about any customer other than the one who has commissioned your project.

Take Option 2!

Assume that the customer bank-rolling your project is the only customer that will ever buy the product. Not only is this probably true, you also have to keep in mind that most custom software projects fail even when you only have one customer to contend with. 

Your primary goal is to make a successful product that can get the job done. If you can't produce software that does the job for your paying customer, then you aren't going to have a reatil market to worry about anyway.

Despite how careful you might be to gather good requirments, create a fantastic design, then code you ass off...  you will not know if your software can do the job until you deliver it to a customer and get to see it succeed in a production environment under regular usage.

If you are successful, and your software meets your paying customer's needs... then can you start thinking about a potential retail market.

The good news is that by the time you get a workable product for your paying customer, you've probably already built an application that is likely to meet the majority of your potential customer's needs too! 

Things to keep in mind for version 1.0:   
  

  • Use an agile process. You don't have to use a formal methodolgy, but stick to the agile ideals. Code the bare minimum you can get away with, deploy it to your paying customer for testing, get their feedback, then go back and code a little more.
      
    Repeat this process until you get all of the 1.0 requirements implemented and deployed. I also suggest that you price your deliverables this way too. Embrace your customer's tendancy to feature creep and shift the requirments as you progress towards the 1.0 release. Be willing and eager to change the design as you progress towards 1.0. 
      
    If you are not directly employed by the paying customer, you do need to make sure you are getting paid immediatly after each delivery, and that you keep the customer informed of the exact price of the changes they request at each iteration.
      
  • Brutally cut everything you can from the customer's requirments for early iterations. Keep it as simple as possible and implement only the absolute bare essentials.
      
    Your client will insist that some fluffy features are "essential". Ignore them and cut those features anyway. You don't have to tell the client you are cutting the features, just tell them you have them scheduled for a later iteration... even if you don't.  
      
    As you deploy iterations towards 1.0, your client will forget about a lot of those fancy features, and you will start to get an idea as to which things are "really" important to them and which ones aren't... all without having wasted time writing the stuff you don't need.  
      
  • Avoid over-architecting your application. It is tempting to layer and componetize everything and follow all those best practices and academic OO techniques. You may also be tempted to split the app into a dozen different assemblies too.

    For version 1.0 you want to deliver the minimum necessary features that get the job done, and you want to deliver it as fast and as cheap as possible. Where OO techniques and best practices will reduce the effort it takes to write your 1.0 code, go for it... but if you can't say for sure exactly how your architecture is getting you to the finish line then scale it back.
        
  • Avoid 3rd party code as much as you can. This includes 3rd party frameworks as well as 3rd party UI components and code libraries. If you cannot produce an essential user facing feature with a built-in component or the stock framework libraries AND you cannot write the code for that user facing feature fairly easily... then you can consider using a 3rd party component.
      
    If  you do use 3rd party code, be sure you have a cheap or free license to redistribute it. While you aren't yet worried about a retail market, you don't want to trap youself by using stuff you can't legally redistribute.
        
  • Don't worry about making everything configurable via online admin tools.  Sure, it is nice to allow the client to use an admin tool to change page titles and text without having to change code... but remember that each and every configurable feature adds complexity to the code and is a potential bug.
      
    Also remember that admin tools are also features that you have to design, test, and debug... but they don't add much value to your application's core functionality directly. These tools can wait until a later version after the core application is deployed and proven successful.
      
  • Stick to the simplest data access mechanism that meets your needs and don't worry about supporting multiple database platforms. Pick a database platform that your paying client can live with, and stick to it. In your code though, you also need to make sure that it can adapt to changes in your database's design without you having to spend too much time fixing up existing code.
      
  • If there is one area you have to totally kick-ass with in version 1.0, it is reporting. If your application has any reporting needs, and most do, you need to be sure you start off with reports that dazzle your customer from day 1.
      
    Be sure the reports are pretty, interactive (sorting, filtering, paging), and most of all printable.
      
    Reporting is the part that your paying customer's management will use the most, and they are the ones that hold the purse strings. You HAVE to knock them dead with killer reports!
      
    Later, if you get a retail market for your app, it will be the reporting features that make or break the sale.   

After version 1.0: getting to the retail market:

I can't tell you how to find and convince other people to buy the application.

What I can tell you is that if you have a 1.0 product that makes your initial paying customer happy, then the odds are good that you also have an application that other similar companies or individuals will be pretty comfortable with too. 

You can probably make a few minor adjustments and take 1.0 to the market as is; and I highly recommend that you get it to market as soon as you can with as few changes as you can. 

Why the rush?

  • You want to recoup the sunk costs of the initial development as soon as possible.
      
  • You need some real clients so you can guage if there are additional requirments that need to be addressed in the next version that you don't already know about.
      
  • You can learn from potential clients that decline to buy your application too, just politely ask them why. Is it too expensive? Do they have some competing software already in place? Does your app lack essential features?

 So... what about 2.0?

If 1.0 was successful with your paying customer, you will likely be heading towards 2.0 even if you don't have a 3rd party market... but by now you should actually know if you have a 3rd party market or not.

If you don't have a retail market then you should consider abandoning the idea of having one later. This will keep the costs low for your paying customer and reduce the scope of your 2.0 project. You can concentrate on enhancing the application for your paying customer's more advanced and ambitious needs as well as shoring up any weaknesses in your 1.0 application's architecture without complications from other customers.

If you do have a 3rd party market, you will want to trim out as many new features as you can and stick to just the ones that are essential to your retail market and paying customer's needs... and I mean essential here... skip the nice-to-have stuff still. 

If there is a retail market buying your product though, you should keep in mind that you have two kinds of customer. Users and management are are one customer, but don't neglect other developers employed by your customers that may need to customize or extend your application. Make sure 2.0 has good developer documentation, a well documented and consistant API, and consider exposing APIs over web services too. 

Either way, 2.0 should be about fixing up sloppy code in 1.0, improving the underlying framework's design with an eye towards supporting all those new features on your client(s) wish-lists. The goal is to re-design, re-architect, and re-code now to support easier and faster development going forward.

In 1.0 you will have identified some fundamental architectual and design mistakes. Fix these in 2.0 to get it out of the way. If you don't, you'll just make it a lot harder to expand your application to meed new requriment later.

And that's it... once you get to 2.0 you will know far more about what you need to do in 3.0 than I do. 3.0 should be sitting on a core application that does a good job, and has been re-designed to be easy to code against. So 3.0 is where you can unleash new features that impress users and improve their lives. 

Tags: , ,

Filed Under: Code

Powered by BlogEngine.NET 1.6.1.6
Theme by Stephen M. Redd