3rd Feb, 2008

Ground-Up ASP.Net Development I: At the…Well…Ground.

We’re going to take a look at developing an ASP.Net application from the ground up here, eventually using things like AJAX, Test-Driven Development, Design Principles, and maybe some other cool buzzwords as they come up. I’m not intending this to be an in-depth, cover-everything-and-the-kitchen-sink type tutorial, but we’ll cover some good stuff. Here’s the things I’m assuming you either know how to do or can figure out:1) Set up your build environment appropriately. I’m using VS 2005 Standard Edition; if you don’t have it, feel free to use whatever environment you’re comfortable with. We will be working with both C# class libraries and ASP.Net web applications, so make sure you’re able to set things up appropriately (if you’re using the Express Editions, you’ll need both Visual C# Express and Visual Web Developer Express; I have no clue what to do with SharpDevelop or MonoDevelop.)
2) Develop/Work with unit tests. I’m going to be working with NUnit, but you can use whatever test environment you want — we’ll be keeping the tests as simple as possible, and they should work with only minor modifications across most xUnit environments.

Note that I said “eventually” in that first paragraph. We’re a ways off from writing any kind of usable program, but we’ll get there. There’s a couple of things I want to cover that are more, well, preliminary than diving in with some heavy-duty JavaScript and worrying about postbacks or event handling. In fact, just FYI, we’re not even going to write enough code worthy of a zipped download in this part, but hopefully we’ll cover some stuff that’ll make it worth your read.

In The Beginning…

Every project starts off with one idea, that little glimmer of inspiration, often beginning with a variation of the phrase “wouldn’t it be neat if…” Our client, Brooks’ Books Inc. had such an inspiration and came to us to fulfill his dreams.

“I was talkin’ to the Missus last night,” Mr. Brooks says in our conference room, “and she says ‘Mike,’ — cause that’s what she calls me — ‘Mike,’ she says, ‘we ought to get the book store on that interweb thingy everyone’s always talkin’ about. So I think to myself: ‘You know what? She’s right; all them other book stores, like Barnes and Noble and Amazon and Border’s, they’re all online. Why not?”

After talking things through for an hour, getting an overview of what exactly Mr. Brooks has in mind for his new website, we go back to the development room with our handful of notes and start coding. No sooner does the clicking of keys start to fill the air when Bob the Project Manager comes in, mutters something about monkeys and Shakespeare, and interrupts.

“No, no, no! Stop it! Right now. I told you guys last week we weren’t going to just dive into any new projects anymore; we’re going to start to do things a little differently.” Of course, being our project manager, none of us ever pay any attention to anything Bob says, so we give him our best blank stare. He gives a nice, melodramatic sigh, looks at his watch, and rubs his forehead. “Okay, it’s almost lunch time. Why don’t we break, then after lunch we’ll have a little class on development techniques. We’ll…” Of course, we’re already out of the room, though some of us heard the words “then after lunch.”

An hour later, after gathering notebooks, drinks, and shuffling into the conference room, our fearless leader sets up a laptop with a projector and “class” begins.

“What do we know about the project?” he says, looking around the room. Various developers look over their notes and start reciting them verbatim. “No, too much info. Let’s start with a very rough spec here, just a sentence or two. How about this: We’re going to write a book catalog program.”

“‘Web application’, you mean, not ‘program’” corrects one of the developers.

“That’s an implementation detail. We’re not worried about that for right now. So, what do we know from that sentence?” No one answers, so he continues, typing things on the laptop as he speaks and noticeably enjoying his monologue as the rest of us pay as much attention as we deem necessary to maintain our employment.

Getting Started

The class carried on quite well, but alas! the transcripts were lost, so we won’t be going along verbatim any further. Still, I think we can carry on well enough, don’t you? Let’s pick up where Bob left off, and develop the Brook’s Books, Inc. website ourselves.

We know two things here:

  1. We need to represent Book information, and
  2. We need to ‘catalog’ said Book information. (We can reword ‘catalog’ as ‘maintain a list of…’; we won’t, but let your mind make that translation as you’re reading.)

We can make a couple of small logical leaps here based on those and come up with our starting points:

  1. We need to store Book information in some kind of permanent manner (i.e. not just in memory while the program is running).
  2. We need to add Book information to our storage.

