MVC LINQ and OOP

November 28, 2008 · 5 minute read

I just had an interesting conversation with the co-editor of this blog which went something along the lines of…

BigMart: I haven’t had chance to dig into LINQ properly but I don’t like it

Me: But delayed execution rocks (follow long rambling explanation)

BigMart: For our next project I won’t be using that MVC rubbish

Me: But it rocks!

OK I’m paraphrasing and neither of us actually talks like that. However it did get me thinking about my willingness to follow the pack (people like Rob Conery ;-)) when it comes to these kind of developments.

I’m not entirely sold on the approach Rob has taken with his MVC storefront app, but then it seems neither is he, however there’s a lot in there that I like.

LINQ

I’m new to LINQ (specifically LINQ2SQL) and at first couldn’t see any reason to use it over Subsonic. However then I delved into the Storefront app a bit more and I can see why Rob’s drawn to it.

Take a simple address query…

   1: public IQueryable
GetContacts()
<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   2:</span>         {</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   3:</span>             <span style="color:#0000ff;">return</span> (from address <span style="color:#0000ff;">in</span> _db.Addresses</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   4:</span>                     select <span style="color:#0000ff;">new</span> Address</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   5:</span>                                {</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   6:</span>                                    Id = address.AddressId,                                   </pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   7:</span>                                    CompanyName = address.CompanyName,</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   8:</span>                                    StreetAddress = address.StreetAddress,</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   9:</span>                                    CityTown = address.CityTown,                               </pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">  10:</span>                                    PostCode = address.PostCode,</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">  11:</span>                                });</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">  12:</span>         }</pre></p>

Address is a simple class with several properties.

   1: public class Address
<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   2:</span> {</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   3:</span>     <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">int</span> Id { get; set; }</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   4:</span>     <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">string</span> CompanyName { get; set; }</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   5:</span>     <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">string</span> StreetAddress { get; set; }</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   6:</span>     <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">string</span> CityTown { get; set; }</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   7:</span>     <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">string</span> PostCode { get; set; }      </pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   8:</span> }</pre></p>

The key thing to note about this is that nothing gets executed until we actually try to do something with this function. Calling GetContacts() won’t execute any SQL until we try to do something with it (like retrieve a generic list for example).

Why is this important? Well it means that you can further refine your query before executing it.

Lets say the above method is in a class called SqlContactRepository and now we’re going to write some business logic to deal with addresses.

   1: public Address GetAddress(int addressID)
<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   2:</span>         {</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   3:</span>             var address = (from a <span style="color:#0000ff;">in</span> _repository.GetContacts()</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   4:</span>                            <span style="color:#0000ff;">where</span> a.ContactDetailsId == addressID</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   5:</span>                            select a);</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   6:</span>&#160; </pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   7:</span>             <span style="color:#0000ff;">return</span> address.SingleOrDefault();</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   8:</span>         }</pre></p>

This code looks for a specific address (by Id in this case) and then returns it. Because of delayed execution nothing will touch our database until we call

address.SingleOrDefault(); 

So what have we achieved? Well we have a nice simple repository which maps our database to our model (address).

We then have a higher level Service which can retrieve the IQueryable object, query it some more and then eventually execute the sql and grab the result directly into our model.

If we need a list (rather than a single object) we can simply do the following…

   1: public List
GetAddresses(string city)
<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   2:</span> {</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   3:</span>     var addresses =  (from a <span style="color:#0000ff;">in</span> _repository.GetContacts()</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   4:</span>                       <span style="color:#0000ff;">where</span> a.CityTown == city</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   5:</span>                       select a);</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   6:</span>     </pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   7:</span>     <span style="color:#0000ff;">return</span> addresses.ToList();    </pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   8:</span> }</pre></p>

ASP.NET MVC

Throw MVC into the equation and things get really interesting. Now we have a simple Contacts Service which returns a generic list of addresses. Its simple to write an MVC controller to retrieve the data and pass it to a view.

   1: public ActionResult AddressSearch(string city)
<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   2:</span> {</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:white;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   3:</span>     <span style="color:#0000ff;">return</span> View(<span style="color:#006080;">"AddressSearchResults"</span>, _service.GetAddresses(city));</pre>

<pre style="font-size:8pt;overflow:visible;width:100%;color:black;line-height:12pt;font-family:consolas, &#39;background-color:#f4f4f4;border-style:none;margin:0;padding:0;"><span style="color:#606060;">   4:</span> }</pre></p>

Approaching all of this with interfaces (IContactRepository, IContactsService) you can easily write unit tests from the repository right the way up to the controller. Everything is loosely coupled, you can swap your repository out at any time (as long as you continue to return IQueryable objects to the service).

That said there are frustrations with this approach (as Rob has also identified). My own personal concerns lie around…

  • Responsibilities – deciding where to put the business logic (its easy for the Service and Repository responsibilities to become muddled)
  • Mocking – I prefer to use a mock framework (e.g. Rhino Mocks) for unit testing and mocking IQueryable is tricky, you tend to end up having to use test repositories instead

So what’s the conclusion? Well firstly I really like ASP.NET MVC. Even in this simple example it’s clear that I don’t have to worry about the presentation logic whilst I’m developing the main business logic. Add to that the fact that I can easily unit test right up to the controller level and I’m sold on it.

LINQ clearly has some major plusses in terms of its delayed execution and mapping to custom objects. The SQL generated is generally fine and I’ve not run into any performance concerns (yet).

Overall only time will tell but so far I think MVC 1.0 (whenever it’s released) and LINQ will be a viable solid basis for a robust Web App.

Join the Practical ASP.NET Newsletter

Ship better Blazor apps, faster. One practical tip every Tuesday.

I respect your email privacy. Unsubscribe with one click.