LINQ to SQL: a three-month checkpoint

Posted on January 19th, 2008 in .NET, LINQ by Jon Kruger

We are now about 3 months into our project using LINQ to SQL. Our project is a Winforms app using SQL Server 2005 (LINQ to SQL only works with SQL Server). We are planning on moving to an n-tier system with a WCF service layer, but for now our application talks directly to the database. Even though we don’t have the service layer in there yet, we’re architecting the system as if we had the service layer in there so we’re having many of the same issues that we will have when actually have the service layer.

Microsoft hasn’t always been known for stellar 1.0 releases (e.g. Vista, the Zune, etc.). When it comes to something that’s in the .NET Framework, I had a little more faith because it’s a little harder for them to go back and fix something if they screw it up. I figured that because of that, they’ll make sure that they get it right.

LINQ to SQL is not complete. There are some issues that Microsoft knows about that LINQ to SQL doesn’t currently handle. None of these issues are show-stoppers. While we’ve had to jump through some hoops to get around these issues, but we’ve been able to do everything that we’ve needed to do. I’ll get into more detail on the hoop-jumping in later posts.

Even with all of these issues, I give LINQ to SQL a rousing endorsement. I’ve always been an ORM fan, and I’ve used Nhibernate on several projects.

Getting Started

When we started our project, we inherited a legacy database that has been developed over the last 10 years. There are some interesting things in the database, such as numerics being used as primary keys, tables that aren’t normalized, and spotty referential integrity.

For the first week, three of us dragged all of the tables onto the LINQ to SQL designer and renamed all the properties to more friendly names. This was a fairly painless process. Now we had all of our entity objects created and ready to go.

Well, almost. We created a BusinessEntityBase class and all of the entity objects derive from this class. We do this by creating partial classes that match up with the classes generated by LINQ to SQL (all of the classes generated by LINQ to SQL are partial classes) and specifying that those classes derive from BusinessEntityBase. We don’t have much in the BusinessEntityBase class — the main thing in there is an abstract Id property that each entity must override to specify the value of the primary key. We use this to keep track of whether an entity object is unsaved or not.

At this point, we were ready to start working! All of our entity objects were generated for us. Contrast this with Nhibernate, where we had to write (or generate) all of our entity objects and the Nhibernate mapping files. It takes most people a long time to figure out how to write those Nhibernate mapping files!

Working with LINQ

“LINQ” is the general term for the syntax that we now use to write queries. These queries can be executed against a database (LINQ to SQL), a collection (LINQ to Objects), and various other things (LINQ to Amazon).

The LINQ syntax and particularly lambda expressions were very foreign concepts at first. You’re just not used to using those types of things in C# code. Then one day is just clicks, and you start discovering all kinds of new ways to use LINQ queries and lambda expressions.

Personally, I think lambda expressions are more revolutionary than the LINQ syntax. They don’t provide you with anything that you couldn’t do in .NET 2.0 with anonymous delegates, but now the syntax is much more concise. You can do what you want to do in fewer lines of code, which also makes for more readable code. Here’s an example of why I like lambda expressions.

Let’s say that I’m working with everyone’s favorite sample database (Northwind) and I want to find a Employee by first name, last name, or both. In the past, you probably wrote a stored procedure that looked like this:

create procedure EmployeeSearch
     @FirstName varchar(20),
     @LastName varchar(20)
as
select EmployeeID, FirstName, LastName, Title, TitleOfCourtesy,
    BirthDate, HireDate, Address, City, Region, PostalCode, Country,
    HomePhone, Extension, Photo, Notes, ReportsTo, PhotoPath
from Employees
where (@FirstName is null or FirstName = @FirstName)
and (@LastName is null or LastName = @LastName)

That worked fine, but having to check if the parameters are null is a performance hit in the stored procedure, and someone had to write the stored procedure in the first place.

With lambda expressions and LINQ to SQL, you can now do something like this and build your query incrementally:

public IQueryable<Employee> SearchEmployees(string firstName, string lastName)
{
    NorthwindDataContext dc = new NorthwindDataContext();
 
    // We'll start with the entire list of employees.
    IQueryable<Employee> employees = dc.Employees;
 
    if (!string.IsNullOrEmpty(firstName))
    {
        // Filter the employees by first name
        employees = employees.Where(e => e.FirstName == firstName);
    }
 
    if (!string.IsNullOrEmpty(lastName))
    {
        // Filter the employees by last name
        employees = employees.Where(e => e.LastName == lastName);
    }
 
    return employees;
}

Why is this better?

  • We didn’t have to write a separate stored procedure.
  • The generated SQL code won’t have to check for NULL parameters passed into a stored procedure.
  • This code is much more testable and easier to read than a stored procedure (IMO).
  • This is compiled, type safe code!

