EF CTP4 Tips & Tricks: Find

Posted on July 15, 2010. Filed under: Entity Framework, Visual Studio | Tags: , , , , |

This is the second in a series of posts taking a quick look at the some of the features in the recent Entity Framework Feature CTP4, also recently dubbed “EF Magic Unicorn Edition”. In the last post we looked at Include with lambda and today we are going to look at the new Find method.

The Problem

Let’s say I want to get a hold of the “FOOD” category. Prior to having the Find method I can do this pretty easily using LINQ:

var food = context.Categories.Single(c => c.CategoryId == "FOOD");

Actually the above line of code is going to query the database every time it runs so I should really use the TryGetObjectByKey method to avoid the query if “FOOD” is already loaded into memory. TryGetObjectByKey isn’t strongly typed so I need to supply an out parameter that is typed as object and then cast if it was found.

Category food;
object searchFood;
if (context.TryGetObjectByKey(key, out searchFood))
{
    food = (Category)searchFood;
}

Now let’s update the code to create the “FOOD” category if it doesn’t exist:

Category food;
object searchFood;
if (context.TryGetObjectByKey(key, out searchFood))
{
    food = (Category)searchFood;
}
else
{
    food = new Category { CategoryId = "FOOD", Name = "Food Products" };
    context.Categories.Add(food);
}

This works fine if I only run the code once but what if I want to find the “FOOD” category again? I now need to account for the fact that it might be sitting in the context in an added state. In this case it has a temporary key so TryGetObjectByKey won’t find it. That’s OK I have access to the ObjectStateManager to check for added objects:

Category food;
object searchFood;
if (context.TryGetObjectByKey(key, out searchFood))
{
    food = (Category)searchFood;
}
else
{
    food = context
        .ObjectStateManager
        .GetObjectStateEntries(System.Data.EntityState.Added)
        .Where(ose => ose.Entity != null)
        .Select(ose => ose.Entity)
        .OfType<Category>()
        .Where(c => c.CategoryId == "FOOD")
        .SingleOrDefault();

    if (food == null)
    {
        food = new Category { CategoryId = "FOOD", Name = "Food Products" };
        context.Categories.Add(food);
    }
}

The Solution

If you feel like you shouldn’t have to write that much code to achieve this fairly simple task you are definitely not alone. That block of code above can now be replaced with:

var food = context.Categories.Find("FOOD");
if (food == null)
{
    food = new Category { CategoryId = "FOOD", Name = "Food Products" };
    context.Categories.Add(food);
}

The rules for find are:

  1. Look for an entity with the supplied key that has already been loaded from the database
  2. If there isn’t one then check if there is an added entity that has the supplied key
  3. Finally query the database and if there still isn’t one that matches then return null

In CTP4 Find is an instance method on DbSet<T> so you do have to be using the new “Productivity Improvement” surface to get the benefit at least for the moment.

Composite Keys

Find takes a “params object[]” as it’s parameter so if you have composite keys you just specify the values for each key property:

var plate = context.LicensePlates.Find("WA", "555-555");

Code First requires that you specify the ordering of composite keys, you can do this via the Fluent API:

public class DMVContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<LicensePlate>().HasKey(p => new { p.State, Plate = p.Number });
    }

    public DbSet<LicensePlate> Plates { get; set; }
}

Or via attributes in you class:

public class LicensePlate
{
    [DataMember(Order = 0)]
    public string State { get; set; }

    [DataMember(Order = 1)]
    public string Number { get; set; }
}

“Yes” that is DataMember from the System.Runtime.Serialization namespace and “no” we shouldn’t make you add a reference to System.Runtime.Serialization.dll just to specify key ordering… we’ll fix that.

The Future

We’ve also looking at modifying the Add method so that it returns the newly added entity, this means we could reduce the code down to one line:

var food = 
    context.Categories.Find("FOOD") 
    ?? context.Categories.Add(new Category { CategoryId = "FOOD", Name = "Food Products" );

Ok ok that’s a bit long to actually put on one physical line… but you get the idea.

Summary

Finding objects based on primary key value(s) used to be a fairly painful exercise that required using advanced API surface… now it’s a lot simpler and encourages you to write performant code that reduces hits to the database.

About these ads

Make a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

3 Responses to “EF CTP4 Tips & Tricks: Find”

RSS Feed for RoMiller.com Comments RSS Feed

Very awesome stuff!

Mr. Miller, you guys are incredible. I just love this Code First stuff. Life is easy and efficient for once.

How do you use DbSet.Find() with Include lambda’s to get a deep object graph?
thanks
Marty


Where's The Comment Form?

    About

    Rowan works as a Program Manager for the ADO.NET Entity Framework team at Microsoft. He speaks at technical conferences and blogs at romiller.com. Rowan lives in Seattle, Washington with his wife Athalie. Prior to moving to the US he resided in the small state of Tasmania in Australia. Outside of technology Rowan's passions include snowboarding, mountain biking, horse riding, rock climbing and pretty much anything else that involves being active. The primary focus of his life, however, is to follow Jesus.

    RSS

    Subscribe Via RSS

    • Subscribe with Bloglines
    • Add your feed to Newsburst from CNET News.com
    • Subscribe in Google Reader
    • Add to My Yahoo!
    • Subscribe in NewsGator Online
    • The latest comments to all posts in RSS

    Meta

Liked it here?
Why not try sites on the blogroll...

Follow

Get every new post delivered to your Inbox.

Join 163 other followers

%d bloggers like this: