Multiple Shapes In ASP.NET MVC

Let’s admit it, whilst Microsoft focussed on getting some “oohs” and “aahs” from the developer community by releasing technologies to make the developers life easier, they certainly ignored the more important areas, especially MVC technologies in web systems.  The problem with this is that the majority of ASP.NET developers that I know today have absolutely no idea what the MVC architecture is, why it benefits any application, and why it’s more prefered over traditional “web forms”.  The real problem out of all of this is convincing developers who’ve been using form-based technologies for 10 years to start using MVC as a method for developing their web applications.  The benefits aren’t clear, and to receive those benefits appears to take a lot of effort and is definitely a big learning curve – this much is understandable.

This week I’ve worked heavily on developing a technical presentation for a big system to show how it can benefit us in the long run.  The funny thing about this is that I never got it to work despite knowing and understanding how to use what is available.  If only it was user error, then the problem would at least be revealed to me.  I re-created the project multiple times to confirm that it wasn’t just my project settings causing conflicts that would ultimately lead to it not working.  Okay, so I’ll go through all the steps.

Step 1: Create the MVC project

Visual Studio has a template project which provides all of the necessary components to get started straight away with an MVC project.  This is okay, but it’s not what I wanted because I need something called areas which Phil Haack kindly provided. This allows us to group controllers into “areas”.  This is useful if your application is actually composed of multiple sub-applications.  So I would set up the default project then implement the areas functionality and make sure at least one view and one controller worked.  This worked fine without any hitches.

Step 2: Hook up your stored procedure

Quite commonly we extract relational data from a database, for example users belonging to groups.  You would output all of the groups, and all of the users with the associations also output.  A user would have a GroupID for example and we can hook them up in the view.  Now, here’s where the problems really start.  Microsoft provide the ability to create stored procedures in LINQ that return the interface ISingleResult, but it won’t work for IMultipleResults by default, that part is custom.  Now your LINQ file has a designer.cs file associated with it (if you remove the connection settings Visual Studio will delete the designer.cs file, which isn’t good).  There’s two methods to developing the necessary function to get the output you require.  Firstly you can create a partial class and completely define this function yourself.  To do this, right click on the LINQ dbml file and hit “View Code” and it will generate the partial class for you.  The other option is to drag and drop the stored procedure onto the diagram and it will auto-generate an incorrect version of the stored procedure in the designer.cs file.  But why? Well, if you find out, please tell me ;) .  The below code is an example only, this isn’t real code before you attack me.  Let’s assume for a second that if you pass in a GroupID it instead retrieves all users for that particular group.  By default the function instead have ISingleResult as opposed to IMultipleResults.  The function body won’t contain the same code either, but this is what you do with it.

[Function(Name="UserGroups")]
[ResultType(typeof(Users))]
[ResultType(typeof(Groups))]
public IMultipleResults UsersGroups(
    [Parameter(Name = "GroupID", DbType = "Int")] string groupID)
{
    IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), groupID);
    return (IMultipleResults)result.ReturnValue;
}

This won’t do much yet because we haven’t set up the rest of the data.

Step 3: The View Model

We need a model to represent the multiple result sets we are returning.  They’re also referred to as multiple shapes in case you ever see that.  This model allows us to contain all of the data we need to display in the view.  There’s various examples out there but I’ll stick with the quick an easy one using some of C#’s features.

namespace MyProject.Areas.Users.ViewModels
{
  using Intranet.Areas.Users.Models;
  using System.Collections.Generic;

  public class UsersViewData
  {
    public IEnumerable<Users> Users { get; set; }
    public IEnumerable<Groups> Groups { get; set; }
  }
}

This is quite straight-forward, it’s a class to contain data only.  Personally I placed this in a separate folder called ViewModels (at the same level as the Models directory, the namespace reveals all).  So now we have this we can start using it, right?  Not quite, there’s a few fiddly bits yet.

Step 4: Namespaces

The page doesn’t understand this yet despite being a part of the project so you can import the namespace in your web.config file like the following:

<namespaces><add namespace="MyProject.Areas.Users.ViewModels"/>
    <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" />
</namespaces>

There’s a couple of ways of doing this, you can set it as a property at the top of your view, but it’s better to do it here in my opinion as you can use it everywhere then.  Talking of namespaces now being available in your project you have to do the following line in your inherits statement

Inherits="System.Web.Mvc.ViewPage<UsersViewData>"