STOP. That’s it. It’s really easy to go on further and say “Well, of course we’ll need to delete and edit Book information. And we’ll want to display a list of all the Book entries, if nothing else so you can look and see if you’re duplicating an entry. And after a while, that list’ll get pretty long, so we’ll want to paginate it so people don’t have to look at all of it; and they’re not going to want to go through thirty pages so we’ll need to search, and filter search results, and….”

For now, let’s just stick with adding a Book to our catalog. Otherwise, we can be here all way with various what-if’s and but-then’s. Let’s make that rule number one here:

Rule #1: Work with the smallest possible change set at any given time. Don’t worry about anything else — no matter how relevant or logical it seems — unless that functionality is absolutely required for your current task.

So, let’s justify our current “change set” here. We’ve decided we need to store Book information somehow. In order to “store” a Book, you need to be able to “add” that Book to where ever you’re planning on storing it. In order to “add” a Book, you need to have somewhere to store it. You can store a Book without deleting it, or editing it, or displaying it. Make sense? We’ll try to apply that concept with everything we do here. Of course, we’re storing the Book information in a ‘catalog’, or rather, a ‘book catalog’ to make it a bit more distinct.

Okay, we’ve seen the words “Book” and “Book Catalog” thrown around quite a bit here, so it’s probably a pretty safe bet that those are what we’ll be working with. We know that a BookCatalog is essentially a collection of Books in some manner, and we know we’re going to need to add a Book to a BookCatalog. So, we can make that our first test case.

Let’s set up a Visual Studio solution and add two class libraries to it: BookCatalogSite.Core and BookCatalogSite.Core.Tests. We’ll make BookCatalogSite.Core.Tests reference the NUnit framework and BookCatalogSite.Core, and we’ll add our first test case to BookCatalogSite.Core.Tests:

   1: [Test]
   2: public void AddBookToBookCatalog()
   3: {
   4:     BookCatalog bookCatalog = new BookCatalog();
   5:     Book book = new Book();
   6:     bookCatalog.Add(book);
   7: }

There’s a small problem here, before we even try to do anything with this: how are we going to know that it worked? How will we know that our attempt to add a Book to the BookCatalog actually added a book to where ever the catalog was storing it’s information? We’re going to need a way to make sure that a BookCatalog contains a Book. We’ll go ahead and add that:

   1: [Test]
   2: public void AddBookToBookCatalog()
   3: {
   4:     BookCatalog bookCatalog = new BookCatalog();
   5:     Book book = new Book();
   6:     bookCatalog.Add(book);
   7:     Assert.IsTrue(bookCatalog.Contains(book));
   8: }

Okay, great! We have our test case, let’s go ahead and compile and test it and…

Oh yeah. Forgot about that. We…um…we forgot to add the details, like Book and BookCatalog…

That’s actually intentional: we aren’t worried about it compiling right now, not in the slightest. That’s the basis behind Test-Driven Development, something you’ve probably heard of once or twice. You basically break your development cycle into three steps:

  1. Write a test case that demonstrates how you want the new functionality to work, both how it looks (i.e. method names, parameters, etc.) and what you expect it to do. It doesn’t have to (and initially shouldn’t) compile at this point.
  2. Add just enough code to get the compiler to shut up so that the project will build, but don’t add (or change) any functionality.
  3. Add just enough code to get your test cases to pass. (Granted, you’re not always adding code, but we’ll burn that bridge when we get there.)

So, we try to compile, and the compiler chokes on BookCatalog and Book. Let’s add those classes to the BookCatalogSite.Core project under the root namespace (just add the classes to the project; don’t worry about folder structure or namespaces or anything for now). That’ll clear up those errors, and introduce problems with BookCatalog.Add and BookCatalog.Contains. Let’s add those methods to our class:

   1: public class BookCatalog
   2: {
   3:     public void Add(Book book)
   4:     {
   5:         throw new NotImplementedException();
   6:     }
   7:
   8:     public bool Contains(Book book)
   9:     {
  10:         throw new NotImplementedException();
  11:     }
  12: }

