Friday, December 28, 2012

Getting Started With Entity Framework 4.3.1

Introduction
Entity Framework (EF) has evolved quite a bit and as it has evolved it has become more and more confusing to keep up with its changes which have been introduced a series of addons in the form of libraries. You can sit down and read pages of documentation to understand what has happened, but when you just need to dive into something because of time constraints it is frustrating. That has led me to ask the question "How do I install and use EF 4.3.1?" Well unfortunately it is not a one click install, it is a multi-step installation.

Assumptions
  • You are using Visual Studio 2010
  • You are using the .Net 4.0 Framework
  • You need to either create a new EDMX or you have an existing EDMX using the stock version of EF that comes with .Net 4.0 and VS2010 which is EF 4.0
  • You are using the database first method (versus code first method)
  • You have NuGet installed (this is required)
  • You have made a backup of your project before continuing (just in case things go sideways)
Can I used EF 5.0 instead?
If you are using Visual Studio 2010 (VS2010), then the answer is NO. EF 5.0 is supported in Visual Studio 2012 (VS2012) for .Net 4.5, if you have access to VS2012 then I say go for it. I haven't had a chance to move on yet myself.

Part A: Setting up a Project with an EDMX file
  1. If you don't already have an EDMX file, then either create a new project or use an existing one, and add a new item to your project (Right click on project file > Add > New Item...)
  2. Under "Installed Templates" (left hand side) select "Data" and then select ADO.Net Entity Data Model - name it however you see fit. Example: InventoryModel.edmx
  3. Follow the wizard and setup your edmx as you would normally - add which ever tables you need etc...
  4. After you have finished adding your edmx, it is time to install EF 4.3.1
Step 1

Step 2-4

Part B: Installing EF 4.3.1
Reminder: You must have NuGet installed to perform these steps
  1. Open up the NuGet Package Manager Console: Tools > Library Package Manager > Package Manager Console
  2. Make sure you have the correct project selected and at the command prompt enter the following command sans soft quotes: "Install-Package EntityFramework -Version 4.3.1" Where did you find this command? After pressing the enter key a bunch of stuff happens. Some of that stuff is a dll named "EntityFramework" is added a dll reference to your project. If you haven't used NuGet before then don't be alarmed by the config file named "packages.config", that is added when adding packages using NuGet.

Step 1

Step 2

Verification 1

Verification 2
Part C: Using EF 4.3.1 and a DBConext Generator of your Choosing
Say hello to the System.Data.Entity.DBContext Pattern, this is essentially what you have enabled yourself to use by installing EF 4.3.1, there are a number of new properties and awesome methods that have been added to the DBContext pattern. You are switching over from the System.Data.Objects.ObjectContext pattern which comes stock with EF 4.0 in VS2010.

Context Generators
You should use the right context generator for the job and each context generator has its own quirks. Really the defining line in my opinion on which context generator to use is by asking yourself, "Do I need to have an SOA or am I going to use WCF?" If the answer is yes, then you want to use a context generator with WCF support. If the answer is no, or you don't mind writing your own translation layer (converting your entities to POCO manually) then you can just use the regular DbContext Generator.

For argument's sake, we are going to use the DbContext Generator since it is basically the next step up from the original ObjectContext generator. It will not generate serializable objects, so don't bother with it for WCF.
  1. Add a new Code Generation Item by opening your EDMX file > right click on blank space > select "Add Code Generation Item..."
  2. Click on Online Templates located on the lower left hand side of the left pane.
  3. Click on "EF 4.x DbContext Generator for C#"
  4. Name it something different from your EDMX, like "DummyDBCModel.tt"
  5. Press the Add button - automatically this model will generate one file per entity and your old ObjectContext will no longer be valid. You will now have to use the new DbContext for any existing code (if you had any).

Step 1

Step 2-5
What Now?
You are probably thinking, "Okay great, I upgraded from 4.0 to 4.3.1, but what can I do that I couldn't do before?", well right off the bat there are two very useful things that I can name. There is now a Database Property, that will easily allow you to get your connection string, not too exciting, but not bad either. The second and most valuable thing in my opinion is the ability to selectively update fields/properties/columns when making an update to an entity.

I Upgraded and Everything is Broken Now!
If you were upgrading an existing project from EF 4.0 to 4.3.1, then yes, there is a good chance a lot of your code will be broken because the 4.0 syntax is different from the 4.3.1 syntax, but the idea remains the same it is still an ORM. For example in 4.0 using the ObjectContext to do an insert into a collection of Books you would write: context.Books.AddObject(book); but in 4.3.1 using the DBContext you will write context.Books.Add(book); - not a major difference, just syntactically different.

Useful Code Snippets
Here are some useful code snippets, specifically extension methods, for stuff you are probably going to do one way or another.

