Problem: You have Plain Old CLR Objects and you need to know how to use entity framework mapping feature to map your business entities and its
attribute to columns in a table.
Solution: Entity framework does not support complete persistence ignorance.
What I mean by persistence ignorance is the fact that you can use your objects with entity framework without knowing about the persistence provider being used. This way later if you decide to use your objects against new ado.net technology stack your business rules and code base does not need to change.
All ORM solutions in the market to some degree try to get closer to concept of complete persistence ignorance but in reality you are faced with some limitations enforced by the provider that you must adhere to work with the framework. Some of the limitations include specific constructor for tracking objects, inheriting from a base class which notifies the framework of how to track the object using a specific key value. In the case of entity framework, you are required to implement 3 interfaces: IEntityWithChangeTracker, IEntityWithKey,IEntityWithRelationships. Although you are not required to implement IEntityWithKey interface but is strongly recommended as it
improves performance for tracking an entity. When you use entity framework designer, all your generated classes inherit from a base class called
EntityObject. EntityObject handles all details of informing the framework about different attributes of an object. EntityObject implements the 3 interfaces we mentioned earlier. Apart from these interfaces, EntityObject also implements StructuralObject which its uses to set field values for properties.
To demonstrate how to use CLR objects with entity framework, we will create Customer and Orders class and map these objects to table in the database using entity framework. After completing the mapping we will create a data context class to query for these objects and entity framework will translate the query into appropriate sql defined by our mapping file.
Below is our Customer class which includes all the attributes and interface implementation to work with entity framework.
Listing 1-1 Customer entity implementing IEntityWithChangeTracker, IEntityWithKey interfaces.
namespace LinqCookBook.EFUsingPOCO {
[EdmEntityTypeAttribute
(NamespaceName="LinqCookBook.EFUsingPOCO",Name="Customer")]
public class Customer : IEntityWithChangeTracker, IEntityWithKey
{
//IEntity tracker is required to participate in change tracking.
private IEntityChangeTracker changetracker;
public void SetChangeTracker(IEntityChangeTracker changeTracker)
{
this.changetracker = changeTracker;
}
protected void ReportPropertyChanging(string propertyname)
{
if (changetracker != null) {
changetracker.EntityMemberChanging(propertyname);
} }
protected void ReportPropertyChanged(string propertyname)
{
[EdmScalarPropertyAttribute(IsNullable=false)]
public string CompanyName
} }
Discussion:
In the Listing 1-1, I have marked the class with EdmEntityTypeAttribute which tells entity framework that customer class in an entity.
EdmEntityTypeAttribute attribute takes two parameters. First parameter represents the namespace where the entity resides and second parameter specifies the name of the entity.Next I implement 2 interfaces
IEntityWithChangeTracker and IEntityWithKey which entity framework requires for persistence and tracking of customer class. The first interface IEntityWithChangeTracker is used to enable change tracking on the customer object. When you implement the interface you provide reference to
IEntityChangeTracker by implementing the SetChangeTracker method.
ChangeTracker is than by properties on the object to report changes. Listing 1-2 implements SetChangeTracker method on IEntityChangeTracker
interface.
Listing 1-2
private IEntityChangeTracker changetracker;
public void SetChangeTracker(IEntityChangeTracker changeTracker)
{
this.changetracker = changeTracker;
}
In the above code, I am setting the reference of my private variable changetracker to the value passed in the parameter of SetChangeTracker method. The reason I am keeping the reference of change tracker in my class is, when any of the property on my object gets changed, I will simply call changetracker.EntityMemberchanging passing in the name of the property to report changes. Since we want to notify the framework both before and after the property changes we will create two methods that will call
entitymemberchanging and entitymemberchanged in their respective methods as follows.
Listing 1-3
protected void ReportPropertyChanging(string propertyname)
{
if (changetracker != null) {
changetracker.EntityMemberChanging(propertyname);
} }
protected void ReportPropertyChanged(string propertyname)
In listing 1-3, I have declared two methods called ReportPropertyChanging and ReportPropertyChanged. ReportPropertyChanging first checks to see if we have a reference to changetracker. If the reference is not null we pass in the name of property that is about change by calling EntityMemberChanging on the changetracker object. Similarly when the property is changed
successfully we call ReportPropertyChanged method which ultimately calls EntityMemberChanged on changedtracker to notify that property has
changed.
Next interface Customer class implements is IEntityWithKey which exposes an entity key to object services. Entity Key is used by object services to identity and track objects. If you do not implement this interface, you would see considerable decrease in performance. In Listing 1-4, we are
implementing IEntityWithKey interface by exposing a getter and setter that sets the value entity key private variable.
Listing 1-4 implementing IEntityWithKey interface EntityKey entitykey;
public System.Data.EntityKey EntityKey
If your object is going to have navigation properties or relationship to other properties, you are also required to implement an interface called
IEntityWithRelationships which I have about earlier.
IEntityWithRelationships interface is used by a class to get reference to the relationship manager object. You then use that reference to access
relatedCollection such as orders for a customer. Similarly if you have an order class, you can access the customer reference by using relation manager object.
To keep the example simple and illustrate bare minimum requirements to use an object with entity framework, I am not exposing any relations in my
customer object class. In future recipes I will cover how to use relationship manager to access other entities related to Customer entity.
Apart from implementing the two interfaces, Customer class exposes 3 properties CustomerId,ContactName and CompanyName. To expose these properties to entity framework, we are marking the properties with
EdmScalarPropertyAttribute as shown in listing 1-5 Listing 1-5
In Listing 1-5, I am exposing my customerid property by attributing the property with EdmScalarPropertyAttribute. I am also passing two additional parameters to the attribute. First parameter EntityKeyProperty is set to true to tell that customerid is the key property on the customer object. Since
cutomerid cannot be null, I am also setting isnullable to false. The other properties on the Customer object also work the same way so I am not going to cover them. In the setter of all the 3 properties, I am calling
ReportingPropertyChanging and Changed to notify change tracker when a property is changing and has changed. This information is useful for the framework to identity the state of the object.
Once I have defined my customer object, I have to create 3 xml files. First file will be our conceptual schema which defines how our customer object and its properties. Second file would be our schema file that defines customer table in the database meaning what columns the table has, its datatype and what is the primary key column. The third file will be our mapping file which defines how to map our customer object to customer table in the database. Listing 1-6 shows conceptual schema of our customer object represented in xml format.
Listing 1-6 Customer object defined in NorthWindEFModel.csdl conceptual model
<EntitySet Name="Customers"
EntityType="LinqCookBook.EFUsingPOCO.Customer" />
</EntityContainer>
<EntityType Name="Customer">
<Key>
<PropertyRef Name="CustomerID" />
</Key>
<Property Name="CustomerID" Type="String"
Nullable="false" />
<Property Name="ContactTitle" Type="String" />
<Property Name="CompanyName" Type="String"
Nullable="false" />
</EntityType>
</Schema>
In listing 1-6, we start with EntityContainer that defines the namespace where customer object reside. Entity container has EntitySet which defines the
entities that we are going to be exposing through our objectcontext. In our case we are going to be exposing Customers which would be of Customer type. When we work on building our ObjectContext, you will see that we use Customers to query for customer object. Next we define our entity in
EntityType attribute by defining the key property in the customer table
followed by other properties with their data type and whether the property can allow null or not.
Next we define our customer schema in NorthwindModel.ssdl schema file as shown in listing 1-7. Schema file in listing 1-7 contains an entity set with Name being the table name and entity type being the type for customer. Than using the Entitytype element, I am defining my customer table with the
column information, column’s data type, length, and whether the column is nullable or not. I am also defining the primary key column in the customer table by using key element and specifying CustomerId as the PropertyRef.
Listing 1-7
<?xml version="1.0" encoding="utf-8" ?>
<Schema Namespace="NorthwindEFModel.Store"
Alias="Self"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl"
Provider="System.Data.SqlClient"
ProviderManifestToken="2008">
<EntityContainer Name="dbo">
<EntitySet Name="Customers"
EntityType="NorthwindEFModel.Store.Customers"/>
</EntityContainer>
<EntityType Name="Customers">
<Key>
<PropertyRef Name="CustomerID" />
</Key>
<Property Name="CustomerID" Type="nchar"
Nullable="false" MaxLength="5" />
<Property Name="CompanyName" Type="nvarchar"
Nullable="false" MaxLength="40" />
<Property Name="ContactTitle" Type="nvarchar"
MaxLength="30" />
</EntityType>
</Schema>
Once I have defined the storage model, I have to create a mapping file which defines how customer entity maps to customer table. Listing 1-8 shows the mapping file I have created. Mapping file has an attribute
CdmEntityContainer that defines the namespace where my entities reside.
Within in the EntityTypeMapping, I am specifiying my customer type with TypeName maps to Customers table defined by StoreEntitySet. Inside the MappingFragment, I am mapping scalar properties on customer entity to column name in customer table.
Listing 1-8
<EntitySetMapping Name="Customers">
<EntityTypeMapping TypeName="
LinqCookBook.EFUsingPOCO.Customer">
<MappingFragment StoreEntitySet="Customers">
<ScalarProperty Name="CustomerID"
ColumnName="CustomerID" />
<ScalarProperty Name="CompanyName"
ColumnName="CompanyName" />
<ScalarProperty Name="ContactTitle"
ColumnName="ContactTitle" />
Now that we have defined our conceptual modal, mapping and storage model, we need to create our datacontext class which can talk to the model we have defined in the xml file. Listing 1-9 shows the code for NorthwindEntities class. In the example, I am creating NorthWindEntities class which inherits from ObjectContext. ObjectContext class is responsible for querying and working with entity data as objects. The constructor of the class takes two parameters. First parameter represents the name of connectionstring defined in either web.config or app.config. For this example, I have defined the connectionstring inside app.config as follows
<add name="nwefpoco"
Second parameter defines the name of the entitycontainer we defined in our conceptual model. Now to query for customers, we are exposing a public property Customers which does lazy loading to load the customers by calling CreateQuery passing in the name of the entityset defined in our conceptual model.
Listing 1-9
namespace LinqCookBook.EFUsingPOCO {
public class NorthwindEntities:ObjectContext {
public NorthwindEntities()
: base("name=nwefpoco", "NorthwindEntities") { } ObjectQuery<Customer> customers;
public ObjectQuery<Customer> Customers {
To test if we can retrieve Customer objects from ObjectContext, we can write a simple query that retrieves Customers with Contact Title of Sales
Representative as shown below.
public static void CustomersWithSalesRepresentative() {
var db = new NorthwindEntities();
var custs = db.Customers.Where(c =>
c.ContactTitle == "Sales Representative");
foreach (var cus in custs) {
Console.WriteLine("cust {0}", cus.CustomerID);
} }