Here is the SQL code that LINQ to SQL runs as a result of this method:

SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title], [t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension], [t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath]
FROM [dbo].[Employees] AS [t0]
WHERE [t0].[LastName] = @p0
-- @p0: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [Kruger]

I’m not saying that stored procedures are obsolete. There will still be cases where you have a query that is so complex that it’s easier to do it in a stored procedure, or it may not be possible to do it in LINQ at all. But LINQ to SQL is allowing me to scrap many of the stored procedures that I used in the past.

More to come…

Over the next few weeks, I’ll post in more detail about how we are using LINQ to SQL and some of the things we’ve had to do to make it work.

Why I should’ve gone to CodeMash last year

Posted on January 13th, 2008 in Uncategorized by Jon Kruger

I went to CodeMash this year. Last year I did not. Sure, I knew that there would be a lot of good talks, but can’t I earn the same information by reading books and blogs, listening to podcasts, etc.?

Now I see why I was wrong. People I work with talk about the value of being involved in the .NET community, and now I see why they are right.

Sure, the talks were great. But by far the best part is being able to sit down with people who know way more than me and ask them about problems that I’m having right now on my current project. That kind of free advice is invaluable.

In the technology world, there is always tons of new stuff out there, and there’s no way that I can keep up with it all (especially with a wife and a kid on the way). If I want to be someone who can make good architectural decisions, how can I do that without having knowledge of what’s out there? Since I can’t keep up with it all, I could use some other people that can help out.

So I plan on trying to be more involved in the local .NET community (user groups, blogging, etc.), and I’m really excited about it. Hopefully I can make some worthwhile contributions of my own while I’m at it.

Minor batch file tricks

Posted on January 7th, 2008 in Uncategorized by Jon Kruger

Just so I don’t forget how to do these things…

Remove quotes:
SET Line=”C:\Program Files\”
SET Line=%Line:”=%
echo %Line% — will output: C:\Program Files\

Remove a trailing backslash:
SET Line=C:\Windows\
IF “%Line:~-1%”==”\” SET Line=%Line:~0,-1%
echo %Line% — will output: C:\Windows

Singing the praises of continuous integration

Posted on December 20th, 2007 in .NET by Jon Kruger

One thing that almost every project seems to struggle with is unit testing. Not so much the writing of said tests as much as the fact that inevitably you will get really busy and no one will run all of the unit tests for a week or so, and then all of a sudden you decide to run them and you find out that half of them are failing.

Many times these are easy to fix, but when there are so many failing, you don’t have time to dive in and fix them all. So no one fixes them, and the unit tests become pretty much worthless because you can’t count on any of them.

At this point, many people stop writing unit tests because they’re not used to ever running unit tests anymore, so they forget about writing them altogether.

Someone will eventually decide that the unit tests need to be fixed, so someone will spend an entire week fixing them all up. But by then the code coverage is lacking because people had stopped writing them (see above). You don’t have time to add tests at that point because you’re a week behind because you spent a week fixing unit tests.

For the first time I am working on a project where none of this is happening. Much of the credit goes to my co-workers, who do a great job of writing lots of good tests and keeping them up. But what is going to keep everything going is using continuous integration with TFS 2008.

I know, some people are getting ready to click the comment button and say that they’ve been doing CI with CruiseControl for years. CruiseControl is great, I won’t deny that.

Much to my surprise, it took me no more than 10 minutes to set up our CI build using TFS 2008. Now I can look at a dashboard screen and see that all 684 of our unit tests have been passing all day. If a check-in causes a test to break, everyone gets an email saying so and TFS automatically creates a bug for the person that broke it. So we stop and fix the tests right away and we get back to work.

Next up is to figure out how can configure our USB Missile Launcher to automatically shoot someone when they break the tests!

Frameworks and layers

Posted on November 5th, 2007 in Architecture by Jon Kruger

I’ve recently started on a new project where we are rewriting a legacy app basically from scratch. This means that the first coding task is to set up all of the architecture, frameworks, and layers that we will use for the rest of the project.

I think that this is the most crucial time in the life of the project. The work that is done now will affect many things, including:

  • How long it will take to develop the rest of the application
  • How long it will take for other developers to understand the architecture (learning curve) so that they can be productive
  • How easy it will be to add new features and make changes several years later (after many of the original developers are no longer on the project)
  • Whether or not the project ultimately succeeds

Those are some important issues! So we better get it right!

Many of the problems I have seen have come from having too many unnecessary layers to deal with in the project. Let’s look at some common rationalizations for layers and tiers in project architecture.

