Shazwazza

Shannon Deminick's blog all about .Net, Umbraco & Web development

Custom MVC ModelBinder with Complex Models/Objects/Interfaces using built in MVC Validation

September 28, 2010 22:49 by Shannon Deminick

I’ve been creating some cool stuff using ASP.Net MVC 3 lately and came across a situation where I’d like to have quite a complex model/object bound to an Action on my Controller based on a set of posted values from a form. In order to do this, a custom ModelBinder is necessary to collect the data from the posted values, turn it into my custom object, and bind that object to my Action’s parameter. The easy part is to write code to turn the posted values into my custom object and return it, the tricky part is trying to get the in-built back-end MVC validation working for my model… which is currently using DataAnnotations. I really didn’t feel like writing my own logic to validate my model against DataAnnotations and also didn’t want to write the logic to take into account that DataAnnotations might not be the current developers validation provider of choice. So after much digging through the source of MVC and using Reflector, I finally found the solution.  To better describe the concept, here’s an example of the issue:

Each IMyObject has many properties: IProperty. Each IProperty is of a type: IType and each IType has a model which is used to render out the editor in MVC (EditorFor)

public interface IMyObject { int Id { get; } string Name { get; } IEnumerable<IProperty> Properties { get; } } public interface IProperty { Guid Id { get; set; } string Alias { get; set; } string Name { get; set; } IType DataType { get; } } public interface IType { Guid Id { get; } string Name { get; } string Alias { get; } object EditorModel { get; } }

So my controller’s Action looks something like this:

public override ActionResult Edit(IMyObject obj) { if (!ModelState.IsValid) return View(obj); //Save some data and do other stuff... }

Initially my model binder looks like this which is the ‘easy’ part that converts the posted values into an IMyObject object with all of it’s values filled in:

[ModelBinderType(typeof(IMyObject))] public class EditorModelBinder : DefaultModelBinder { protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { if (modelType.Equals(typeof(IMyObject))) { //get the id from the posted values var id = (int)controllerContext .RouteData .Values .GetRequiredObject("id"); //get the object from my data repository by id var model = GetMyObjectFromMyDataRepository(id); foreach (var p in item.Properties) { //it's the editor model that created the editor //for each property in mvc which means that //it's this data that is being posted back var editorModel = p.DataType.EditorModel; // ... Go convert all of the posted values using the bindingContext // ValueProvider and build up the MyObject object created above // ... (A bunch of code goes here to do the conversion) // Except, now that it's created, how the heck do i run it through // MVC validation? } return model; } return base.CreateModel(controllerContext, bindingContext, modelType); } }

In order for the call in your controller to check if your ModelState.IsValid, something needs to do the validation of the model and put the validation results inside the ModelState object. This of course already exists in the MVC framework and is done with the DefaultModelBinder. The DefaultModelBinder is pretty smart and can figure out how to automagically parse and transform the posted values into the specified model and also run it through the MVC validators so long as the model is simple enough to figure out. When your model consists of interfaces, it generally can’t do much about it because it doesn’t know how to create your interface. It also has problems when the model is complex and contains sub objects of sub objects (like the IMyObject). So how do we tap in to this underlying functionality?

[ModelBinderType(typeof(IMyObject))] public class EditorModelBinder : DefaultModelBinder { protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { if (modelType.Equals(typeof(IMyObject))) { //get the id from the posted values var id = (int)controllerContext .RouteData .Values .GetRequiredObject("id"); //get the object from my data repository by id var model = GetMyObjectFromMyDataRepository(id); foreach (var p in item.Properties) { //it's the editor model that created the editor //for each property in mvc which means that //it's this data that is being posted back var editorModel = p.DataType.EditorModel; //get a binder for the EditorModel IModelBinder binder = this.Binders .GetBinder(model.GetType()); //create a new context for it ModelBindingContext customBindingContext = new ModelBindingContext(); //get the meta data for it customBindingContext.ModelMetadata = ModelMetadataProviders .Current .GetMetadataForType(() => model, model.GetType()); //ensure we use our correct field 'prefix' //(this is optional and depends on if you are using a custom prefix) customBindingContext.ModelName = p.Id.ToString(); //use our existing model state customBindingContext.ModelState = bindingContext.ModelState; //use our existing value provider customBindingContext.ValueProvider = bindingContext.ValueProvider; //do the binding! this will also validate and put the errors into the ModelState for us. model = (object)binder.BindModel(controllerContext, customBindingContext); } return model; } return base.CreateModel(controllerContext, bindingContext, modelType); } }

The concept above is pretty much how a Controller’s TryUpdateModel method works and how it does the underlying validation.

Injecting JavaScript into other frames

September 9, 2010 18:44 by Shannon Deminick

The beautiful part of JavaScript is that it is ridiculously flexible and lets you do things that ‘probably’ shouldn’t be done. Here’s a good example of that.

During uComponents development I stumbled upon a situation where I needed to attach a JavaScript method to the top-level frame from inside of an iframe. Well in fact it turns out this is quite easy, you can do something like this:

window.top.doThis = function() { alert("woot!"); }

However, since we’re attaching the ‘doThis’ method to the main frame from an inner iframe, when the inner iframenavigates to another page, this function will no longer exist on the main frame… So this clearly isn’t going to work if we want to be able to call the ‘doThis’ method from the inner frame no matter when and where it navigates to… Conundrum!

So the next possibility is to try to just inject a script block into the main frame from the iframewhich actually does work in Firefox and Chrome but fails in Internet Explorer and Safari. (This snippet of code requires that you have jQuery loaded in the main frame)

var js = "function doThis() { alert('woot!'); }"; var injectScript = window.top.$('<script>') .attr('type', 'text/javascript') .html(js); window.top.$("head").append(injectScript);

In the above, we’re creating a string function, creating a <script> block with jQuery, appending the string function to the script block and then appending this script block to the <head> element of the main frame. But as i said before, Firefox and Chrome are ok with this but Internet Explorer and Safari will throw JavaScript exceptions such as: Unexpected call to method or property access

Ok, so unless you don’t want to be cross browser, this isn’t going to work. It took me a while to figure out that you can do this, but this does work. Yes it looks pretty dodgy, and it probably is. In reality, attempting to do something like this is pretty dodgy to begin with. So here it is (this works in Internet Explorer 8 (probably earlier ones too), Firefox 3.6 (probably earlier ones too) and Chrome 5 (probably earlier ones too) and i didn’t get around to testing Safari but I am assuming it works):

var iframe = window.top.$("#dummyIFrame"); if (iframe.length == 0) { var html = "<html><head><script type='text/javascript'>" + "this.window.doThis = function() { alert('woot'); };" + "</script></head><body></body></html>"; iframe = window.top.$("<iframe id='dummyIFrame'>") .append(html) .hide() .css("width", "0px") .css("height", "0px"); window.top.$("body").append(iframe); }

So i guess this requires a bit of explanation. All browsers seem to let you create iframes dynamically which also means that you can put whatever content into the iframes while it’s being created, including script blocks. Here’s what we’re doing:

  • checking if our ‘dummy’ iframe already exists (since we don’t want to create multiple dummy iframes since we only need one), if it doesn't:
  • create an html text block including the script that will attach the ‘doThis’ method to the ‘this.window’ object (which for some reason will be referring to the window.top object)
  • next we create an iframe element and append the html text block, and then make sure the iframe is completely hidden
  • finally, we append the iframe to the main window’s body element
window.top.doThis();

Nice! So this pretty much means that you can create code from an inner frame and attach it to a different frame, then have that code run in the context of the main frame with the main frame’s objects and script set.

One last thing that i found out you can do, though i wouldn’t recommend it because i think it might start filling up your memory. But this is also possible:

var html = "<html><head><script type='text/javascript'>" + "this.window.doThis = function() { alert('woot'); };" + "this.window.doThis();" + "</script></head><body></body></html>"; iframe = window.top.$("<iframe id='dummyIFrame'>") .append(html);

All that is happening here is that I’m attaching the ‘doThis’ method to the main frame’s window object, calling it directly after and then creating an iframe in memory with this script block. The funny part is that the method executes straight away and I haven’t attached the iframe to the DOM anywhere!

VirtualPathUtility Fixed in .Net 4

August 30, 2010 20:13 by Shannon Deminick

If you are not already aware there’s an issue with the VirtualPathUtility object in the .Net framework. If you are trying to use the IsAbsolute or ToAbsolute methods with a path that contains a query string, you’ll get an exception thrown. A quick Google search on this issue will show you a ton of posts and ‘work arounds’ for this problem and I’ve implemented a custom work around for this in the ClientDependency framework as well. In fact, I only noticed this problem when testing the ClientDependency framework with .Net 3.5 and trying to register embedded resources through it (as see in this blog post here). Since I’d been developing the latest builds of ClientDependency in .Net 4, i had no problems at all but as soon as I changed the build to .Net 3.5, the exceptions began to show since web resource URLs have query strings.

So it the good new is that it appears as though Microsoft have finally fixed this bug in .Net 4! The bad news is that you’ll still have to use your ‘work arounds’ if you are targeting framework versions below 4.

How to embed ASMX web services in a DLL

August 12, 2010 22:01 by Shannon Deminick

The development of uComponents for Umbraco is still ongoing but is probably close to a v1.0 release. During the development of the Multi-node tree picker data type, I’ve spotted a bug in the Umbraco core in regards to the ASMX web service the existing tree uses to render out it’s initial node. The bug basically won’t allow the tree to render an individual tree, only a full app. Since the Multi-node tree picker requires rendering of an individual tree, i needed to fix this. To do this was a bit of a work around but i had to override the tree’s ASMX web service with my own (I’ll write a post about this one later). It then occurred to me that I’ve never embedded an ASMX web service into a DLL without a physical file and I didn’t want to go updating the web.config with custom handlers, etc… So I came up with a way to embed ASMX web services in your DLL without modifying web.config’s, etc… here’s how:

First, create your web service as a standard class and add all of the functionality that your require your web service to have:

image

In your class library project, create a new text file but give it the extension .asmx:

image

Set the file to be a non compiled file:

image

Create an resource file:

image

Double click on your new RESX file, then drag your ASMX file into the editor to add the file as a property of your RESX file:

image

Double click your ASMX file to edit it and set it to inherit from your custom web service class:

image

Now, we need to register this ASMX web service into the web application that it’s being deployed to. This is easily done by just checking if the ASMX file exists where it needs to be before it needs to be called and if it doesn’t, we just create the file. In the case of uComponents and the multi-node tree picker requires the ASMX web service for the CustomTreeControl, so we’ll do this check in the CustomTreeControl’s static constructor:

public CustomTreeControl() { var filePath = HttpContext.Current .Server.MapPath("~/CustomTreeService.asmx"); //check if it exists if (!File.Exists(filePath)) { //lock the thread lock (m_Locker) { //double check locking.... if (!File.Exists(filePath)) { //now create our new local web service using (var sw = new StreamWriter(File.Create(filePath))) { //write the contents of our embedded resource to the file sw.Write(CustomTreeServiceResource.CustomTreeService); } } } } }

 

That’s it!

.Net Open Source Blog Comparison

August 11, 2010 09:09 by Shannon Deminick

When I started creating this site this morning, the first thing i though to myself was that I wasn’t going to use BlogEngine.Net for yet another blog. So I went on a mission to find some other open source blogging systems written in ASP.Net. One of the things I needed the codebase to support was to work seamlessly with Live Writer, so after some quick research I found that the new Orchard project support Live Writer and so does SubText. So using WebPI, I decided to give them each a quick test run.

I’m sure the Umbracians out there are asking: Why wouldn’t you just use Blog 4 Umbraco considering I’m on the core development team. Well, i guess it’s because all I really want is a very simple blogging tool and really just wanted to try out some new tools.

So I started with Orchard and was kind of excited to see what it offered since it’s newcomer to the CMS scene and has a bit of hype behind it. The admin UI was nice and is was very easy to install. Setting up a new blog was pretty straight forward as well… just click on Create Blog. It' also has a couple nice skins you can test out. So far so good (even though this is still a alpha/beta product). So I tried plugging in Live Writer to it and it turns out that Orchard really hasn’t implemented some of the much needed features of the MetaWeblog API such as categories & tags! ouch. On a side note, i tried looking at creating new content types in Orchard, thinking it would be similar to how Umbraco works. Turns out it’s not quite the same and looks as though this functionality is in its infancy in Orchard. There was really only one data type to choose from which was text and it appeared as though i couldn’t delete a content type either. We’ll see how the project looks in a few months.

Next I went on to SubText. This was also pretty easy to install (not as easy as Orchard though) but the admin UI isn’t so pretty. SubText has pretty much every feature that you could want out of a blogging system and comes with a bunch of skins as well. One thing i noticed which looked pretty cool is that you can add text filters so the SubText will automatically create links for you in your posts. So I plugged in Live Writer and everything is pretty good, except that they haven’t implemented tagging in their MetaWeblog API either.

As you’ve probably noticed, I’ve settled back on BlogEngine.Net. It has better Live Writer integration than either of these other two blogging systems, the admin UI is nice enough and it’s pretty damn easy to use. I really wish they’d implement non ASPX page extensions sometime soon though!

And it begins

August 11, 2010 08:22 by Shannon Deminick

For the last couple of years I've been blogging on FARMCode.org and today decided that I was going to start up my own blog as well. This doesn't mean that I won't still be blogging on FARMCode, it just means that I have a new place to post up information that may not directly relate to TheFARM.

In the coming weeks I'll start posting up some new content, code snippets, some info on the up and coming Umbraco project we've been working on called uComponents and maybe some portfolio work as well.