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

Moving forward with TicketDesk 2.0... again

by Stephen M. Redd 5. July 2010 07:21

I've been hard at work trying to nail together a stable beta of TicketDesk 2.0. I'd started the re-write last year for MVC, but decided to shelve the project in late December '09 until the final RTM versions of the .net 4 and related new stuff matured and were officially released.

Now that the 4.0 stack is out, and I've managed to come to grips with it, I've resumed active work on TicketDesk 2.0 again.

Overall, the plan remains the same; put together the same features as 1.x, but using the new platforms. There will be much streamlining of the features in 2.0, but not a whole lot of totally new stuff.

The core technology stack is shaping up quite well now. I'm using MVC 2 on the front-end, and an Entity Framework 4 generated model on the back-end. In between I'm using the Managed Extensibility Framework (MEF) for IoC and dependency injection (for now). The overall design is MVC with view models on the front-end, and a transaction script based domain service behind.

Right now, my focus is on finalizing the general architecture and getting it stable enough for a beta release and check-in at codeplex, and I'm probably about a month away from that point (assuming I don't get side-tracked with the day job of course).

Once the core of the 2.0 project is done, I plan to aggressivly move forward with new features in subsequent mini-releases.

On the proposed feature list right now are the following (no particular order, not all will be approved and implemented):

  • Private Tickets: Tickets that are visible only to the owner and assigned user. Useful for using TicketDesk as a personal task manager along side the regular help-desk roles. This may also relate to ticket-tasks/sub-tickets (see below) where you can attach private tickets as children to a public parent ticket.
  • Multiple Editor options – system-wide or as a user preference
    • Markdown Editor (with WMD or MarkItUp!)
    • Rich HTML Editor (with CKEdit or TinyMCE)
    • Plain text Editor (with an empty box)
  • Add back tag & category views to ticketcenter
  • Dashboard (widgets for close rates, new ticket rates, activity levels, etc.)
  • List view editor: Ability to create custom lists for the ticket center
  • Workflows Enhancements:
    • Ability to disable the "more info" flow (system-wide)
    • Skip "resolved to closed" workflow (submitter doesn’t have to sign-off, resolve auto-closes ticket)
    • Shelve/Park ticket workflow (park ticket in limbo until later date)
    • "no reopen" option. Prevent user from re-opening the same ticket after it is closed; can be set as a system-wide policy "never-allow re-open", or as a per-ticket option for helpdesk in resolve ticket dialog.
    • Option for re-open to automatically re-assign to previous assigned user (system wide setting)
    • Optional auto-assign for new tickets (either round-robin, or to single named person)
  • Multi-User tickets: Ability to have more than one staff or submitter on a ticket.
    • Ticket would have a "currently responsible" staff member. This will allow IT to pass a ticket to another staff member without losing track of the ticket while it is being worked by someone else.
    • Multiple ticket owners: optional primary owner (if not, any owner can manage the resolve/close workflow equally).
    • Ability for any user to "follow" a ticket without being an assigned or owning user.
  • Merge Tickets (close as duplicate with automatic owner/subscriber hookups to the open ticket)
  • Ticket Tasks: This is a sub-ticket concept where tickets can be split into multiple sub-tickets. The sub-tickets have to resolve before the parent ticket can resolve. Major state changes for sub-tickets would also log to parent's activity log.
  • Admin Views/Reports: simple overview dashboard and some basic reporting features (non-performance tracking only)
  • Suggest-a-ticket: when creating new tickets, perform tag and title analysis on recent and open tickets to suggest potential existing tickets to look at before submitting a new ticket (reduce duplicate problem reports)
  • Multiple projects: Ability to have multiple projects within the system. Each "project" acts as a separate instance of ticketdesk with their own settings and tickets.
  • Support for partial trust deployment environments (making it friendly to 3rd party hosting providers). This may require some additional arcitecting or have to wait on the ASP.NET MVC 3 framework (MEF, which I'm using for IoC, does not appear to be friendly in restricted trust right now; Hammett is currently working with MVC team on MEF related features for MVC 3)
  • Email templates: control how notifications are formatted
  • Mobile Device Support
    • Limited display views
    • Javascript free functionality
    • Optional geo-location support
  • Actionable notifications & Escalations:
    • Email submitters reminders for resolved but unclosed tickets after x time
    • Email assigned users when ticket is untouched for x time
    Email managers when ticket reaches x age (different ages depending on ticket’s status)
  • External User Features:
    • Support for Anonymous Users (by email address)
    • POP 3 email Ticket Submission
    • System wide-ability to prevent submitters from viewing other user’s tickets (also disables multi-owner and ticket "following" features for submitters).
    • Modified email-notifications behavior (notify submitter of changes, send new ticket note to submitter)
    • Customer Identity
      • Ability to create organizations & assign users/staff to it
      • Require organization identity for externally submitted tickets
      • Customer specific ticket lists & views (may be related to multi-project tickets)
      • Ability to enable submitter cross-viewing/following for submitters within same customer org (authenticated or verified users only)
  • Teams and Tiered Support Levels
    • Limited support for users in standard help desk org teams or support tiers.
    • Ticket’s awareness of which team/tier it is in
    • Per-team/tier unassigned queues & ticketcenter views
    • Optional ability to attach submitters to different default teams/tiers

If you have any feature ideas that aren't on this list, please feel free to drop a comment below. Once the 2.0 branch is checked in on codeplex these will be tracked in issue tracker there

Tags: , , , ,

Filed Under: Code | TicketDesk

ASP.NET MVC 2: validation and binding issues with rich-text input, DataAnnotations, view-models, and partial model updates

by Stephen M. Redd 3. July 2010 06:21

Here I'm going to tackle a series of validation related annoyances with MVC 2 that tend to come up rather frequently.

This will include:

  • Input Validation: Potentially Dangerous Request even when using [ValidateInput(false)] attribute.
  • DataAnnotations validation warnings with partial model updates.
  • Dealing with View-Model binding AND DataAnnotations validation warnings with partial model updates together.

OK... so, you are writing a page on the asp.net MVC 2 framework that creates a record in the DB for you. You have a view. Nice!

You have a controller. Also nice!

The view needs select lists and stuff too, so you have a view-model. Sure thing!

Your controller relies on a model class to handle business logic like auto-populating values on the entity and similar. You bet!

And the entity itself is an EF 4 generated class. Nothing special there!

And you created a meta-data buddy class for the entity so you can flag fields with attributes and get MVC to automate your validation. Absolutely!

So you type in your values into the page, hit submit, and the page blows up!

Fuck!

Now, since you were using a view-model, your controller looks something like this:

[Authorize]
public virtual ActionResult Create()
{
    Ticket ticket = new Ticket();
    var model = new TicketCreateViewModel(ticket);
    return View(model);
}

[Authorize]
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Create(FormCollection collection)
{
    try
    {
        Ticket ticket = new Ticket();
        UpdateModel(ticket, collection);
        if(TicketService.CreateTicket(ticket))
        {
            RedirectToAction("Success");
        }
        else
        {
            return View(new TicketCreateViewModel(ticket));
        }
    }
    catch { return View(new TicketCreateViewModel(ticket)); }
}

The first error you are likely to come across will be a potentially dangerous request error. This will happen if your view tries to submit a field that contains HTML characters, like with a rich text editor.

Now, you used to be able to work around this by just flagging the action method with [ValidateInput(false)] and all was well; but not with MVC 2.0 you don't!

In .NET 4.0, the MS dev team decided to overhaul (badly) the input validation mechanism. They wanted to have the same mechanism work for asp.net, web services, and just about any other kind of request too. So they now have a new input validation system that operates so high-up in the request pipeline that it dies long before it actually gets to your controller to see if you've overridden the default behavior. More info in this whitepaper.

Basically, to work around this you need to tell ASP.NET to use the old .NET 2.0 validation mechanism instead of the fancy new one. You do this in web.config by adding this to the system.web section:

<httpRuntime requestValidationMode="2.0"/>
  
  

Basically, this change is so retarded, that any application with a rich text editor ANYWHERE has to turn off the new security feature for the entire application. The good news is that the old mechanism still protects asp.net pages, but you are on your own again for web services and other request types.

Good job guys!

Now, once you've fixed that up the next problem you'll likely run across is that the UpdateModel method has trouble populating the data into the right properties. This is because you are using a view-model class, but when the form is posted back you are trying to use UpdateModel on just an entity.

Generally this is pretty easy. You just tell the UpdateModel method the "prefix" for the values in the form that should match the entity you are updating. In my example here, the property in the view-model that had the ticket fields was called "NewTicket"... we we just alter the controller to supply the prefix "NewTicket" and the UpdateModel method can figure out what properties in the form data should go get shoved into our entity.

[Authorize]
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Create(FormCollection collection)
{
    try
    {
        Ticket ticket = new Ticket();
        UpdateModel(ticket, "NewTicket", collection);
        if(TicketService.CreateTicket(ticket))
        {
            RedirectToAction("Success");
        }
        else
        {
            return View(new TicketCreateViewModel(ticket));
        }
    }
    catch { return View(new TicketCreateViewModel(ticket)); }
}

This is also how the standard example apps for MVC typically do things. And it works fantastic as long as you are posting back ALL of the properties from the view, or at least all the ones that have validations via DataAnnotations attached to them.

But when you have a [Required] attribute from DataAnnotations on entity properties that are NOT being passed back from the view, then you run into the third major problem... the UpdateModel will blow up with validation errors for the fields that your view didn't post to the controller.

This is also due to a late-breaking, and very unwise, change in behavior that the asp.net team made right before MVC 2.0 was released.

The MVC validation stuff in 1.0 only validated properties that were posted up; a mechanism called input based validation. The problem with it is that a hacker could, in theory, get around triggering validation errors by hacking out required fields from the HTTP post. So, they decided to switch to "model based validation" where the validation would check the validity of ALL properties in the model even if they weren't included in the post.

Again... this is an ok idea, but the asp.net team should not have implemented such a breaking-change without giving you some way to easily override the behavior too... after-all, doing a partial model update from a form is NOT exactly an edge case scenario. It is damned common!

Now... Steve Sanderson (smart guy!) posted about a really slick action filter attribute way to handle partial model updates. In short, his mechanism allows you to flag an action method with an attribute, and an action filter will automatically loop in after the model binding is done and remove any model state validation errors for fields that weren't included in the post.

Now... suppose you were doing this instead of what we were doing above:

[Authorize]
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Create(Ticket newTicket)
{
	// stuff
}

With Steven's simple attribute, you could get the desired partial model update to work, without barfing on the data annotations by just flagging the action with one more attribute; like this:

[Authorize]
[HttpPost]
[ValidateInput(false)]
[ValidateOnlyIncomingValuesAttribute]
public virtual ActionResult Create(Ticket newTicket)
{
	// stuff
}

Fantastic!

Except that it only works if you are using a model binder, but we weren't. Remember, in our case we're using a view-model. We don't want to bind to the view-model on the postback... we just want to bind to an instance of the entity we are trying to create.

The UpdateModel method is NOT a model binder though largely it does the same thing as one. So the action filter attribute mechanism Steven describes doesn't work when using the UpdateModel method.

sigh...

OK, now I could just put similar code to what Steven's attribute was using in the controller itself. All his attribute does is loop through the model state and remove errors for fields that weren't in the posted form collection. But honestly, that's an ugly beast and I HATE having my controllers marshaling values around like that.

Now... this example is actually simplistic, and the easiest way to work around this is to use the default binder and bind to the model automatically. This can be done simply with the Bind attribute, which allows you to supply a "prefix" to use for the binding. It looks something like this:

[Authorize]
[HttpPost]
[ValidateInput(false)]
[ValidateOnlyIncomingValuesAttribute]
public virtual ActionResult Create
(
	[Bind(Prefix = "NewTicket")] Ticket ticket
)
{
	if(TicketService.CreateTicket(ticket))
	{
		RedirectToAction("Success");
	}
	else
	{
    	return View(new TicketCreateViewModel(ticket));
	}
}

This works fantastic for simpler cases like this one. We just supply the prefix to use and the binder takes care of it.

But, for reasons I don't want to delve into here, my code was a tad more complex and this technique didn't quite work out for me...

So... what I did to work around this when the Bind attribute wasn't enough, was to create a custom model binder that could do the same thing we're doing with UpdateModel. Anyway, once we have a custom binder we can then modify the controller action to use the custom binder AND Steven's attribute both.

So... here was my custom model binder:

public class NewTicketModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelName = "NewTicket";
        return base.BindModel(controllerContext, bindingContext);
    }
}