“By separating the business entities and logic from the database code, we can insulate the business code so that it can work with any database.”

I would hope that almost every application does this one. I was on a project that used NHibernate and we were able to rip out Oracle and replace it with SQL Server in a day or so. This separation is fairly easy to implement, especially if you use an O/R mapper. It also lets you write unit tests against the business layer so that you can make sure that database changes don’t break your application.

“We’ll have a separate UI layer and business layer and keep the validation code out of the UI.”

This is good on many fronts — you are able to write a new UI and use the existing business layer (e.g. put a web front end on top of a business layer that was used in a Winforms app, or maybe you’re really brave and you move from Winforms to WPF!). Also, in most cases you will need certain pieces of validation and logic code that can’t just live in one screen on the UI, and putting validation logic in the UI makes it impossible to unit test.

“Let’s write our data layer so that we could plug in any database with any schema so that it will work without our business layer knowing that anything changed.”

I really have to question this one. Let’s say that there is a possibility that we could have another database schema that we might have to plug in. But let’s think about this some more.

  • Honestly, what are the chances that this will ever happen? If we’re going to invest a significant amount of time in order to have this flexibility, we better be pretty sure that we’re going to have to plug in another database someday.
  • Even if we did someday have to plug in a different database schema, would we even be able to get it working? Can you really switch to a drastically different database schema and have the app be fast enough? What if the new database has different constraints and foreign keys?
  • Would it be easier to write some kind of integration that would bring the data from the new database into our database? No one likes to write integration code, but it gets the job done and it won’t affect the performance of the application.
  • Having a bulkier architecture to deal with will affect how fast you can develop pretty much any feature that you want to add to the application. Also, when a new developer joins your team, it will affect how fast they can get up to speed (and they might be bitter at you for making their life more painful).

We need to always remember why we are writing code in the first place — to provide business value. Sure, writing applications can be fun, but they’re not paying us just to have fun!

Frameworks and layers are meant to serve us… if you feel like you’re a slave to your framework, maybe you need to rethink how your project is structured.

I like to keep things simple. Use existing code libraries whenever possible (such as Enterprise Library, an O/R mapper, etc.). Don’t create some extra layer just because of what we might have to do in the future, when we don’t even know if we’re going to have to do it. Design your application to be flexible so that you can adapt to change, but don’t burden yourself with something that is just going to make everything more difficult in the process.

Creating a custom handler for the Policy Injection Application Block

Posted on November 4th, 2007 in .NET by Jon Kruger

I’ve thought for awhile that the Policy Injection Application Block looked interesting, but now I’ve finally had a chance to use it. The basic idea is that you can wrap a method call with a “handler” which will execute custom code before and after the actual method is executed. The block comes with a bunch of handlers out of the box, but you can also add custom handlers that you can use either by putting a custom attribute on a method or by adding to the configuration file. This post explains in more detail how to use the Policy Injection Application Block.

I’ve taken the Policy Injection Quick Start solution and added a custom handler as an example. Here’s a quick overview of what I did:

  • Added four files:
    • MyHandler.cs - this is the file where you will write the custom code that you want to execute before and after the actual method call.
    • MyHandlerAssembler.cs - creates a MyHandler object from a configuration object.
    • MyHandlerAttribute.cs - attribute that creates a MyHandler object when placed on a class, method, or property.
    • MyHandlerData.cs - stores data from custom attributes when handlers are created in the configuration file.
  • There are two ways to add a handler, and either way will accomplish the same purpose.
    • Place a [MyHandler] attribute on a method, property, or class. In the BankAccount.cs class, I decorated the Deposit() method with a [MyHandler] attribute. If you run the application, click the Deposit button, enter a value, and click OK, code in MyHandler.Invoke() will be executed and you’ll see some stuff in the output window.
    • Add to the <policyInjection> section of the app.config file. If you open the app.config file and search for “My Custom Stuff” you will find the section that I have added. If you run the app and click the “Balance Inquiry” button, code in MyHandler.Invoke() will be executed and you’ll see some stuff in the output window.

Here is the quick start project with my changes included.

Hope this helps!

IQueryable<T> vs. IEnumerable<T> in LINQ to SQL queries

Posted on October 19th, 2007 in LINQ by Jon Kruger

I ran into something interesting today when working with LINQ to SQL. Take a look at these three code snippets:

#1:
NorthwindDataContext dc = new NorthwindDataContext();
IEnumerable<Product> list = dc.Products
     .Where(p => p.ProductName.StartsWith("A"));
list = list.Take<Product>(10);
Debug.WriteLine(list.Count<Product>());

