Long Cloud Technologies
"... A Yankee in the Land of the Long White Cloud, Aotearoa ..."

Working with Orchard from a Hard Programmers Prospective – Part II

Creating a Hello World Module the Orchard way

So here we are ready to do the next part in our series of Orchard Discovery… Let’s make their Hello World module in the default installation, and then let’s see about implementing the Hello World in Visual Studio 2010, using no helpers.  “Buckle Up Boys, it’s going to be a bumpy ride”

So to start with, Here is the link to the tutorial provided by Orchard website on how to do the “Hello World”.  The first thing they tell you is before you get started to download and install the Code Generation feature Command-line Code Generation . To be totally fair I’m going to use the OrchardWpi we got running in WebMatrix to do this part…  I start up WebMatrix, select Choose a Site option and Select my Orchard Site.

image

image

Now we click on the URL for the orchard website and open it up, get into Manage settings and from here on out we just follow the steps in the original article on installing the tools.

Tips for their instructions:

  • first up, there are way too many modules in the gallery if you use the Feed: Any optionimage Be sure to drop that down and select the image and click the Apply button.
  • Gee wouldn’t it be nice if there was a sort order? don’t look for it there isn’t one as of 1.0
  • Gee wouldn’t it be nice if there was a search function?  Again don’t look for it, there isn’t one as of 1.0
  • Quickest way to find your module is do a Ctrl+F (find function for your browser) and search for Code Generation
    image
  • Hey, if I install a module, how about we assume that I WANT to use it, other wise I probably wouldn’t have isntalled it. Can we make the default for installed modules as setting it to already being enabled? that would be helpful

With that done, we continue on our way with the Hello World Tutorial. And right off the bat we have our first question from their instructions: “Open the Orchard command-line”; so how do you open the command-line.  Intuitive it is not, here is a link to the instructions on using the command-line interface.  Basically you need to open a Command line in the bin directory of the Orchard website you want to work on… Hey, give them a break, this is version 1.0…  Don’t forget to make sure you start up the command line as an administrator, to save yourself some grief, and making it a Visual Studio 2010 command prompt would probably be best, although supposedly not required.

You can find the bin directory by going back to WebMatrix and clicking on the path link for the website, and navigate down from there to the bin directory.

image

Ok, now that we got the command prompt open we continue on with our exercise from the Hello World Tutorial. First you need to get the Orchard Commandline tool started.  I’ll save you the digging around in the documentation to find out how: from the command line in the bin directory run the Orchard.exe.  That will initialize the Orchard Command line tools and then we basically just run the command codegen module HelloWorld.

image

I decided to follow the editing instructions using the built in editor in WebMatrix

image

Editorial: A txt file?  you might as well have an INI file.  AND you have a warning that I should be careful to use spaces and not tabs to indent?  Come on Orchard developers, I know XML is verbose and over used, but in this case it seems a lot more intelligent, what we have now looks so VB 3 world type… (and for the record I started programming in VB3 so please don’t flame me too much).

Ok, I save my changes and move onto the next step, where they recommend that I put in a Routes.cs file in the HelloWorld Folder, let me show you how I did that in WebMatrix.  (make sure to click on the “Files” header in the Un-outlook bar), I click on the HelloWorld folder and then click on the Create a new file link on the page.

image