See, now everything builds, so we’re right on track. Run it in NUnit — yes it will fail, just humor me — and look for the pretty red lights to come on. Great! We’ve passed through step 2 of the TDD cycle. Let’s make it work.

Alright, so now we’ve come to the moment of truth, where we start actually (gasp!) writing real code. And now the fun begins with a single question: where are we storing the information? A database? A plain-text file? An XML file?

Okay, we hadn’t thought that far along, and on purpose too. Because, with what we’ve done so far, does it really matter? No, sorry, it doesn’t. We’ve so far done a nice job of defining what we want our class to do, without actually doing it. Brownie points for whomever can think of another word for that…

Alright, since I can’t tell who answered and who didn’t, no brownie points but here’s what I’m talking about: interface. We’ve defined a simple interface for our BookCatalog objects, and we can either move along and force it to have a specific implementation, such as tied to a MySql database or a particular internal format (i.e List<Book>), or we can just say “Okay, BookCatalog is an interface, not a class, sorry about that,” and move on with life. Let’s step back and think about this for a second before we decide one way or another.

Interfaces vs. Classes: The Battle Begins

We’ve got two sides to this coin here, whether to go with interfaces or classes (as I’m sure you knew already). It’s a fairly easy choice this early in the design phase, because we don’t have to worry about breaking any existing code, but it’s still worth debating.

Reconsider the problem here: where are the Books in our BookCatalog going to be stored at? We can take a look to the future here and say “Well, it’s easy: it’s going to be a web application. File access is slower on a server than database access, and working with a database is much easier than working with files anyways.” Aside from pre-optimization concerns, is that a valid assumption to make at this point in the game?

Valid? Probably. Safe? Not hardly. What if the specs change, and it’s no longer a web application? Maybe Mr. Brooks wants to put his catalog out on a CD or something. Do you really want to go through the hassle of setting up a MySql or Sql Server Express installation on an end-user’s computer just so they can check out a couple of books? No.

At the same time, we want the option to work with a database, because those are valid points: database servers are generally faster than roll-your-own data storage code, especially if you’re dealing with a web environment that’ll be working with many queries at once. It is generally easier to write and maintain code that works with a database server than with a custom file format.

And, last but not least, we’re just trying to lay foundation code right now and set up some simple unit tests so we have a good idea of how things work out overall. We don’t want to worry about file formats or databases if we’re wanting to test some simple functionality of another part of the system later on, say a module that’ll read a list of books and average out the prices. We’ll want to be able to work with a quick-and-dirty implementation that utilizes the same methods and properties we’d be using in the real world but doesn’t require all the initialization overhead of setting up and maintaining files or database servers.

(I know that I said earlier to leave the what-if’s and but-then’s out for now. I acknowledge that, and still choose to continue with this little tangent for a while. Let’s move on.)

The primary benefit that using an interface here for our BookCatalog object is that it won’t tie us to a particular implementation — we can develop an implementation that utilizes a particular database server later, or five implementations that utilize five different servers, or an implementation that’s built around XML files or web services or whatever, and still test all of our code that depends on this particular interface using a much simpler implementation. This is a design principle known as the Dependency Inversion Principle, and it’s worth it’s weight in gold when you develop your program based on it.

Hopefully there was a part of you that’s been screaming (or fervently suggesting, at least) this whole time about abstract classes. This is where the line gets a little hazy, but at this point in our program we can still acknowledge them: they don’t tie us to a particular, concrete implementation, and they still provide us with the common interface we want.

The main question to ask here though, is what benefit does an abstract class give us? Right now, none whatsoever. We have no functionality at the moment, just a couple of methods that we know we’re going to work with. There’s no member variables, no default implementation that descendants can override, no core functionality that descendants can utilize. Right now, there’s absolutely no reason to choose an abstract class over an interface.

There is, however, at least one reason to choose an interface over an abstract class: flexibility. Later on, if the time comes where we can look at all — and I mean all — of our BookCatalog implementations and say “Well whaddya know? They all do this little thing here, and they all do it exactly the same, so we can refactor that into a base class,” then we can change our interface to an abstract class with little effort. If we were to have it as an abstract class now, and then someday we decided to change it to an interface, we’d probably have a lot more work to do.

Make sense? Okay, I’ll try to keep the what-if’s back down to a minimum for now. No promises though.

Tangent.End(); TheProject.Resume();

Okay, so we’re going to change BookCatalog to an interface — I’m sure you can manage that much without example code — and we’ll need to modify our test case as well. Unfortunately, we can’t create an instance of an interface (which would kinda defeat the purpose of using an interface, right?) so we still need some kind of concrete implementation here. We’re going to create a mock object implementation for our BookCatalog interface. And, to keep things simple, we’re going to just use a List<Book> as it’s storage mechanism. Since it’s only a mock, and not part of the actual project at this time, I personally put it a subfolder of the Tests project (named ‘Mocks’); feel free to do what you wish with it. Later on, we may decide we like it as it is and we’ll include it in our “real” project, but for now we aren’t worried about that.

Here’s the code for the mock catalog:

   1: public class MockBookCatalog: BookCatalog
   2: {
   3:     private List<Book> books = new List<Book>();
   4:
   5:     #region BookCatalog Members
   6:     public void Add(Book book)
   7:     {
   8:         books.Add(book);
   9:     }
  10:
  11:     public bool Contains(Book book)
  12:     {
  13:         return books.Contains(book);
  14:     }
  15:     #endregion
  16: }

Yeah, I know the implementation is trivial, and that we could’ve just gotten away with using a List<Book> implementation to begin with, or perhaps even implementing the built in ICollection<> interface and relying on that for all of our operations. However, at this point in time, those each have a lot of functionality that our tests don’t require, so there isn’t any point in using them. If we find out later that our BookCatalog looks extensively like ICollection<Book> would, then maybe we’ll reconsider. Until then though…

Now, change your test case to this (adding the appropriate using clauses as needed):

   1: [Test]
   2: public void AddBookToBookCatalog()
   3: {
   4:     BookCatalog bookCatalog = new MockBookCatalog();
   5:     Book book = new Book();
   6:     bookCatalog.Add(book);
   7:     Assert.IsTrue(bookCatalog.Contains(book));
   8: }

Compile and run it in NUnit — you should get pretty green lights now. Yippee — we just created a class that’s pretty much useless as far as the actual project is concerned, outside of unit tests at least.

Bollocks. What we just created was an example of how we’re planning the BookCatalog interface to be used, and an implementation that we can depend on so we know when our tests need to be updated (i.e. compiler errors saying that MockBookCatalog doesn’t implement such-and-such method). Granted, it won’t guarantee that we’ll actually update our tests, at least not with anything useful, but it’ll let us know pretty quickly when we change something that should be tested. And, if we want to later on, we can always steal code from our mock and throw it into a real class (or, rather, refactor it into a base class that implements the BookCatalog interface, because copy & paste is generally a horrible development strategy).

One thing I want to draw your attention to here: notice that I did not name the interface “IBookCatalog”, it’s simply BookCatalog. What’s the point of the additional “I”? To notify the developer that it’s an interface? Unnecessary. I don’t name classes CBook, or AMyAbstractClass; I don’t use Hungarian notation for the variables either, such as bBook or sMyString. When I need to know what type I’m working with, I use Intellisense, plain and simple. And if the time comes that I want to change IBookCatalog from an interface to an abstract class, what then? I don’t want to have to go in and rename every instance — and if I did, it’d be hypocritical to not call it ABookCatalog, wouldn’t it?

Yes, it’s a naming convention, and conventions are generally considered “good”, but I consider myself a conscientious objector on this one (I’m not alone on this; I know Robert C. Martin agrees on this, and honestly he’s where I got the idea, from his book Agile Principles, Patterns, and Practices in C#. Great book, by the way.).

Next time, we might start in on developing some actual code — stuff we’ll be using in the long run. We’ll start looking into a bit of the Model-View-Presenter format, and how we can apply that to web development. If you want a head start, check out this article on CodeProject, as it’s where a lot of the stuff I plan on writing about came from (for me, at least).

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Responses

[...] didn’t finish up (obviously) a continuation of yesterday’s article — I worked on it a bit, but it’s still in process at the moment. No, I thought [...]

Leave a response

Your response:

Categories