ASP.NET MVC tip – Don’t use the Content or Scripts directories for view specific files
ASP.NET MVC creates a default file structure for you when you create a new project. It includes a Scripts directory, which contains the MS AJAX and jQuery .js files, and a content directory, which contains a Master page and CSS files. I find this to be extremely cumbersome and it forces me to jump around a lot. Storing shared CSS and JavaScript files in those locations is fine in my opinion. But if you are using View specific CSS or JavaScript files, you should put them alongside your view page in the Views directory. This allows you to quickly find the file you want to work on.
ASP.NET MVC TIP – Keep your controllers and actions thin
When writing you controller actions, keep them short and sweet. Don't add a lot of actions and keep a close eye on the length of each action.
Whenever possible, I try to make my controller actions look something like this.
-
public ActionResult DoSomething()
-
{
-
MyModel model = MyService.GetModel();
-
return View("MyView",model);
-
}
-
//or
-
public ActionResult DoSomethingWithAKey(int myKey)
-
{
-
MyModel model = MyService.GetModel(myKey);
-
return View("MyView",model);
-
}
(1)
There are two reasons to do this:
1) It makes your controllers less complicated and easier to maintain. - You don't want to have to do a lot of debugging in your controllers. Chances are, if you have a lot of complicated logic in your controller actions, you are probably mixing concerns. Make sure that your controllers are only responsible for getting data from your models and figuring out what view to send to the client.
2) It makes testing easier. If all you are doing is getting a model and passing that model to a view, what do you really need to test? Maybe that the correct view is being returned.
(1) Of course I use more descriptive names than "myKey".
Declaration of Awesomeness for the Mono Compiler
MonoDevelop 2.0 Beta 1 - Miguel de Icaza
Now, technically speaking we have not received any awards for our C# 3.0 compiler, but we should have, because we are awesome. And in fact, I will be arranging a dinner at my place this coming weekend where we will award prizes to the best pieces of technologies and our C# compiler is a nominee.
We the undersigned do hereby declare the Mono C# 3.0 compiler to be awesome. This is not meant to denigrate the .NET compiler from Microsoft as it is a fine compiler in its own right. We are declaring the Mono C# 3.0 compiler to be an awesome piece of work by a ragtag group of merrymakers well-dressed group of distinguished gentlemen and worthy of all levels of awesomeness.
Leave a comment if you believe the Mono C# 3.0 compiler to be awesome.
Object oriented programming vs. class oriented programming
In the last post, the code was pretty clean. Our resident Rhino.Mocks guru at work, Sean, left a comment saying that the new code was much better than the Do-Func stuff I had before. Sean was the one that pointed me to the Repeat.Times methods in Rhino.Mocks. I thought I'd post the old code that I had cobbled together from a StackOverflow answer.
-
IDataReader reader = MockRepository.GenerateStub<IDataReader>();
-
-
reader.Stub(x => x.Read()).Do((Func<bool>) delegate()
-
-
{
-
-
m_NumberOfTimesIDataReaderHasBeenCalled++;
-
-
return
-
-
(m_NumberOfTimesIDataReaderHasBeenCalled%2 != 0);
-
-
});
-
-
reader.Stub(x => x["ID"]).Return(Guid.Empty);
-
-
reader.Stub(x => x["FullName"]).Return("Test User");
-
-
-
-
List<UserDTO> list = SearchProvider.ParseUserData(reader);
-
-
Assert.IsNotNull(list);
The re-factored code using the Repeat.Times methods.
-
IDataReader reader = MockRepository.GenerateStub<IDataReader>();
-
-
reader.Stub(x => x.Read()).Return(true).Repeat.Times(1);
-
-
reader.Stub(x => x.Read()).Return(false);
-
-
reader.Stub(x => x["ID"]).Return(Guid.Empty);
-
-
reader.Stub(x => x["FullName"]).Return("Test User");
-
-
-
-
List<UserDTO> list = SearchProvider.ParseUserData(reader);
-
-
Assert.IsNotNull(list);
You can see how the second code sample is much cleaner than the first. A lot of the messiness of the first code sample comes from talking to the compiler instead of talking to objects. What do I mean by that? Well, in the first example we have to tell the compiler what the delegate should return
-
Func<bool>
You'll also notice some ugliness inside of the delegate body.
-
m_NumberOfTimesIDataReaderHasBeenCalled++;
-
return (m_NumberOfTimesIDataReaderHasBeenCalled%2 != 0);
Here, I was incrementing a class member and checking to see if it was odd or even. If it was even, I'd return true, otherwise I'd return false. This allowed me to control the number of times the IDataReader.Read() method would return true. In this case, it would return true once, then the variable would be incremented to an odd number and the Read method would return false.
That's all part of me telling the compiler what to expect, when what I really want to do is just tell my objects what to do. This episode of the Alt.net podcast also talks a little bit about class-based programming versus object-oriented programming.
New ASP.NET MVC sample – Oxite – Needs some TLC
Last week the MIX online team announced that they are releasing the ASP.NET MVC code that powers their MIX Online blog as open source at CodePlex. This is a great thing.
The code base, while touted as a "a real-world sample written using ASP.NET MVC.", is not without some problems. The commentary on Twitter soon after it's release was pretty negative.
This is why Oxite is not good. http://pastie.org/339644
browsing the Oxite source... wondering how many people will try to learn from this mess
@robconery because microsoft doesn't deal with foibles with courage, i expect oxite will be allowed to degrade yet more customer potential
Oxite source is being slaughtered by the alt.net crowd; sad thing is, since its by MS others will use it as guidance on how to do MVC
@cwoodruff please, please, please do not use Oxite as a guide when doing MVC... PLEASE DO NOT
@lazycoder if u read the code, it's actually a collection of anti-patterns of MVC... I hope people are not going to learn from it
Rob Conery has a great post where he outlines some of the issues with the Oxite code base and how they can be fixed. He has already offered some patches to the team.
That's what I mean when I say this is a great thing. Since the team is doing all their work out in the open, we get to see how it evolves. I fear that a lot of the ASP.NET MVC code that we see written the first few years after it is released will look very similar to the Oxite code. Hopefully, the refactoring of the code base into something that embraces the strengths of MVC will be done in public as well so that first time MVC developers who find themselves writing the same code that the Oxite team has will have a lot of resources available.
I'd encourage everyone interested in the ASP.NET MVC product to watch the evolution of Oxite closely and contribute to the development.(1)
(1) That doesn't include me for quite some time as we're getting ready to have a baby soon. You don't want to see code I've written on only 4 hours sleep over two days.
Mocking IDataReader using Rhino.Mocks 3.5
The other day I was writing unit tests for some legacy code and I needed to mock IDataReader. I really just wanted to populate the reader with a single row of data, then the Read() method should return false. Using Rhino.Mocks it was a piece of cake.
-
IDataReader reader = MockRepository.GenerateStub<IDataReader>();
-
-
reader.Stub(x => x.Read()).Return(true).Repeat.Times(1);
-
-
reader.Stub(x => x.Read()).Return(false);
-
-
reader.Stub(x => x["ID"]).Return(Guid.Empty);
-
-
reader.Stub(x => x["FullName"]).Return("Test User");
-
-
-
-
List<UserDTO> list = SearchProvider.ParseUserData(reader);
-
-
Assert.IsNotNull(list);
The magic happens in the Repeat.Times(1) statement. This tells Rhino.Mocks that when the method is called, the mock should Return the given value that number of times. After that it can return a different value.
Update: Jeremy Miller told me about the DataTableReader class in the BCL and that you can use it to stub out IDataReader. I like this better because it removes the dependency the test had on Rhino.Mocks.
-
DataTable table = new DataTable();
-
DataRow row = table.NewRow();
-
table.Columns.Add(new DataColumn("ID"));
-
table.Columns.Add(new DataColumn("FullName"));
-
row["DirectoryUserID"] = Guid.Empty;
-
row["FullName"] = "Test User";
-
table.Rows.Add(row);
-
DataTableReader reader = new DataTableReader(table);