The model dialog that opens has Common Selected for “File Types” none of which will give you the ability to create a class, click on the Suggested Category scroll down and find the Class (C#) item, click on it and in the Name box below set it to Routes.cs and click OK

image

Copy and paste the text from the tutorial and repeat the process as instructed for the HomeController.cs, then do the same for the View file.

Then the instructions tell you to add some lines manually to the HelloWorld.csproj file ( and this tutorial is obviously not for the newbie or someone who wants to ask questions.  It tells you want to do, not so much on the how ).  Just follow thru the instructions. So I did and this is what I got…

image

OK, so I can make Orchard say hello, I have no idea how that all comes together so let’s see if I can take it apart.

First things first, I think we need to know what the CodeGen did before we know how to proceed manually.  Consequently I’m going to run CodeGen and create a new module called UMadeThis.  I’ll follow the same steps as above from the command line.

Note: if you create a module in Orchard using command line and it doesn’t show up in WebMatrix, be sure to Right Mouse on the Modules Folder and Click the Refresh, your module should now show up.

Going again in the Files group, find the UMadeThis folder and let’s “expand” every folder; Now let’s go see what got made…

image

Not a whole bunch.  The web.config for both the Scripts and the Styles is the following

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
  <system.web>
    <httpHandlers>
      <!-- iis6 - for any request in this location, return via managed static file handler -->
      <add path="*" verb="*" type="System.Web.StaticFileHandler" />
    </httpHandlers>
  </system.web>
  <system.webServer>
    <handlers accessPolicy="Script,Read">
      <!--
      iis7 - for any request to a file exists on disk, return it via native http module.
      accessPolicy 'Script' is to allow for a managed 404 page.
      -->
      <add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
    </handlers>
  </system.webServer>
</configuration>

The web.config is in the View Directory is a little more interesting

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
  <system.web>
    <httpHandlers>
    </httpHandlers>

    <!--
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    -->
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <controls>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <handlers>
    </handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

At first glance seems pretty standard for an MVC View Web.config, but taking a closer look I noticed something else: just below the first block of code is from the code generated by the CodeGen in Orchard, the second block is from a normally generated MVC project.

<pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">

<pages 
        validateRequest="false" 
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">

They look almost the same but I see a minor difference in the Orchard Stuff,  The type engines for the Orchard have all had the processorArchitcture specified processorArchitecture=MSIL. basically if not specified your module would be built to what ever processorArchitecture was specified in the build settings, but this extra flag overrides that, and makes sure that your module is built to the MSIL, Microsoft Intermediate Language.  MSIL can be viewed as the assembly code of the CLR.  It means of course that your module is not optimised for the target processor architecture, but on the plus side, this means it will run correctly in CLR at run time.

Finally I noticed one other thing, the View web.config DID NOT include the section for razor web pages…

Now that was interesting, so where is that information for Razor pages located? it is located in base Orchard website web.config file.  In a normal MVC generated project, the reverse is true.  Let me do a quick comparison between the razor entry in the Orchard Web site’s base web.config

<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Linq" />
        <add namespace="System.Collections.Generic" />
        <add namespace="Orchard.Mvc.Html" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

And the razor entry in a standard MVC projects View Web.Config file

  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="RecoveryAssist.Dal" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

And if you look at the pageBaseType you see the real difference.  In Orchard they want to make sure that the Page base type is based on the Orchard.Mvc.ViewEngines.Razor.WebViewPage, whereas a standard mvc project makes System.Web.Mvc.WebViewPage the page base type.  Again, I’m not sure what impact that has, but I’d be willing to bet it isn’t insignificant.

Next time, we will do this all over again, but inside of Visual Studio 2010…

Posted on 20 Mar 11 14:38 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


New Code Highlighter plugin for Windows Live Writer

Just download and installed a new Syntax Highlighter Plugin for Windows Live Writer, for usage with Alex Gorbatchev’s SyntaxHighlighter jscript stuff.  The new plug in was created by Tugberk Ugurlu, you can find it here: http://tugberkugurlu.com/archive/windows-live-writer-codehighlighting-plugin-for-alex-gorbatchev-s-syntaxhighlighter

It is greatly appreciated from me, as the one I was using was created back in 2007… and nothing since then was good enough.

Now all that is left for me it so provide a Razor Plugin to go with Gorbatchev’s library.

Posted on 20 Mar 11 14:33 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


Working with Orchard from a Hard Programmers Prospective – Part I

Moving Orchard from WebMatrix to a “real” programming environment.

For the record, that sub-title is meant in jest. WebMatrix is a a real programming environment, but you know us professional programmers, we are just a bit on the snobbish side, and so unless it is “our” development environment (OS, Language, Tool, Etc.) we do have a tendency to sneer at the alternatives.  The truth of the matter is, that there is so much to learn out there, that there is no way we can know it all.  We are forced by necessity of not truly being the geniuses we sometimes think we are, of specializing in some way in our choice of languages and tools.  We are a lazy bunch and we will often go out of our way to avoid having to “work”, and learning something new is “work”.  Since I have no illusions that I am lazy (I know I am lazy), I will take the “hit” of work of moving out of a dev environ that I am not used too (and having to learn it) to move into a dev environment where I already know how to swim like a duck.

Historical note: as I sit here typing up this post, twitter has just informed me that it is possible that an Official with Japan’s safety agency says that a Meltdown may be under way at Fukushima Nuclear Reactor.  I hope Twitter is wrong.

So let’s start with installing Orchard using the Microsoft Web Platform Installer(WPI) Rather then repeating what is already out there I refer you to the documentation post at the Orchard web site on Installing Orchard with the WPI for the complete list of steps and recommendations. Suffice it to say I installed the WebMatrix platform, then installed the Orchard CMS application, naming the site OrchardWebMatrix.  Finishing up the install and configure steps, this is what I got. …

Whoops, wrote the above paragraph in anticipation of it working correctly… it didn’t; seems that the instructions on the Orchard website aren’t exactly complete.  I followed their steps, and Orchard didn’t install in WebMatrix and it didn’t run! DOH.  Seems that if you have a professional .Net Development machine configured (meaning Server 2008 or Windows 7 with IIS installed and configured) the WPI, doesn’t install by default into WebMatrix, you have to do an extra step they left out of the instructions.

Once you have downloaded and installed Web Platform Installer to your machine, click the Add to include Orchard CMS as an item to install.

Image

This is from the Orchard website.  If you have IIS installed, BEFORE you click the Install button as they tell you to, make sure to click the “Options” hyperlink just to the left of the Install button.

image

Make sure to select the IIS Express (required for use with WebMatrix) the option, then click ok THEN click install. At that point you can return to the instructions on Orchard and go on your way.

Wait, they left something else out from the instructions,

When the installation is complete, click the Launch link.

Image

When you launch Orchard in your browser, you will be presented with the Orchard setup screen.

They make it seem like when you click Launch that you go right into Orchard, you actually don’t..  you go into WebMatrix like this.

image

In order to Launch Orchard you need to click on the URL There at the top.  So I did and configured it to use the Compact Framework SQL.

I’ve taken a quick spin around the WebMatrix, and it’s a pretty good starting point…  I think I even know web developers who would prefer it to my “professional” Visual Studio 2010 setup…  Maybe I will take the time to learn this thing… but not today.  If you click on the Files section of the Un-Outlook bar you get an interesting Tool Ribbon button if you have VS installed.

image

Clicking on that Visual Studio launch button and you get this.

image

Orchard loaded up in Visual Studio as a Web Application.  Not too shabby.

Now let’s see how we can get Orchard setup to run correctly in IIS.  From my earlier attempts I know we can do it using the WPI, let me walk thru that method first.

Ok, this time in the WPI after we click “Add” for Orchard, and before we click install, let’s go into that options dialog again and select the other option – IIS

image

Click OK and Click Install, Accept the License, and we get a new dialog

image

I’ll leave it to go into the Default Web Site, and name it OrchardWpi.

Now you may notice that above it says Step 1 of 2… They lie, there is no step 2 of 2, Orchard just starts installing.  Once it is done you get the following..

image

Click on the Launch Orchard CMS and let’s see what happens.

image

Wow what do you know, it works.  Click Finish Setup and we are good to go.

image

Let’s see where that ended up on the Harddrive, no great surprise c:\inetpub\wwwroot\orchardWpi

image

Oh and look there is a csproj file in there… opening it in VS gets us this.

image

Not too bad, though I am sorry to see that it doesn’t get set to run against the IIS directory where it is installed. That is easily fixed.

image

OK, enough with the WPI doing our work for us, let’s manually set this thing up.

Turns out the instruction for Manually installing Orchard on the Orchard web site can’t be beat.  I followed their directions and it worked like a charm.  But I think I want to be a little more adventurous and install the whole source project to see what I get. So download the Source Code from the codeplex website

Ok, that was easy and now we have the whole system set up to really allow us to dig into the guts.

I open the solution and in Visual Studio 2010. Rebuild the entire solution, no errors that’s good.

Open the project window for Orchard, and on the web tab, select Specific page (leaving the entry blank), check the Enable Edit and Continue (because this is 2011, and I Love Edit and Continue, naysayers be damned), and hit the run with debug button

image

Site started up, ready for configuration.

image

So I click Finish Setup and (It is all a lie, there is no Pie afterwards) I have a working Orchard site with all open source available and loaded up for me in Visual Studio. 

Note, next screen shot is a “Lie”

image

Next time we see about building a module.

 

It’s all really a Lie

Ok, folks to be totally fair, the last screen shot and “TA-DA” sentence is really not exactly true.  You see I started this blog entry at home on my home dev machine and I got some REALLY strange errors in trying to get that last step from “enter configuration” to completed site, all source running Visual Studio 2010.  What I actually got on my home machine was this:

image

I worked on it for a couple of hours trying to figure it out and as of this writing, I actually still don’t know what the problem is on my home machine.  Finally I threw up my hands, it being the weekend and all, and put it aside.  Came into work this morning, and tried the EXACT same steps on my work computer, and lo and behold it worked as I detailed above.  So I decided to finish off this post, but I’m not about to give up on figuring out why it didn’t work at home.  If you go to this Post Orchard Source Install Problems I will keep a running log of my efforts to figure it out at home.

Posted on 13 Mar 11 17:09 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


Working with Orchard from a Hard Programmers Prospective – Prologue

So after much research and study, our intrepid group of programmers at Red Jungle decided we would attempt to settle on the Orchard CMS as our Platform of choice for clients looking for a CMS solution.  We did this for many reasons, but here are the highlights as far as I’m concerned

  • Completely open source http://orchardproject.net/
  • ASP.NET MVC 3 base (with Razor)
  • Originally built by Microsoft, but no longer owned by Microsoft
  • Funded by Microsoft (with no strings attached) for the next two years
  • Principle Developers of Oxite, Erik Porter and Nathan Heskew joined team (outside MS perspective)
  • Plug-in Architecture and extensibility built on a simplified model.

Before I go much further, I think a quick retrospective on last Microsoft Opensource CMS system is in order.  It was built in ASP.NET 1.0 using what was then considered best practices.  During internal development it was known as IBuySpy, and was meant to showcase those best recommended practices when creating a n-tiered web platform.  The Main developer they had brought in for the example site took the core of the IBuySpy example and extended it to a Full Featured CMS system that we all know better as DotNetNuke.

I’ve used DotNetNuke in the past, I’ve even wrote a tutorial about making modules for DNN and explaining what is really going on under the hood as you develop the module: Creating a DNN Module and Understanding DNN Architectural Approach.  While DNN continues to be used and does a great job, it, like so many old systems, has a bit of a legacy built in that it can’t just drop, that at times can make it feel creaky and old.

The world has moved on quite a bit since IBuySpy, and sometimes it is for the best to sell the old house and start from a fresh new foundation, we at Red Jungle feel that Orchard is just such a candidate.  If it becomes even half as successful as DNN was before it, I think we will have made a VERY good choice.

Now that doesn’t mean that I don’t have some issues with Orchard, life is beautiful, la la la.  First up, the install, extension and maintenance of Orchard was made to be easy for people with minimal technical background. By default if you use the install provided by the The Microsoft Web Platform it uses the new Microsoft WebMatrix which in itself is a free-ware and geared towards open source development (you didn’t really expect Microsoft to let AMP be the only free option out there did you)?  While I think the WebMatrix is a great tool, and the price is right (free for everything to run a website) for a hard code monkey like myself it just doesn’t have the horse power and the shade tree mechanic capabilities I want.

Also the out of the box tutorials and instructions of how to extend Orchard are VERY easy, but they “hide” too much under the covers for my taste.  I may yet end up using the shortcuts and tools, but I want to understand exactly how all those helpers fit together to make the magic work.

So I have three main goals with this set of blog posts and two sub-goals. 

  1. I want to move Orchard (and document how) to run in a more robust development environment then WebMatrix, get it to run in IIS with full customization from within Visual Studio 2010 Universal, as a Web Application.
  2. Create the Hello World module using the CodeGen capablities as explained on the Orchard website here, then recreate the Module doing everything manually.
  3. Finally Recreate my “ToDo Task List” from my original DotNetNuke tutorial on codeproject to be applicable to Orchard and deconstruct the Architecture of the system as it applies to Module Creation.
  4. Sub-goals
    1. Package the ToDo Task list as a redistributable, and document how to do so
    2. Publish the ToDo module on NuGet feed for Orchard.
Posted on 13 Mar 11 13:16 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


Linq odd behavior and the need for a .ToList() [where I suspect there shouldn’t be any need]

Give an Entity Frameworks model that has an EmployerUsers table, in which there are two columns, Guid and Name  Consider this code…

List<SelectListItem> employerUserSelectList = new List<SelectListItem>( );

var employerUsers = dal.EmployerUsers.Where( eu => eu.EmployerGuid == SecurityHelpers.EmployerGuid );

employerUserSelectList.Add( new SelectListItem( ) { Text = " [ Select TeamMember ] " , Value = Guid.Empty.ToString( ) } );

employerUserSelectList.AddRange(
   employerUsers.ToList( ).Select(
      eu => new SelectListItem( )
      {
         Text = eu.Name ,
         Value = eu.Guid.ToString( )
      }
   )
);

The important piece of code I want you to really look at is this:

 

employerUsers.ToList( ).Select(
   eu => new SelectListItem( )
   {
      Text = eu.Name ,
      Value = eu.Guid.ToString( )
   }
)

now compare that with this…

employerUsers.Select(
   eu => new SelectListItem( )
   {
      Text = eu.Name ,
      Value = eu.Guid.ToString( )
   }
)

Would you or would you not expect these to both work exactly the same?  Well I would but if you try to run the code with the second clause you get the following error

image

System.NotSupportedException was unhandled by user code
  Message=LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
  Source=System.Data.Entity
  StackTrace:
		...SNIP...
  InnerException: 

Yeah, pretty weird, you have to add the .ToList() to coalesce the IQueryable into an IEnumerable BEFORE you call the .Select.

I think this is a bug, either the .Select shouldn’t be allowed on the IQueryable if it can’t handle it, or it should be smart enough to do the .ToList() if necessary.

Your Thoughts?

You can reach me on Twitter at @matthewhintzen:  http://twitter.com/matthewhintzen

Posted on 26 Aug 10 22:58 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


With Linq you too can program like C++ programmer (if a line of code doesn’t confuse, it’s not doing enough)

I don’t know if you ever had the opportunity to attend those old time geek fests where after the days conferences and presentations, the programmers would gather for drinks and a little after hours friendly competitions, but they were fun, and sometimes really scary.  One of my favorite was to attend a contest where C++ programmers would be given a programming task and the team that produced the lowest total sum of number of semi-colons plus number of programming structures won.

In case you’re not sure what that means exactly, it means who could do the most stuff on the least lines of code.  Get as much processing into a single line of code.  And in C++ you canREALLY do a lot of work in a single line of code if you know what you were about; Mind you the line could end up being 400 characters in length, and completely impossible to figure out what it was doing in retrospect, but damn it was concise!

Needless to say BASIC (and it’s variant Visual Basic) definitely DIDN’T have that capability.  I come from a VB background, and while you can get ridiculous in C#, my coding habits from my VB days means I generally don’t go for obtuse line coding.  In fact one of my mantras is “One line of code, should only do One thing.”

But then along comes Linq, which just BEGS you to do many things in one line, and before you know it you start figuring out how to combine.  And there is this urge to just “combine a little bit more” and before you know it, a C++ programmer would be willing to buy you a beer for your ability to product obtuse, obscure, confusing code.

_Events = Claim.Events
 .OfType<Wizard>( )
 .Where(
 w => w.StatusType == ( int )Enums.EventWizardStatusType.Open || w.StatusType == ( int )Enums.EventWizardStatusType.InProgress )
 .Cast<Event>( )
.Union( Claim.Events .OfType<Task>( )
 .Where( w => w.StatusType == ( int )Enums.TaskStatusType.Open || w.StatusType == ( int )Enums.TaskStatusType.InProgress )
 .Where( w => w.Tasks.Count == 0 )
 .Cast<Event>( ) ).OrderBy( e => e.DueDate ).ToList( );

Ok quick can you tell at a glance what that does?  No, neither could I, after I had finished compressing four statements into one.  It was really cool what I had achieved, but when the day comes that I need to modify that code, and trust me the day will come, I would curse myself.  So even though it means I have to hand in my Obfuscated Code Black Belt, I decided to go with instead the following (which is functionally equivalent to the preceding).

var wizards = Claim.Events
 .OfType<Wizard>( )
 .Where( w => w.StatusType == ( int )Enums.EventWizardStatusType.Open || w.StatusType == ( int )Enums.EventWizardStatusType.InProgress )
 .Cast<Event>( ); 

var tasks = Claim.Events
 .OfType<Task>( )
 .Where( w => w.StatusType == ( int )Enums.TaskStatusType.Open || w.StatusType == ( int )Enums.TaskStatusType.InProgress ) .Where( w => w.Tasks.Count == 0 )
 .Cast<Event>( ); 

var events = tasks.Union( wizards ); 

_Events = events.OrderBy( e => e.DueDate ).ToList( ); 
Posted on 11 Jun 10 22:31 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


How to get to modelmetadata’s additionalvalues and use them in MVC

Got a request from my new client, wanted to have Boolean values map to radio buttons, (no problem, we’ll just make a Shared Boolean.ascx partial view in the EditorsTemplates).  The real kicker came when he asked for a way to attach the labels to use for the yes and no options to the DataModel, which if you think of it makes a lot of sense, because it is a lot easier to ask a True False Question if you can make clear to the user exactly what True means and what False means.

Oh and one other thing, it is very likely that we will end up with boolean that can be Null in the database (yeah I know that maybe we should use an enum with three enumerations for that, but still if the database can do a NULL boolean value, then we should be able to somehow indicate an indeterminate state to the user for a boolean, think tri-state check box).  When you add in the need to possibly have a tri-state boolean, true, false, indeterminate, then it becomes even more clear that labels for the individual radio buttons are necessary to help the user understand clearly what they are being asked.

Of course that means I need to use the DataAnnotations, we basically want to see something like this:

[DisplayName( "NZ Tui Beer?" )]

[DisplayBooleanChoicesAttribute(DisplayChoiceFalse="No", DisplayChoiceTrue="Yes", DisplayChoiceNull="Not Sure, came in glass")]

public bool IsGoodNZBeer { get; set; }

That means I need to figure out and understand the DataAnnotation provider model… Oy Vay, someday my brain is going to explode!  I keep getting these tasks to do things that aren’t really well documented.  As I started down this path, looking behind the curtain when running the app, to see what exactly can I do with DataAnnotations I see they have an AdditionalValues column

clip_image002

That looks like it could be very helpful so it’s off to find some, and I come across the following two links

Now they both involve overriding the DataAnnotationsModelMetadataProvider with a custom class, but they look at the world a little differently.  In the end I implemented the first link’s solution because it was more complete comprehensive walk thru and required less skull sweat.  Having said that, while preparing this blog entry I really started looking closely at that second link and I think it may be a better way to go about this.  I’ll let you know how it eventually turns out.

Closing Note:

Due to loosing the fight against protecting my blog comments from the bloody spammers.  I’ve given up and just shut them off.  If you would like to comment you can reach me on twitter at @matthewhintzen.

Posted on 25 Apr 10 18:35 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


Updating Scott Hanselman’s DateTime Custom Model Binder (DateAndTimeModelBinder)

So new client asks me to help with some of the New ASP.NET MVC 2 development of a new site.  Very cool stuff and having a good time (pioneering spirit, arrows in the back and all).  Anyway it comes up that the model will have some DateTime fields, where we need to actually record time.  Date is easy, we go with the Telerik Asp.net MVC date picker (open-source free for non-commercial usage), but time…

Not to worry cause I remembered that Scott Hanselman had done a Custom Model Binder: DateAndTimeModelBinder for asp.net mvc here.  So I go and download it.  First up, things have changed a little since he first wrote it, for example TryGetValue

bindingContext.ValueProvider.TryGetValue

Is no longer valid on the ValueProvider.  Scott used it for checking if the prefix for the model was included in the name, instead we now have a new method to use for the case of checking for prefix:

bindingContext.ValueProvider.ContainsPrefix(

So with a few minor changes / edits I get it updated to work with the new MVC 2.0 get it into the project when my client says

Hey, end users can’t handle a 24 hour clock, I want AM PM selection, and some of the date fields will be nullable so we need to handle that as well.

DOH! Scott’s work doesn’t handle that.  Damn, that means I have to really understand how the ModelBinder works because I need to modify it.  Of course some of my best code is stolen from inspired by code samples I find on the web, but it does make it easy to “use” code without fully understanding how something works.  When you want to modify it, not so much!  The new documentation for the MVC 2.0 on msdn in general, and around ModelBinders and all the associated classes / methods is not exactly feature rich. Looking up “IModelBinder.BindModel” gets you “Binds the model to a value by using the specified controller context and binding context.” with no examples or very handy links.  Of course that means it’s up to me to add some Community Content (can’t complain if I’m not willing to “light a candle to push back the darkness") about what I have discovered, maybe some example code. However, trying to understand this complicated process with minimal input really makes one feel the arrows in the back.

Anyways, enough whining, in the end I figured it out, and I won’t give you all the hairy details but here in all its Glory(?) is my update to Scott Hanselman’s original DateAndTimeModelBinder with support for a 12 hour clock, am/pm selection and nullable values.

New Model Binders

ModelBinders.Binders[typeof( DateTime )] =
new DateAndTimeModelBinder( ) { Date = "datevalue" , Hour = "hour" , Minute = "minute" , AmPm = "ampm" };

ModelBinders.Binders[typeof( DateTime? )] =
    new DateAndTimeModelBinder( ) { Date = "datevalue" , Hour = "hour" , Minute = "minute" , AmPm = "ampm" , IsNull = "isnull" };

Example Editor Template

(using Telerik Date Control for display)

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>

<%
	var pm = false;
	var hour = DateTime.MinValue.Hour + 1;
	var min = DateTime.MinValue.Minute;
	
	if ( Model.HasValue )
	{
		hour = Model.Value.Hour;
		min = Model.Value.Minute;
	
		if ( hour > 11 )
		{
			pm = true;
			if ( hour > 12 )
			{
				hour -= 12;
			}
		}
		else if ( hour == 0 )
		{
			hour = 12;
		}

	}
	
	var hourSelectList = new List<SelectListItem>( );

	for ( int i = 1 ; i < 13 ; i++ )
	{
		hourSelectList.Add( new SelectListItem( ) { Text = i.ToString( ) , Value = i.ToString( ) , Selected = ( i == hour ) } );
	}

	var minuteSelectList = new List<SelectListItem>( );
	
	for ( int i = 0 ; i < 60 ; i++ )
	{
		minuteSelectList.Add( new SelectListItem( ) { Text = i.ToString( ) , Value = i.ToString( ) , Selected = ( i == min ) } );
	}

	var ampmSelectList = new List<SelectListItem>( );
	ampmSelectList.Add( new SelectListItem( ) { Text = "AM" , Value = "false" , Selected = !pm } );
	ampmSelectList.Add( new SelectListItem( ) { Text = "PM" , Value = "true" , Selected = pm } );
%>

	<div class="editor-label">
		<%: Html.LabelFor(model => model) %>
	</div>
	<div class="editor-field">
		<%: Html.CheckBox("isnull", !Model.HasValue) %>
		<%: Html.Telerik().DatePicker()
					.Name( ViewData.ModelMetadata.ContainerType.Name + "." + ViewData.ModelMetadata.PropertyName + "." + "datevalue" )
									.HtmlAttributes( new { id = ViewData.ModelMetadata.PropertyName } )
			.Value( Model > DateTime.MinValue ? Model : DateTime.Today )
		%>

		<%: Html.DropDownList( "hour" , hourSelectList )%>
		<%: Html.DropDownList("minute", minuteSelectList) %>
		<%: Html.DropDownList( "ampm" , ampmSelectList )%>
		<%: Html.ValidationMessageFor(model => model) %>
	</div>

DateAndTimeModelBinder

public class DateAndTimeModelBinder : IModelBinder
{
    public DateAndTimeModelBinder ( ) { }

    public object BindModel ( ControllerContext controllerContext , ModelBindingContext bindingContext )
    {
        if ( bindingContext == null )
        {
            throw new ArgumentNullException( "bindingContext" );
        }

        //Maybe we're lucky and they just want a DateTime the regular way.
        DateTime? dateTimeAttempt = GetA<DateTime>( bindingContext , "DateTime" );
        if ( dateTimeAttempt != null )
        {
            return dateTimeAttempt.Value;
        }

        //If they haven't set Month,Day,Year OR Date, set "date" and get ready for an attempt
        if ( this.MonthDayYearSet == false && this.DateSet == false )
        {
            this.Date = "Date";
        }

        //If they haven't set Hour, Minute, Second OR Time, set "time" and get ready for an attempt
        if ( this.HourMinuteSecondSet == false && this.TimeSet == false )
        {
            this.Time = "Time";
        }

        //If this datetime can be set to Nullable, lets see if they set it to null
        if ( this.IsNullSet )
        {
            var isNull = GetA<bool>( bindingContext , this.IsNull );
            if ( isNull.HasValue && isNull.Value )
            {
                return null;
            }
        }

        //Did they want the Date *and* Time?
        DateTime? dateAttempt = GetA<DateTime>( bindingContext , this.Date );
        DateTime? timeAttempt = GetA<DateTime>( bindingContext , this.Time );

        //Maybe they wanted the Time via parts
        if ( this.HourMinuteSecondSet )
        {
            var hour = GetA<int>( bindingContext , this.Hour ).GetValueOrDefault();
            var min =  GetA<int>( bindingContext , this.Minute ).GetValueOrDefault( );
            var sec = GetA<int>( bindingContext , this.Second ).GetValueOrDefault( );

            //and if they are doing the am/pm thing we need to adjust the hour
            if ( AmPmSet )
            {
                var pm = GetA<bool>( bindingContext , this.AmPm ).GetValueOrDefault(false);

                // if we have set for pm
                if ( pm )
                {
                    //we need to convert the hour to the 24 hour clock for times greater then
                    //12 noon (pm)
                    if ( hour < 12 )
                    {
                        hour += 12;
                    }
                }
                //if we are in am
                else
                {
                    // just one case to handle here when they put in 12 midnight (am)
                    if ( hour = 12 )
                    {
                        hour = 0;
                    }
                }

            }

            timeAttempt = new DateTime(
                 DateTime.MinValue.Year , DateTime.MinValue.Month , DateTime.MinValue.Day ,
                 hour ,
                 min ,
                 sec );
        }

        //Maybe they wanted the Date via parts
        if ( this.MonthDayYearSet )
        {
            dateAttempt = new DateTime(
                 GetA<int>( bindingContext , this.Year ).GetValueOrDefault( ) ,
                 GetA<int>( bindingContext , this.Month ).GetValueOrDefault( ) ,
                 GetA<int>( bindingContext , this.Day ).GetValueOrDefault( ) ,
                 DateTime.MinValue.Hour , DateTime.MinValue.Minute , DateTime.MinValue.Second );
        }

        //If we got both parts, assemble them!
        if ( dateAttempt != null && timeAttempt != null )
        {
            return new DateTime( dateAttempt.Value.Year ,
                            dateAttempt.Value.Month ,
                            dateAttempt.Value.Day ,
                            timeAttempt.Value.Hour ,
                            timeAttempt.Value.Minute ,
                            timeAttempt.Value.Second );
        }
        //Only got one half? Return as much as we have!
        return dateAttempt ?? timeAttempt;
    }

    private Nullable<T> GetA<T> ( ModelBindingContext bindingContext , string key ) where T : struct
    {
        if ( String.IsNullOrEmpty( key ) )
            return null;

        ValueProviderResult valueResult;

        //Try it with the prefix...
        if ( bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) )
        {
            valueResult = bindingContext.ValueProvider.GetValue( bindingContext.ModelName + "." + key );
        }
        //Didn't work? Try without the prefix if needed...
        else
        {
            valueResult = bindingContext.ValueProvider.GetValue( key );
        }

        if ( valueResult == null )
        {
            return null;
        }

        return ( Nullable<T> )valueResult.ConvertTo( typeof( T ) );
    }

    public string Date { get; set; }
    public string Time { get; set; }

    public string Month { get; set; }
    public string Day { get; set; }
    public string Year { get; set; }

    public string Hour { get; set; }
    public string Minute { get; set; }
    public string Second { get; set; }
    public string AmPm { get; set; }

    public string IsNull { get; set; }

    public bool IsNullSet { get { return !String.IsNullOrWhiteSpace( IsNull ); } }

    public bool DateSet { get { return !String.IsNullOrWhiteSpace( Date ); } }
    public bool MonthDayYearSet { get { return !( String.IsNullOrWhiteSpace( Month ) && String.IsNullOrWhiteSpace( Day ) && String.IsNullOrWhiteSpace( Year ) ); } }

    public bool TimeSet { get { return !String.IsNullOrWhiteSpace( Time ); } }
    public bool HourMinuteSecondSet { get { return !( String.IsNullOrWhiteSpace( Hour ) && String.IsNullOrWhiteSpace( Minute ) && String.IsNullOrWhiteSpace( Second ) ); } }
    public bool AmPmSet { get { return !string.IsNullOrWhiteSpace( AmPm ); } }

}

public class DateAndTimeAttribute : CustomModelBinderAttribute
{
    private IModelBinder _binder;

    // The user cares about a full date structure and full
    // time structure, or one or the other.
    public DateAndTimeAttribute ( string date , string time )
    {
        _binder = new DateAndTimeModelBinder
        {
            Date = date ,
            Time = time
        };
    }

    // The user wants to capture the date and time (or only one)
    // as individual portions.
    public DateAndTimeAttribute ( string year , string month , string day ,
         string hour , string minute , string second )
    {
        _binder = new DateAndTimeModelBinder
        {
            Day = day ,
            Month = month ,
            Year = year ,
            Hour = hour ,
            Minute = minute ,
            Second = second
        };
    }

    // The user wants to capture the date and time (or only one)
    // as individual portions.
    public DateAndTimeAttribute ( string date , string time ,
         string year , string month , string day ,
         string hour , string minute , string second )
    {
        _binder = new DateAndTimeModelBinder
        {
            Day = day ,
            Month = month ,
            Year = year ,
            Hour = hour ,
            Minute = minute ,
            Second = second ,
            Date = date ,
            Time = time
        };
    }

    public override IModelBinder GetBinder ( ) { return _binder; }
}

Closing Note:

Due to loosing the fight against protecting my blog comments from the bloody spammers.  I’ve given up and just shut them off.  If you would like to comment you can reach me on twitter at @matthewhintzen.

Posted on 21 Apr 10 23:10 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


Enumerated Data Types, and ASP.NET MVC (Editor Template for ease of use)

This one took me a few hours to figure out.  I could see that the seeds necessary to pull off what I wanted to accomplish were “planted” the new MVC 2.0, but documentation at this point is just a bit on the light side for some of the new features.

So here is the skinny; Imagine you have a Car Object that has four properties that are fixed, meaning they are perfect candidates for enumerations.

clip_image002

So you create four types for the Car Object

public class Enums
{
    public enum Color
    {
        None=0,
        Black,
        Red,
        NeonOrange
    }

    public enum DoorCount
    {
        None=0,
        Two,
        Four,
        Five
    }

    public enum CarModel
    {
        None=0,
        Family,
        BoyRacer,
        Bachelor,
        Retired
    }

    public enum VehicleType
    {
        None=0,
        Ute,
        Sedan,
        Coup,
        Van,
        SUV
    }
}

Great, now you want to make a screen where the person can create a new car.  Pretty straight forward in MVC… but I have four properties that are all basically enumerations, unfortunately the are all different lists.  I could code up four drop downs, but that’s a lot of typing for the same thing over and over.  What would be really cool is if I could somehow use the EditorFor (which uses the Data driven EditorTemplates framework) function where I could pass the field to be edited and the select list to use, and the control would do the rest for me.  Good news is I can, not so good news, figuring out how.

Let’s start by doing some data annotations on the model for the car as per His Gu’ness’s instructions

namespace BloggingMVCSkeleton.Models
{
	[MetadataType( typeof( Car_Metadata ) )]
	public partial class Car
	{
	}
	internal class Car_Metadata
	{
		[HiddenInput]
		public Guid Uid { get; set; }

		[DisplayName( "Choose Car Color" )]
		[Required( ErrorMessage = "Color must be selected" )]
		public Enums.Color Color { get; set; }

		[DisplayName( "Choose Number of Doors (odd # denotes Hatchback)" )]
		[Required( ErrorMessage = "Number of doors must be selected" )]
		public Enums.DoorCount DoorCount { get; set; }

		[DisplayName( "Choose Model" )]
		[Required( ErrorMessage = "Model type required" )]
		public Enums.CarModel CarModel { get; set; }

		[DisplayName( "Vehicle Type" )]
		public Enums.VehicleType VehicleType { get; set; }

	}
}

Now we need some select lists for those enums, so Let’s add a static method to the Enums class that returns select list items (and uses a NEW enumeration of our enumeration types).

public enum SelectListItemType
{
    None = 0 ,
    Color ,
    DoorCount ,
    CarModel ,
    VehicleType
}


public static IEnumerable<SelectListItem> SelectListFor (
    SelectListItemType selectListItemType ,
    int selectedItem = -1 ,
    string firstItemPromptText = "" ,
    bool includeEmptyFirstItem = false )
{
    var selectList = new List<SelectListItem>( );

    if ( includeEmptyFirstItem )
    {
        selectList.Add( new SelectListItem( ) { Text = "" , Value = "" } );
    }
    else if ( !string.IsNullOrWhiteSpace( firstItemPromptText ) )
    {
        selectList.Add( new SelectListItem( ) { Text = firstItemPromptText , Value = "" } );
    }

    switch ( selectListItemType )
    {
        case SelectListItemType.Color:
            selectList.Add( new SelectListItem( )
            {
                Text = "Black" ,
                Value = Color.Black.ToString( ) ,
                Selected = ( ( ( int )Color.Black ) == selectedItem )
            } );
            selectList.Add( new SelectListItem( )
            {
                Text = "Red" ,
                Value = Color.Red.ToString( ) ,
                Selected = ( ( ( int )Color.Red ) == selectedItem )
            } );
            selectList.Add( new SelectListItem( )
            {
                Text = "NeonOrange" ,
                Value = Color.NeonOrange.ToString( ) ,
                Selected = ( ( ( int )Color.NeonOrange ) == selectedItem )
            } );
            break;
        case SelectListItemType.DoorCount:
            selectList.Add( new SelectListItem( )
            {
                Text = "2" ,
                Value = DoorCount.Two.ToString( ) ,
                Selected = ( ( ( int )DoorCount.Two ) == selectedItem )
            } );
            selectList.Add( new SelectListItem( )
            {
                Text = "4" ,
                Value = DoorCount.Four.ToString( ) ,
                Selected = ( ( ( int )DoorCount.Four ) == selectedItem )
            } );
            
	... [SNIP] ...

        case SelectListItemType.None:
        default:
            break;
    }

    return selectList;

}

I admit that last part looks a little ugly, and I can see some seeds that should assist in building up those select list items, but I didn’t want to hold back this entry while I figure that out, I’ll just amend this entry (probably put in a new entry) when I figure it out.

Pressing on for now, and accepting that the last bit didn’t look very elegant, but the pay off is worth it!

So first let’s create the car controller and put in the Index and the Create functionality

public class CarController : Controller
{
    //
    // GET: /Car/

    public ActionResult Index ( )
    {
        using ( var dc = new Models.CarDataContext( ) )
        {
            var cars = dc.Cars.ToList( );
            return View( cars );
        }
    }

    //
    // GET: /Car/Create
    public ActionResult Create ( )
    {
        var car = new Car( );
        return View( car );
    }
}

Create our views by right mouse clicking, and making them a strongly typed view

clip_image003

Here is what we get…

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend>Fields</legend>
                       
            <div class="editor-label">
                <%: Html.LabelFor( model => model.Color )%>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor( model => model.Color )%>
                <%: Html.ValidationMessageFor( model => model.Color )%>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.DoorCount) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.DoorCount) %>
                <%: Html.ValidationMessageFor(model => model.DoorCount) %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.CarModel) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.CarModel) %>
                <%: Html.ValidationMessageFor(model => model.CarModel) %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(model => model.VehicleType) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(model => model.VehicleType) %>
                <%: Html.ValidationMessageFor(model => model.VehicleType) %>
            </div>
            
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

