Feature CTP Walkthrough: Code Only for the Entity Framework
Code Only Walkthru:
1) Create a Console Application called "CodeOnlyWalkthru":
2) Add a new Project to the "CodeOnlyWalkThru" solution:
3) Choose 'Class Library' and call the library "Entities":
4) Add a "Category" class to your Entities project:
Right click on the Entities project and add a class called "Category" then past this code into the class:
public class Category
{
private List<Product> _products;
public int ID { get; set; }
public string Name { get; set; }
public virtual List<Product> Products {
get
{
if (_products == null)
_products = new List<Product>();
return _products;
}
set
{
_products = value;
}
}
}
5) Generate the "Product" class:
Inside the "Category.cs" class right click on a reference to the non-existent Product class and choose "Generate > Class" from the context menu:
In the new Product class paste this code:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
}
Your project should now look like this:
We are keeping these entity classes in a separate project so they are compiled into an assembly that has no dependencies on the Entity Framework. The Entities assembly is therefore persistence ignorant, which is very important for some developers. The persistence aware code lives in a separate assembly which references the persistence ignorant assembly.
6) In the "CodeOnlyWalkThru" Project add a reference to the Entities Project:
7) In the "CodeOnlyWalkThru" project add a reference to "System.Data.Entity" and "Microsoft.Data.Entity.Ctp":
NOTE: In the long run we plan on merging the Code Only functionality into the core Entity Framework assembly. When we get to this work you would only need the first reference.
8) In the "CodeOnlyWalkThru" project add a new class called "ProductDBContext":
9) Put this code in the "ProductDBContext" class:
public class ProductDBContext: ObjectContext
{
public ProductDBContext(EntityConnection connection)
: base(connection, "ProductDBContext")
{
ContextOptions.DeferredLoadingEnabled = true;
}
public IObjectSet<Category> Categories
{
get { return CreateObjectSet<Category>(); }
}
public IObjectSet<Product> Products
{
get { return CreateObjectSet<Product>(); }
}
}
Since this class extends ObjectContext, it represents the shape of your model and acts as the gateway to your database. Notice that we create ObjectSets for both of our entity types. We also add a constructor that takes an EntityConnection, the ContextBuilder will fabricate an EntityConnection, which is a wrapper around the real database connection and the Entity Framework metadata, and pass it to this constructor when we ask it to create a new instance of our ProductDBContext. In the constructor we also configure the Entity Framework to enable DeferredLoading (aka LazyLoading).
10) Now write paste this code into your "Program" class:
SqlConnection connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;Initial Catalog=ProductDB;Integrated Security=SSPI;");
using (ProductDBContext context = ContextBuilder.Create<ProductDBContext>(connection))
{
if (!context.DatabaseExists())
context.CreateDatabase();
Category food = new Category { CID = 1, Name = "Food" };
Product bovril = new Product { ID = 1, Name = "Bovril" };
Product marmite = new Product { ID = 2, Name = "Marmite" };
Product vegemite = new Product { ID = 3, Name = "Vegemite" };
food.Products.Add(bovril);
food.Products.Add(marmite);
food.Products.Add(vegemite);
context.Categories.AddObject(food);
context.SaveChanges();
// Query the database
food = context.Categories.Single();
foreach (var product in food.Products)
Console.WriteLine(product.Name);
}
This code creates a context completely by convention, that connects to the ProductDB database on the local SQLExpress installation. If the ProductDB database doesn’t exist it will create it.
Finally once you have a context up and running you use it just like you normally would, in this case we create a Category and 3 Products related to each other, and then we retrieve the Category again from the database and use LazyLoading (aka DeferredLoading) to loop over its products
It is that easy.
11) Configure the ContextBuilder
So far we have done everything by convention because Code Only could infer everything by convention. If however we change the Category class so it looks like this:
public class Category
{
private List<Product> _products;
public int CID { get; set; }
public string Name { get; set; }
public virtual List<Product> Products {
get
{
if (_products == null)
_products = new List<Product>();
return _products;
}
set
{
_products = value;
}
}
}
CodeOnly doesn't know that CID is the key*. This means you have to create an instance of a ContextBuilder and use it to register CID as the key, like this:
var builder = new ContextBuilder<ProductDBContext>();
builder.RegisterKey((Category c) => c.CID);
You can Register many keys on the same builder if necessary. Once you have done configuring your builder, you can create an instance of the ProductDBContext using the Create method:
using (ProductDBContext context = builder.Create(connection))
The rest of the code is unchanged.
You should generally re-use your configured ContextBuilder whenever you need to create an ObjectContext, rather than re-configuring each time, as this will help with performance. The best way to do this is probably via a static variable somewhere.
*By convention any property called ID, Id, ClassNameID or ClassNameId is assumed to be the entity key. If a property with one of these names can't be found then you have to call RegisterKey() to tell the entity framework which property is a key.
Known Limitations of Code Only in the Feature CTP for Beta1
This release is a very early preview of Code Only, and as such has a number of known limitations, that we plan on addressing in subsequent releases, including:
- ComplexTypes are not supported.
- Configurable Mapping is not supported.
- Table per Hierarchy is the only inheritance strategy supported.
- Specifying Facets is not supported, so for example you can't specify the max length of a string column in the database. Instead defaults are used.
- There is no Provider Model, so CodeOnly only supports SqlServer at this point.
- CodeOnly provides no way to specify which properties participate in the same relationship but are simply the inverse of each other. So for example if you added a Category property to the Product class used in the example above the result would be 2 relationships between Product & Category, and consequently 2 FKs in the Products table, because the Entity Framework can't infer that Product.Category and Category.Products are simply the inverse of each other.
- ManyToMany relationships are not supported.
- Alex James
Program Manager, Entity Framework