Thursday, January 7, 2010

Optional parameters in .NET 4.0

If you're wondering how you can use optional parameters in .NET 4.0 - here's a brief starter post:

When using optional parameters, you need to put your required parameters BEFORE the optional parameters. You define a parameter as optional by assigning a value to it directly within the method signature:


public static void GetCar(string maker, string color = "red", int year = 2009)
{
...
}


Here maker is a required parameter, but color is optional with a default value of red. If you don't specify anything else, it will default to red, and same for the year. Optional parameters always need a default option.

We can call it like this:


GetCar("Toyota"); // returns a red 2009 toyota
GetCar("Toyota", "blue") //returns a blue 2009 toyota
GetCar("Toyota", "blue", 2010) // returns a blue 2010 toyota

Aside from optional parameters, in .NET 4.0 you can specify the order of parameters to call in, or in the case of multiple optional parameters, specify values for certain ones by using ":". Let's say in our above method, we just want to pass in the color, but use the default year. We can call it like this:


GetCar(maker : "Toyota", color: "blue");


Very useful.

Wednesday, December 30, 2009

How do I create type safe includes with Entity Framework?

A question you might come across is - "How do I include other entity sets when doing a query so as not to make multiple round trips to the database? Calling ".Include" in your LINQ statements provides eager loading when retrieving objects to the database. To take a step back: let's consider this scenario:

You have two linked tables - "Car" and "Maker". When you select a car object, you will have a linked entity called "Maker" and vice versa - if you select "Maker", you will have a linked entityset "Cars". If you try calling one of these objects, EF will automatically make a call to the database for you to return the linked object.

So you say, I don't want to do that! I want to get the Car, and the Maker in one shot to the database. You would think it'd be something like creating a join in the query, but not so. You need to call ".Include" like so:


using (var db = new DatabaseEntities())
return db.Cars.Include("Maker").Where(c => c.Color == "Red").FirstOrDefault();


or for query syntax:

return (from c in db.Cars.Include("Maker") where c.Color equals "Red" select c).FirstOrDefault();


The problem is we are passing a string into the Include. This is bad. So I set out on creating a type safe Include extension. Here it is:


public static IQueryable<T> Include<T>(this IQueryable<T> mainQuery, Expression<Func<T, object>> subSelector)
{
var selector = subSelector.Body as MemberExpression;
var expression = selector.Expression;
var includeString = new StringBuilder();

//Go through and insert parent expressions + "." (insert puts them before like prepend)
while (expression != null && expression.Type.Name != subSelector.Parameters[0].Type.Name)
{
includeString.Insert(0, ".");
includeString.Insert(0, expression.Type.Name);
expression = ((MemberExpression)expression).Expression;
}

//The actual name we were looking to get in the first place
includeString.Append(selector.Member.Name); //The base type we want to get

return ((ObjectQuery<T>)mainQuery).Include(includeString.ToString());
}

Basically we extend an IQueryable object. These are, for the most part, our ObjectSets (mapped to database tables). You pass in a lambda expression specifying the entity you want to retrieve eagerly, and it loops back from that and creates a string out of it. You can use it like this:

db.Cars.Include(c => c.Maker)

You can also call several layers down like so:

db.Cars.Include(c => c.Maker.Country)