Good enough… but is it?  couple of things come up, first up, this is for create, now when I want to do the edit page, I’m going to end up with a duplicate of all of this, that means any changes to the model means I have to make changes in two places. That is NOT acceptable!  My mantra (that I have really come to fully embrace only in the last two years) is

“… if you have the same thing happening in two places, then Refactor into one place and have two pointers”

Before we go any further let’s run the app and get to the create page to see what we got.

clip_image004

So let’s use the new EditorFor template capabilities as described on MSDN: Walkthrough: Using Templated Helpers to Display Data.

In the View | Car folder let’s create the EditorTemplates folder

clip_image005

Now let’s create a partial view in that folder set up for the Car.

clip_image006

To start with it looks almost identical to the Create page,

clip_image007

But we edit it a bit and we end up with this.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BloggingMVCSkeleton.Models.Car>" %>

	<div class="editor-label">
		<%: Html.LabelFor( model => model.Color )%>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor( model => model.Color )%>
		<%: Html.ValidationMessageFor( model => model.Color )%>
	</div>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.DoorCount) %>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor(model => model.DoorCount) %>
		<%: Html.ValidationMessageFor(model => model.DoorCount) %>
	</div>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.CarModel) %>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor(model => model.CarModel) %>
		<%: Html.ValidationMessageFor(model => model.CarModel) %>
	</div>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.VehicleType) %>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor(model => model.VehicleType) %>
		<%: Html.ValidationMessageFor(model => model.VehicleType) %>
	</div>

