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.