In my last post, I showed how to use the code-first technology in Entity Framework 4.1 to create your business entity classes and integrate them into EF. I’ll complete that code-first process in this post and add a warning about a limitation in the process compared to Entity Framework’s original data-first process.
After defining your business entities, the next step in the code-first process is to create the factory that will transfer data (and updates) between the database and your business entity classes. You start creating your factory by defining a class that inherits from DbContext:
Public Class NorthwindModel
Once you’ve created the class, you need to add a property that returns a DbSet class of the classes that represent a row in the table. This example returns the OrderDetail class in a property called OrderDetails and Product class in in a property called Products:
Public Property OrderDetails As System.Data.Entity.DbSet(OfOrderDetail)
Public Property Products As System.Data.Entity.DbSet(OfProduct)
Of course, this factory class won’t be able to do much if you haven’t told it where your data is kept. The simplest solution is to add a connection string to your config file’s ConnectionStrings section with the same name as your factory class (“NorthwindModel” in this example). If you want complete control you can pass a connection string your DbContext object’s constructor. The most flexible way to handle that is to create a constructor for your factory that accepts a connection string and passes the string on to the constructor of the DbContext your factory inherits from:
Public Sub New(ByRef ConnectionString As String)
A simple LINQ query will tell you if you’ve successfully defined your classes and factory:
Dim dbc As New NorthwindModel("Data Source=.SQLEXPRESS…")
Dim res = (From ordl In dbc.OrderDetails
For Each prd In res
Debug.Print(prd.ProductName & ":" & prd.OrderLines.Count.ToString)
Updates work as you would expect them to with EF. This code, for instance, retrieves the first Product object and changes its UnitPrice before saving the changes:
Dim res = (From prd In dbc.Products
res.UnitPrice = 2
Since you’re now supporting updates, it makes sense to expand the OrderDetail class to include some business logic to ensure that only good data is entered. This code in the OrderDetail class’ UnitPrice property ensures that no one can enter negative numbers (or 0) for the price:
Private _UnitPrice As Int16
PublicProperty UnitPrice As Decimal
Set(value As Decimal)
If value < 1 Then
Throw New Exception("Invalid value for Price. Must be greater than 0.")
_UnitPrice = value
This design is a simple implementation of an object model for the Northwind database—effectively, I’ve implemented the Table Module pattern where there’s one table for each class and one class for each table (as discussed in Learning Tree’s design patterns and best practices for .NET course). There’s nothing wrong with that, and (for most applications) much of any application will use the Table Module pattern. However, using the code first development process described in this post (and the previous post) means that you can’t implement the Domain Object Model where a single table may, for instance, be represented by multiple classes. As the design patterns course demonstrates, those more functional models can be easily developed using EF’s data-first process.
As an example, there really are two kinds of products in the Northwind Products table: active products and inactive products (products that have been discontinued). The business logic for active and inactive products should be different—you shouldn’t be able to order an inactive product, for instance. An implementation of the Domain Object Model pattern would create a base Product class that was responsible for functionality shared by both active and inactive products. The next step in the pattern would be to create an ActiveProduct class that inherits from Product and contains all (and only) the code for active products. Finally, a second, InActiveProduct class that also inherits from Product would contain all (and only) the logic for Inactive products. While this is easy to implement in the data-first process, it isn’t possible in the code-first process: the DbContext object won’t allow the same class to appear twice in the DbSet properties.
But if what’s important to you is to integrate your code into Entity Framework (and you may already have the classes you’ll want to integrate and just need to add some data annotations to them), the code-first tools will let you do that quickly. And you’ll pick up all the functionality of EF and LINQ on the way.