Now back on the Create Page we can modify it to read a-lot more simply, Pay attention to Line 15 below.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<BloggingMVCSkeleton.Models.Car>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend>Fields</legend>
                       
				<%:Html.EditorFor(model => model) %>            
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

The real advantage will come with the Edit Page, let’s do that real quickly so you can see why this is worth while right from the start.  We modify the controller’s edit to be

public ActionResult Edit ( Guid uid )
{
    using ( var dc = new Models.CarDataContext( ) )
    {
        var car = dc.Cars.FirstOrDefault( c => c.Uid == uid );
        return View( car );
    }
}

And generate our Edit view, and after a little clean up we get (looking again at line 17):

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<BloggingMVCSkeleton.Models.Car>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Edit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit</h2>

    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>
        
        <fieldset>
            <legend>Fields</legend>
            
				<%:Html.EditorFor(model => model) %>            
            
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

So now if I need to modify the car, all I have to do is go edit the Car.ascx in the EditorTemplates folder, and Viola both Edit and Create are automatically updated. So far nothing really spectacular and I still haven’t justified the mess I made inside the Enums class with that static SelectListItem function.  So let’s go on to the Enumeration Selects, the interesting take away so far however is I only need to implement the update in the Cars.ascx and both create and edit will get it.

Let’s start with Color:

Being good little MVC programming Children, we DON’T EVER do any code processing in the View, that must all take place in the Controller, so obviously in the Create function we need to get the select list for colors and pass it into the View.

public ActionResult Create ( )
{
    var car = new Car( );
    var colors = Enums.SelectListFor( Enums.SelectListItemType.Color , firstItemPromptText: " [ Select Color ] " );
    ViewData["Colors"] = colors;
    return View( car );
}

We don’t need to make any changes to the View, all we need to do is make a quick modification to the Cars.ascx in the EditorTemplates folder

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BloggingMVCSkeleton.Models.Car>" %>

	<div class="editor-label">
		<%: Html.LabelFor( model => model.Color )%>
	</div>
	<div class="editor-field">
		<%
			var selectList= this.ViewData["ColorsSelectList"] as IEnumerable<SelectListItem>;		
		%>
		<%: Html.DropDownListFor( model => model.Color , selectList )%>
		<%: Html.ValidationMessageFor( model => model.Color )%>
	</div>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.DoorCount) %>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor(model => model.DoorCount) %>
		<%: Html.ValidationMessageFor(model => model.DoorCount) %>
	</div>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.CarModel) %>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor(model => model.CarModel) %>
		<%: Html.ValidationMessageFor(model => model.CarModel) %>
	</div>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.VehicleType) %>
	</div>
	<div class="editor-field">
		<%: Html.TextBoxFor(model => model.VehicleType) %>
		<%: Html.ValidationMessageFor(model => model.VehicleType) %>
	</div>