This allows you to access all the properties in the ViewData.Models.* property in your page.

Step 5: Getting the data

Using the controllers functions, or actions as they’re called we do all of the hard work to get it to the view.  Let’s pretend that a null value means that we want all data.  Here’s how we can do it.

public ActionResult DataContracts()
{
    IEnumerable<Contract> users;
    using(UsersDataContext db = new UsersDataContext())
    {
        IMultipleResults results = db.UsersGroups(null);
        users = results.GetResult<Users>();
    }

    return View(new UsersViewData {
            Users = users,
            Groups = groups
          });
}

Then in your view you should be able to access all of the properties strongly-typed, which is excellent.  Unfortunately this doesn’t work for me, so now I’m going to go over the problems I’ve faced.

Problem 1: Where’s the constructor? (solved here)

Here’s an error I receive if I place the function in the dbml file into the partial class

'System.Data.Linq.DataContext' not contain a constructor that takes '0' arguments

I get this every single time, no matter what project I do it in for a C# project (the VB one worked).  I’m not sure if this is a bug, but I’d love to see it be reproduced.  I don’t want to put it on MS Connect until I can verify it happens for other people.  The way I got around this was to instead modify the automatically created function for the stored procedure in the .designer.cs file.  Once I do this, the project builds successfully, great! Or is it?

Problem 2: Generics (solved here)

Using the type in the inherits statement of your view causes the ViewData property to vanish from intellisense, and the project will no longer build.  That’s all I know, I can’t get around the problem therefore I can’t get a working project.  I’ve tried on multiple projects, again, it worked under the non-areas VB version of the MVC project.

Conclusion

Basically returning multiple result sets from LINQ in an MVC project is terrible.  It takes a lot of time, patience and work to get a small set of results, but the real problem is that it’s difficult to get around this.  Bringing back multiple result sets isn’t easy simply because returned shapes don’t have to comply with schemas.  Quite commonly in our business environment our stored procedures bring back a whole lot of custom data.  Personally I created my own classes in the dbml file which are used to represent the data output.

Whilst I think the Microsoft team have come a long way with these problems in mind it actually makes it impossible for me to proceed in making MVC the choice architecture in our application.  Furthermore I really don’t think the Microsoft team have addressed the issue of returning multiple result sets in enough detail yet, it appears to be a set of sporadic blog posts from various Microsoft team members.  In fact, I don’t even think the ASP.NET MVC tutorials have anything on this from what I remember.

Note: If you take a look at the next article you’ll find the solutions to problem 1 & 2.

2 comments

  1. The dirty little secret about .Net, which I’ve been harping on about for years, is that while it’s been very innovative in the language department, and Visual Studio continues to be a fantastic developer-friendly tool, it had a massive blind spot around providing solid, higher-level architectural software components for years.

    That reflected the user base well – there were lots of VB6 graduates, and developers in environments that were heavily desktop and small-server oriented, moving upwards to more complex things. People coming from the other end, from large enterprise & distributed systems, tended to go for Java not because of the language (which we can safely say C# has now surpassed), but because the architectural components were there, they were mature, and there were a number of options reflecting different kinds of systems. Open source added massively to this of course.

    .Net will get there – MS have in the last couple of years woken up to the yawning gap in their offering, that so many developers didn’t seem to even recognise was there (“It’s all about how clever the language and IDE is!”. Er, no). But, they’re only one company, and these things take time to build and to mature. I still think the ‘ivory tower’ model they use delivers results too slowly and not necessarily the way their user base wants, but CodePlex is growing fast so with time, that will undoubtedly be a great source of components once the law of natural selection has had time to identify & polish the best ones.

    The question is it will get there fast enough, before Google or someone else makes it irrelevant, or whether MS will continue to take the strategy of obseleting their own technology before it’s had chance to grow up (e.g. L2SQL), rather than building a steady, incremental platform.

  2. I think they’ve built some of these tools so fast to try and keep ahead of the game that they’ve missed major things. The documentation I’ve read to try and get around these problems is absolutely terrible, it really has no explanation. I’ve relied mainly on very few blog posts and had to combine them together to come up with something I can work with.

    In my opinion though, MS should standardise their data access technologies instead of having so many to choose from, especially with the new ones they’ve brought out like LINQ and the entity framework.

    Whilst older technologies may not be the thing, they’re much more robust and easier to debug in my opinion.

Leave a comment