Code
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

/// <summary>
/// Get the connection string from the provided DbContext
/// </summary>
/// <param name="context">the target context</param>
/// <returns>the connection string this context is using</returns>
public static string GetConnectionString(this DbContext context)
{
 return context.Database.Connection.ConnectionString;
}

/// <summary>
/// Insert the entity of type T into the appropriate entity collection of type T
/// </summary>
/// <typeparam name="T">an entity of type T</typeparam>
/// <param name="context"></param>
/// <param name="entityObject">an entity of type T</param>
public static void Insert<T>(this DbContext context, T entityObject) where T : class
{
 context.Set<T>().Add(entityObject);
 context.SaveChanges();
}

/// <summary>
/// Update an entity of type T and only update the specified properties
/// </summary>
/// <typeparam name="T">an entity of type T</typeparam>
/// <param name="context"></param>
/// <param name="entityObject">an entity of type T</param>
/// <param name="properties">a strict list of properties to update</param>
public static void Update<T>(this DbContext context, T entityObject, params string[] properties) where T : class
{
 context.Set<T>().Attach(entityObject); //Attach to the context

 var entry = context.Entry(entityObject); //Get the Entry for this entity

 //Mark each property provided as modified. All properties are initially 
 //assumed to be false - further more properties cannot be marked as false, 
 //this is sadly a limitation of EF 4.3.1
 foreach (string name in properties)
  entry.Property(name).IsModified = true;

 context.SaveChanges();
}

/// <summary>
/// Mark an entity of type T as deleted. This is lazy deletion aka soft deletion. This method should only be used
/// if the entity has a Boolean Property named "Deleted".
/// </summary>
/// <typeparam name="T">an entity of type T</typeparam>
/// <param name="context"></param>
/// <param name="entityObject">an entity of type T</param>
public static void SoftDelete<T>(this DbContext context, T entityObject) where T : class
{
 //This method is the equivalent of setting the "Deleted" property to true and saving changes. 
 //Essentially this is a single property update.
 context.Set<T>().Attach(entityObject);

 var entry = context.Entry(entityObject);
 
 DbPropertyEntry p = entry.Property("Deleted");

 p.CurrentValue = true; //Mark this as deleted
 p.IsModified = true; //Mark this as modified

 context.SaveChanges();
}

/// <summary>
/// Permanently delete/remove an entity of Type T from it's corresponding entity collection.
/// </summary>
/// <typeparam name="T">an entity of type T</typeparam>
/// <param name="context"></param>
/// <param name="entityObject">an entity of type T</param>
public static void HardDelete<T>(this DbContext context, T entityObject) where T : class
{
 context.Set<T>().Attach(entityObject);
 context.Set<T>().Remove(entityObject);
 context.SaveChanges();
}

Usage Examples
using (TestDumpEntities context = new TestDumpEntities())
{
 Book obj = new Book();

 context.Insert(obj);

 obj.AuthorID = 10;
 obj.BookName = "A Walk in the Life of Foo";

 //Only update BookName, even though AuthorID was modified too.
 context.Update(obj, "BookName");

 context.SoftDelete(obj); //Set obj.Delete = true and update in the DB

 context.HardDelete(obj); //Complete remove this entity from the DB

 Console.WriteLine(context.GetConnectionString()); //Print out the connection string
}

DbContext Generator with WCF Support
If you require WCF support, then I strongly suggest using the DBContext Generator with WCF Support as opposed to the POCO generator. The classes that are generated are lightweight, so much so that you lose a little bit of functionality unfortunately. With the regular DbContext Generator when you load an entity, any relational properties (collections of other entities related by Foreign Key), those relational properties are loaded for you. With the DbContext Generator with WCF Support, those relational properties are not loaded for you, you have to explicitly state that you want them loaded in your lambda expressions or linq queries.

How to Explicitly Load a Relational Property
For demonstration purposes, let's say we have a Author and Book entities. Authors write multiple Books and therefore the Author Entity has a List<Book> property named Books. (Author.Books) - this is done in the database by taking the Author's Primary Key (PK) AuthorID and placing it as a Foreign Key (FK) in the Book table, also named AuthorID. An explicit FK relationship is created in the DB. This is construed as a Relational Property automatically by EF when generating the entities for the model. If you do not explicitly load the this property it will be empty. Therefore to achieve this do the following:

using System.Data.Entity;
using System.Linq;

//Example of returning a list of Author and loading each Author's Books property with their Books
using (TestDumpEntities context = new TestDumpEntities())
{
 return context.Authors
      .Where(x => x.AuthorID == 1)
      .Include(x => x.Books) //This will load your books for you
      .ToList();
}

Enjoy

No comments:

Post a Comment