New to The Big Boy MVC Series?
Read the series from its humble beginnings.
This series is quickly becoming a crappy Harry Potter ripoff–it gets darker and darker, more and more complex, and, slowly but surely, the audience and the author are starting to meld into one giant, unstoppable, magical man-lizard. Ah, mogwharps!
I admit: I might lose you today. Or, you might need to spend some extra time working through this post. Or, you might decide that you’re better equipped to eat popcorn and watch Oprah Winfrey (I am). Or, you might think that the way I’m setting up my Model isn’t so awesome. Whatever you think, I don’t blame you. It’s okay. Life is complicated, and so is software development. Hence, take as much (or as little) advice from this post as you so desire.
In short: today, we’ll be working on building our model (that’s the M in our MVC). Yesterday, we created our Linq dbml file, which was the first step in this journey. We’ll be using the classes that Linq autogenerated for us yesterday, and we’ll be creating a simple, safe repository.
If you’re new to the idea of the repository pattern, you can check out Liam’s article on implementing a repository. If you’re still not satisfied, check out Mike Hadlow’s article on implementing an IRepository. If you still want more (you devil you), you can peruse Graham’s article on the generic repository model. All three articles serve as inspiration for the creation of my Repository pattern.
Okay. Prepped? Lubed? Let the steps toward pure wizardry begin!
Step 1: Create an IDataContextWrapper interface. Place this file in your Models folder.
Linq To SQL autogenerated a BfdDataContext class for us. Our DataContext is the creator and the overseer of all the entities (Food, Restaurant, Address, State) that we retrieve from the database. Unfortunately, it’s not a class that’s easy to unit test, so we’re forced to perform some delicate condom-esque trickery. Hence, I present the IDataContextWrapper:
using System.Data.Linq;
using System.Linq;
namespace Big.Fat.Dish.Models
{
public interface IDataContextWrapper
{
ITable<T> GetTable<T>() where T : class;
IQueryable<T> GetIQueryable<T>() where T : class;
void Save();
}
}
Step 2: Create a DataContextWrapper class that implements the IDataContextWrapper interface. Also place this file in your Models folder. Once we’ve created this class, we’re all done with wrapping up our DataContext. Lucky us.
using System;
using System.Linq;
using System.Data.Linq;
namespace Big.Fat.Dish.Models
{
public class DataContextWrapper : IDataContextWrapper
{
BFDDataContext context;
public DataContextWrapper()
{
this.context = new BFDDataContext();
}
public ITable<T> GetTable<T>() where T : class
{
return this.context.GetTable<T>();
}
public IQueryable<T> GetIQueryable<T>() where T : class
{
return this.context.GetTable<T>().AsQueryable();
}
public void Save()
{
this.context.SubmitChanges();
}
}
}
Step 3: In your test project, create a Models folder. As always, we’re trying to keep our web app and our test project in perfect juxtaposition.
Step 4: Create a RepositoryTest class. Place this class in our newly created Models folder. If you’ve forgotten how to create a test class, no worries. Check out this blast from the past.
Step 5: Create some tests. Make them awesome. These tests should verify that our Repository class (which we haven’t created yet) exposes basic CRUD functionality. We’ll also make use of RhinoMocks in order to make our tests work. We initially set up RhinoMocks in an earlier post. These tests are actually pretty simple. We’re just verifying that our Repository class invokes the correct DataContextWrapper methods:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using Big.Fat.Dish.Models;
using System;
namespace Big.Fat.Dish.Tests.Models
{
[TestClass]
public class RepositoryTest
{
MockRepository mocks;
IDataContextWrapper context;
Food food;
IRepository repo;
[TestInitialize]
public void Init()
{
mocks = new MockRepository();
context = mocks.DynamicMock<IDataContextWrapper>();
repo = new Repository(context);
food = new Food();
mocks.ReplayAll();
}
[TestMethod]
public void Models_Repository_Add()
{
context.Expect(c => c.GetTable<Food>().InsertOnSubmit(food));
repo.Add(food);
mocks.VerifyAll();
}
[TestMethod]
public void Models_Repository_Delete()
{
context.Expect(c => c.GetTable<Food>().DeleteOnSubmit(food));
repo.Delete(food);
mocks.VerifyAll();
}
[TestMethod]
public void Models_Repository_Save()
{
context.Expect(c => c.Save());
repo.Save();
mocks.VerifyAll();
}
}
}
Step 6: Build, fix, and repeat. As part of our TDD process (a process we’ve already explored when creating MVC routes), we’ll use the compiler errors as personalized build instructions, and we’ll build only as much as we need to in order to pass all of our unit tests.
Okay. Flash to the future. Magic. As a result of a couple minutes of building, fixing, and repeating, we’ve created an interface (IRepository) and a class (Repository). Now, our project compiles and all of our tests pass successfully. Phew. Gin drinking time:
//IRepository.cs
namespace Big.Fat.Dish.Models
{
public interface IRepository
{
void Add<T>(T record) where T : class;
void Delete<T>(T record) where T : class;
void Save();
}
}
//Repository.cs
namespace Big.Fat.Dish.Models
{
public class Repository : IRepository
{
IDataContextWrapper context;
public Repository() : this(new DataContextWrapper()) { }
public Repository(IDataContextWrapper context)
{
this.context = context;
}
public void Add<T>(T record) where T : class
{
this.context.GetTable<T>().InsertOnSubmit(record);
}
public void Delete<T>(T record) where T : class
{
this.context.GetTable<T>().DeleteOnSubmit(record);
}
public void Save()
{
this.context.Save();
}
}
}
Step 7: Add some more tests. Make them awesome. Believe it or not, we’ve created all of the functionality that we need in order to create new database records. But that’s not good enough. We also need to be able to (a.) filter, (b.) retrieve, and (c.) modify records. We’ll do that by creating Pipe classes. To start, let’s build the tests. See if you can decipher what non-existent Pipe functionality I’m designing here:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using Big.Fat.Dish.Models;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
namespace Big.Fat.Dish.Tests.Models
{
[TestClass]
public class PipeTest
{
MockRepository mocks;
IDataContextWrapper context;
Food food;
IRepository repo;
static IQueryable<Food> foods = new List<Food>
{
new Food {
FoodId = 1,
Name = "Popcorn"
},
new Food {
FoodId = 2,
Name = "Redbull"
},
new Food {
FoodId = 3,
Name = "Iced Tea"
}
}.AsQueryable();
[TestInitialize]
public void Init()
{
mocks = new MockRepository();
context = mocks.DynamicMock<IDataContextWrapper>();
context.Expect(c => c.GetIQueryable<Food>()).Return(foods);
repo = new Repository(context);
food = new Food();
mocks.ReplayAll();
}
[TestMethod]
public void Models_Pipe_ToList()
{
IFoods foods = repo.Foods;
IList<Food> foodList = foods.ToList();
Assert.AreEqual(3, foodList.Count());
}
[TestMethod]
public void Models_Pipe_FoodId_Extension()
{
Food food = repo.Foods.WithId(2);
Assert.AreEqual("Redbull", food.Name);
}
}
}
Step 8: Build, fix, and repeat. Okay. More magic. Flash forward. At this point, we’ve added all of the functionality we need to make these tests pass. And, better than that, our model is flexible enough (I hope) such that we can add more functionality–like new pipes, or caching, or new ways of filtering and retrieving records from the database–when necessary. Pretty cool.
To make my tests pass, I started by creating the IFoods interface. I realized that my IFoods pipe would likely share a lot of functionality with any other pipe, so I setup IFoods to inherit from a nonexistent interface, IPipe<T>:
namespace Big.Fat.Dish.Models
{
public interface IFoods : IPipe<Food>
{
Food WithId(int foodId);
}
}
Now, let’s create IPipe<T>:
using System.Collections.Generic;
namespace Big.Fat.Dish.Models
{
public interface IPipe<T>
{
IList<T> ToList();
}
}
What’s an interface without a class that implements it? So, let’s create a Foods class and a Pipe
using System.Linq;
namespace Big.Fat.Dish.Models
{
public class Foods : Pipe<Food>, IFoods
{
public Foods(IQueryable<Food> records) : base(records) { }
public Food WithId(int foodId)
{
return records.Single(m => m.FoodId == foodId);
}
}
}
And Pipe<T>:
using System.Linq;
using System.Collections.Generic;
namespace Big.Fat.Dish.Models
{
public class Pipe<T> : IPipe<T>
{
protected IQueryable<T> records;
public Pipe(IQueryable<T> records)
{
this.records = records;
}
public IList<T> ToList()
{
return this.records.ToList();
}
}
}
Let’s add a Foods property to our IRepository interface:
namespace Big.Fat.Dish.Models
{
public interface IRepository
{
void Add<T>(T record) where T : class;
void Delete<T>(T record) where T : class;
void Save();
IFoods Foods { get; }
}
}
And, last but not least, let’s create a Foods property in our Repository class:
namespace Big.Fat.Dish.Models
{
public class Repository : IRepository
{
IDataContextWrapper context;
public Repository() : this(new DataContextWrapper()) { }
public Repository(IDataContextWrapper context)
{
this.context = context;
}
public void Add<T>(T record) where T : class
{
this.context.GetTable<T>().InsertOnSubmit(record);
}
public void Delete<T>(T record) where T : class
{
this.context.GetTable<T>().DeleteOnSubmit(record);
}
public void Save()
{
this.context.Save();
}
public IFoods Foods
{
get
{
return new Foods(this.context.GetIQueryable<Food>());
}
}
}
}
Don’t Panic
Some of this code might seem confusing. That’s okay. Sit with it. Talk to it. Make sweet love to it. If it still doesn’t make sense, just copy and paste it. Because, sooner or later, you’ll have one of those A-HA moments. It’ll happen. I promise. Be patient. It’s just a matter of time.
If you’re a visual learner, this series of Visio diagrams might help.
First, we have our DataContext and our DataBase. These two are lifelong, interactive buds:
Second, we created a DataContextWrapper:
Third, we created a Repository:
From our Repo class, we can Insert, Delete, or Save changes. We can also retrieve a filterable Foods pipe, which is a glorified delay-loaded list of Food records:
Hopefully you can see how much flexibility our little baby model has. We could easily extend our model to look something like this:
Okay. That last diagram sure makes me sleepy. Hence, snooze time. Lots of snooze time. Then, eggs and sausage. Then, blogging, cont’d.
Move on to Part 20: Buddy Classes, Reflection, and Model Validation.
Read More
You can leave a response, or trackback from your own site.
5 Responses to “The Big Boy MVC Series — Part 19: Linq To SQL, Wrap Up That DataContext, Buddy”
Leave a Reply










[...] Move on to Part 19: Linq To SQL, Wrap Up That DataContext, Buddy. [...]
Hey, cheers for the props bro.
Apologies for the colorless code styling. Thelonious broke some of my most precious plugins. But I’ve created a temporary fix. Anger.
I like to create a blog about .net in Spanish, can I translate your posts to Spanish? (with credit and link…), a friend can’t read in english and I suppose I can help him and other new developers doing it. I not have the experience to post on it and your posts are great.
sorry if my english is awful, I speak in Spanish
Thanks!
[...] a wrapper for our WebRequest. In a similar flash of passion, we spent some time a few days ago wrapping up our frisky DataContext. For now, with our WebRequestWrap class, we really only need to expose one method. He’s a [...]