Run it and here is what we get

clip_image008

So yes I could do the other four properties exactly the same, but… that would mean that with the exception of which select list I get, I would be repeating almost identical boiler plate and we all know the Manta…

“… if you have the same thing happening in two places, then Refactor into one place and have two pointers”

This is where things begin to get a little tricky.  Just as we can make an editor for a whole object we can make an editor for just an particular field on an object.  So that just like we have <%: Html.DropDownListFor( model => model.Color , selectList )%>  we should also be able to have a <%: Html.EditorFor( model => model.Color , selectList )%> and so we can.  And thinking of Inheritance and other object oriented ideas, all of the properties are of some type of Enumeration, so let’s see if we can refactor out a single partial view that can handle any of our enumerations.

Let’s start by creating a partial view in the EditorTemplates folder and set up to be strongly typed for Enum (note: you have to Manually type in “Enum”, it isn’t in the drop down).  Let’s give this partial view the logical Name of “EnumeratedType”.

clip_image009

For now, inside of the partial view let’s just copy in what we had for the dropdown for Color

clip_image010

As you can see we have some read squigglies, the error for all three of these is:

‘System.Enum' does not contain a definition for 'Color' and no extension method 'Color' accepting a first argument of type 'System.Enum' could be found

At this point the control is expecting you to just pass in a single value of type Enum, in short the “model” referenced in the code at this point should be paired down to just the single property being handled.  So let’s delete the “.Color” for all the lines.