#2:
NorthwindDataContext dc = new NorthwindDataContext();
IEnumerable<Product> list2 = dc.Products
     .Where(p => p.ProductName.StartsWith("A"))
     .Take<Product>(10);
Debug.WriteLine(list2.Count<Product>());

#3:
NorthwindDataContext dc = new NorthwindDataContext();
IQueryable<Product> list3 = dc.Products
     .Where(p => p.ProductName.StartsWith("A"));
list3 = list3.Take<Product>(10);
Debug.WriteLine(list3.Count<Product>());

What would you expect the generated SQL statements of all of these to be? I was expecting something like this:

SELECT TOP 10 [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
FROM [Products] AS [t0]
WHERE [t0].[ProductName] LIKE @p0

Which is what you get in #2 and #3, but not in #1, where you get this:

SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID], [t0].[QuantityPerUnit], [t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder], [t0].[ReorderLevel], [t0].[Discontinued]
FROM [Products] AS [t0]
WHERE [t0].[ProductName] LIKE @p0

(notice the “TOP 10″ is missing)

I’m not exactly sure what is going on under the hood, but it appears that when you do a query (or a portion of a query in this case), you cannot save the result as an IEnumerable, add to the query in a later statement (as I did in #1), and have LINQ to SQL know that it needs to combine the statements into one query. However, if you store the first part of your query as IQueryable, then you can add onto that query later (before it actually gets executed, of course).

Bottom line — if you have a LINQ to SQL statement and you can save it as an IQueryable instead of an IEnumerable, do so so that you have flexibility to continue to add onto that query.

I’ve been published!

Posted on July 24th, 2007 in Uncategorized by Jon Kruger

How to step into NHibernate code

Posted on June 29th, 2007 in .NET, NHibernate by Jon Kruger

In 10 minutes you can be stepping into NHibernate code to see exactly what’s it’s doing with your project. Here’s how to do it:

1) Go download the NHibernate source code from here. As I’m writing this, the latest release version is 1.2.0.GA. (I’m assuming here that you already have NHibernate set up to work with your application.)
2) Unzip the source to a location on your machine.
3) Open the NHibernate.sln file in Visual Studio that corresponds to the version of the .NET framework that you are using (1.1 or 2.0). If you really want to build everything, including the Iesi.Collections assemblies and all the unit tests, then open the NHibernate-Everything.sln file, but for me this is overkill because I usually only want to step into the NHibernate code. If you build NHibernate-Everything.sln, you’ll have to have NUnit 2.2.8 installed to build the unit test projects.
4) Make sure the Debug configuration is selected and build the entire solution.
5) If you open the \src\NHibernate\bin\Debug-2.0 (or Debug-1.1) folder, you will see all of the assemblies along with the .pdb files (if you just build the NHibernate-1.1/-2.0.sln file, you will only see one .pdb — NHibernate.pdb). Copy all of the .dll and .pdb files to the location where you currently have your NHibernate .dll files in your project.

Now when you debug your project, you should be able to step into NHibernate code!

Obvious caveat: we’re assuming that the source code that you downloaded is the exact same code that they used to build the release assemblies. I don’t see any reason why this wouldn’t be the case, but I’ve run into this problem before with other third party packages (like Infragistics NetAdvantage controls). So it might not be a bad idea to remove the debug NHibernate dlls when you’re done stepping into them and put the release dlls back in so that you don’t get burned.

Hope this helps! If you encounter any problems with this process, please let me know.

Winamp’s equalizer no longer works on DRM music files

Posted on June 29th, 2007 in Uncategorized by Jon Kruger

Winamp has been my media player of choice for years, and for two very simple reasons — global hotkeys that allow me to change what’s playing without opening up the app, and the Classic Skin which is really really small so that it doesn’t take up lots space on my desktop with some gaudy looking interface. No other media player has these things (even though someone could probably code something like this in a day or two).

I installed Winamp the other day (I had just been listening to music on my mp3 player for awhile) and I discovered that the equalizer no longer works on DRM tracks! I thought, there has to be some mistake. Without the equalizer your music doesn’t sound right at all.

Apparently there is no mistake. It sounds like AOL (which now owns Winamp) was in danger because it was fairly easy to create Winamp plugins that would allow you to strip DRM out of your music files. Because of this, AOL was getting pressure, so they first completely took away the ability to play WMA files in Winamp and eventually fixed so that you could play WMA again — but now the equalizer doesn’t work.

So now because Winamp was a little too user-friendly, Microsoft and the music industry are playing hardball and are forcing us to use their inferior media players. I never really minded DRM because DRM helped bring about iTunes and other online music stores without the mass piracy of the last 90’s, but when something like this happens, it’s really frustrating.

« Previous PageNext Page »