Simple enough. All it does is supply the "prefix" to the binding context to achieve pretty much the same results as our UpdateModel method used to. Otherwise it uses the DefaultModelBinder's behavior as-is. Again though, this example is omitting some details that just aren't necessary to describe here, but my custom binder has a few other overloads besides what's shown here.

Now the controller looks like this:

[Authorize]
[HttpPost]
[ValidateInput(false)]
[ValidateOnlyIncomingValuesAttribute]
public virtual ActionResult Create
(
	[ModelBinder(typeof(NewTicketModelBinder))] Ticket ticket
)
{
	if(TicketService.CreateTicket(ticket))
	{
		RedirectToAction("Success");
	}
	else
	{
    	return View(new TicketCreateViewModel(ticket));
	}
}

All I had to do here was declare what binder to use and specify Steven's [ValidateOnlyIncomingValuesAttribute]. I was also able to clean up the controller a bit, because now I don't need the try/catch for binding failures either.

Now... one thing I still didn't like was that the custom model binder here is specific to each view model. There isn't a clean way to supply the "prefix" to the binder without some ugly reflection or such. But I didn't like the idea that every time I needed to bind this way I'd have to make a new custom model binder.

So I settled on a "convention" for this and created a single custom model binder that could be used with any view-model, as long as it followed the convention:

public class ViewModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelName = bindingContext.ModelType.Name;
        return base.BindModel(controllerContext, bindingContext);
    }
} 

Here, the binder requires that the property on the view-model be named the same as the entity's type. So if we want to expose a Ticket entity, we need to name the property in the view-model "Ticket" too.  Previously in my view-model I was using the property named "NewTicket", so I just change that to use property name "Ticket" instead. This way the ViewModelBinder can always find the right prefix name to supply for the default binder.

This still seems like an awful lot of work for a common scenario. I sure hope the MVC team gets all this worked out in the next release of the MVC framework to support this kind of situation more elegantly.

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

TicketDesk 1.2.3 Released on CodePlex

by Stephen M. Redd 8. September 2009 20:11

I've formally packaged a new version of TicketDesk at Codeplex.

TicketDesk 1.2.3 is a minor update. The most significant change is that the HTML editor has been replaced with the markitUp! editor using the markdown syntax.

This should hopefully work around some problems many users were reporting with adding comments in IE8 as well as some display problems that could occur when a user cut/paste content from a word processor or other sources with embedded rich formatting. 

 

Tags: , ,

Filed Under: Code | TicketDesk

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