Now we have

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Enum>" %>
	<div class="editor-label">
		<%: Html.LabelFor( model => model )%>
	</div>
	<div class="editor-field">
		<%
			var selectList= this.ViewData["ColorsSelectList"] as IEnumerable<SelectListItem>;		
		%>
		<%: Html.DropDownListFor( model => model , selectList )%>
		<%: Html.ValidationMessageFor( model => model )%>
	</div>

Now that we have done that, we can go off to the original Cars.ascx partial view and modify it to call this editor.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BloggingMVCSkeleton.Models.Car>" %>

	<%: Html.EditorFor( model => model.Color )%>

	<div class="editor-label">
		<%: Html.LabelFor(model => model.DoorCount) %>
	</div>

... [ SNIP ] ...

and when we run it we now get …

clip_image011

No change, so far so good.  Now it’s Refactor payoff time (finally)!

Now I need to modify the EnumeratedTypes.ascx partial view so it will accept all of the enumerated properties for the car.  We want the Cars.ascx to look like this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BloggingMVCSkeleton.Models.Car>" %>

	<%: Html.EditorFor( model => model.Color )%>
	<%: Html.EditorFor( model => model.DoorCount )%>
	<%: Html.EditorFor( model => model.CarModel )%>
	<%: Html.EditorFor( model => model.VehicleType )%>

First things first, we need to add the select lists to the view data, so back to the controller.  Before I show you the changes I made, I would like you to Note that I VERY carefully named the Enumerations to be the same as the property name of the value on the Car. 

public ActionResult Create ( )
{
    var car = new Car( );
    ViewData["Color"] = Enums.SelectListFor( Enums.SelectListItemType.Color , firstItemPromptText: " [ Select Color ] " );
    ViewData["DoorCount"] = Enums.SelectListFor( Enums.SelectListItemType.DoorCount , firstItemPromptText: " [ Select # Doors ] " );
    ViewData["CarModel"] = Enums.SelectListFor( Enums.SelectListItemType.CarModel , firstItemPromptText: " [ Select Model ] " );
    ViewData["VehicleType"] = Enums.SelectListFor( Enums.SelectListItemType.VehicleType , firstItemPromptText: " [ Select Vehicle Type ] " );
    return View( car );
}

Please also note that I modified ViewData[“ColorSelectList”] to be ViewData[“Color”].  This will allow me to look up the select list to use with the property in the EnumeratedTypes.ascx partial view by looking at the name of the property passed in.  I could have left “SelectList” in the key, but then I would have to stay with that pattern for the other items.  Anyways, here is the new code in the EnumeratedTypes Partial View.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Enum>" %>
	<div class="editor-label">
		<%: Html.LabelFor( model => model )%>
	</div>
	<div class="editor-field">
		<%
			//var selectList= this.ViewData["ColorsSelectList"] as IEnumerable<SelectListItem>;
			var propName = this.ViewData.ModelMetadata.PropertyName;
			var selectList= this.ViewData.FirstOrDefault( i => i.Key == propName ).Value as IEnumerable<SelectListItem>;			
		%>
		<%: Html.DropDownListFor( model => model , selectList )%>
		<%: Html.ValidationMessageFor( model => model )%>
	</div>

You’ll notice that now I look up the prop name passed into the partial view from the model metadata, then I use that to look up the select list in the view data.  If I had wanted to leave the select lists with “SelectList” tacked onto the end then I would have had to do the same for the other selectlists passed into the view data and I would have to modify the linq look up to be

var selectList= this.ViewData.FirstOrDefault( i => i.Key == propName + "SelectList" ).Value as IEnumerable<SelectListItem>;

Ok so that’s the magic, what do we have now on our Create (and Edit page as well)?

clip_image012

So to Recap

Create Code Tree

Code from Controller

public ActionResult Create ( )
{
    var car = new Car( );
    ViewData["Color"] = Enums.SelectListFor( Enums.SelectListItemType.Color , firstItemPromptText: " [ Select Color ] " );
    ViewData["DoorCount"] = Enums.SelectListFor( Enums.SelectListItemType.DoorCount , firstItemPromptText: " [ Select # Doors ] " );
    ViewData["CarModel"] = Enums.SelectListFor( Enums.SelectListItemType.CarModel , firstItemPromptText: " [ Select Model ] " );
    ViewData["VehicleType"] = Enums.SelectListFor( Enums.SelectListItemType.VehicleType , firstItemPromptText: " [ Select Vehicle Type ] " );
    return View( car );
}

Code From Create View

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<BloggingMVCSkeleton.Models.Car>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend>Fields</legend>
                       
				<%:Html.EditorFor(model => model) %>            
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

Code From Car.ascx (the EditorFor template for car)

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BloggingMVCSkeleton.Models.Car>" %>

	<h2>Specify your new Car Properties</h2>

	<%: Html.EditorFor( model => model.Color )%>
	<%: Html.EditorFor( model => model.DoorCount )%>
	<%: Html.EditorFor( model => model.CarModel )%>
	<%: Html.EditorFor( model => model.VehicleType )%>

Code from EnumeratedType.ascx (the EditorFor template for enumeration values)

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Enum>" %>
	<div class="editor-label">
		<%: Html.LabelFor( model => model )%>
	</div>
	<div class="editor-field">
		<%
			//var selectList= this.ViewData["ColorsSelectList"] as IEnumerable<SelectListItem>;
			var propName = this.ViewData.ModelMetadata.PropertyName;
			var selectList= this.ViewData.FirstOrDefault( i => i.Key == propName ).Value as IEnumerable<SelectListItem>;			
		%>
		<%: Html.DropDownListFor( model => model , selectList )%>
		<%: Html.ValidationMessageFor( model => model )%>
	</div>

All the hard work is done for me in the datatyping.  I’m loving this new MVC 2.0!!!

Posted on 19 Apr 10 22:45 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS


Getting the additionalViewData Object out of the EditorFor in Asp.net MVC

EditorExtensions.EditorFor<TModel, TValue> Method (HtmlHelper<TModel>, Expression<Func<TModel, TValue>>, Object)

Isn't this great, they tell you how to pass some additional data.  Now if only someone would tell you how to get at that data in the editor.  Not too worry, my pain is your gain.
Additional Data should be passed into the editor using an anonymous type say something like

Html.EditorFor( model => model.Claim , new { employers = Model.Employers, claimStatuses = Model.ClaimStatuses } )

You then access that data in the Editor using

this.ViewData.Values

But wait there is more, this Values is actually the ValueCollection that was created by the Dictionary that was created by the anonymous type.  That unfortunately means that you can't get to your items using the keys you specified in the anon type, i.e. you CAN'T do ViewData.Values["employers"].
And to make matters worse, it is an ICollection, so you can't do the indexer (it doesn't know how many items are in it) so you CAN'T do ViewData.Values[0].
So how do you get to one exact item in the collection?  Well you have to force the ICollection into a list, then you can index it. so using the above example let's assume that the Model.Employers is a collection of SelectListItems, you would get it out doing something like this.

var employers = this.ViewData.Values.ToList( )[0] as IEnumerable<SelectListItem>;

Posted on 17 Apr 10 08:49 by matthew.hintzen |

Bookmark this post with:

E-mail | Comments(0) | Comment RSS