0% found this document useful (0 votes)
32 views127 pages

Entity Framework-Full

Uploaded by

Lim Siew Ling
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views127 pages

Entity Framework-Full

Uploaded by

Lim Siew Ling
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 127

ADO.

NET Entity Framework


[http://msdn.microsoft.com/en-us/library/bb738553.aspx]
The ADO.NET Entity Framework is designed to enable developers to create data access applications by
programming against a conceptual application model instead of programming directly against a relational storage
schema. The goal is to decrease the amount of code and maintenance required for data-oriented applications.
Entity Framework applications provide the following benefits:

 Applications can work in terms of a more application-centric conceptual model, including types with

inheritance, complex members, and relationships.

 Applications are freed from hard-coded dependencies on a particular data engine or storage schema.

 Mappings between the conceptual model and the storage-specific schema can change without changing

the application code.

 Developers can work with a consistent application object model that can be mapped to various storage

schemas, possibly implemented in different database management systems.

 Multiple conceptual models can be mapped to a single storage schema.

 Language-integrated query support provides compile-time syntax validation for queries against a

conceptual model.

In This Section

Introducing the Entity Framework


Provides an overview of the features and functionality introduced in the ADO.NET Entity Framework.

Getting Started (Entity Framework)


Describes how to design and create an Entity Framework application.

Programming Guide (Entity Framework)


Contains conceptual topics, how-to's and tutorials that demonstrate the capabilities of the Entity
Framework.

Feature Reference (Entity Framework)


Contains conceptual content describing the Entity Data Model, Object Services, and Entity SQL.

Samples (Entity Framework)


Contains sample applications demonstrating the features and functionality of the Entity Framework.

Entity Framework Terminology


Describes terminology used in the Entity Framework.

Introducing the Entity Framework


The Entity Framework is a set of technologies in ADO.NET that support development of data-oriented software
applications. Architects and developers of data-oriented applications have struggled with the need to achieve two
very different objectives. They must model the entities, relationships, and logic of the business problems they are
solving, and they must also work with the data engines used to store and retrieve the data. The data may span
multiple storage systems, each with its own protocols; even applications that work with a single storage system
must balance the requirements of the storage system against the requirements of writing efficient and
maintainable application code.

The Entity Framework enables developers to work with data in the form of domain-specific objects and properties,
such as customers and customer addresses, without having to concern themselves with the underlying database
tables and columns where this data is stored. This is enabled by elevating the level of abstraction at which
developers can work when they deal with data and reducing the code that is required to create and maintain data-
oriented applications. Because the Entity Framework is a component of the .NET Framework, Entity Framework
applications can run on any computer on which the .NET Framework 3.5 Service Pack 1 (SP1) is installed.

Giving Life to Conceptual Models

A longstanding and common design pattern for data modeling is the division of the data model into three parts: a
conceptual model, a logical model, and a physical model. The conceptual model defines the entities and
relationships in the system that is being modeled. The logical model for a relational database normalizes the
entities and relationships into tables with foreign key constraints. The physical model addresses the capabilities of
a particular data engine by specifying storage details such as partitioning and indexing.

The physical model is refined by database administrators to improve performance, but programmers writing
application code primarily confine themselves to working with the logical model by writing SQL queries and calling
stored procedures. Conceptual models are generally used as a tool for capturing and communicating the
requirements of an application, frequently as inert diagrams that are viewed and discussed in the early stages of a
project and then abandoned. Many development teams skip creating a conceptual model and begin by specifying
tables, columns, and keys in a relational database.

The Entity Framework gives life to conceptual models by enabling developers to query entities and relationships in
the conceptual model while relying on the Entity Framework to translate those operations to data source-specific
commands. This frees applications from hard-coded dependencies on a particular data source. The conceptual
model, the storage model, and the mapping between the two are expressed in an external specification, known as
the Entity Data Model (EDM).The storage model and mappings can change as needed without requiring changes to
the conceptual model, data classes, or application code. Because storage models are provider-specific, you can
work with a consistent conceptual model across various data sources.

An EDM is defined by the following three model and mapping files that have corresponding file name extensions:

 Conceptual schema definition language file (.csdl) - defines the conceptual model.

 Store schema definition language file (.ssdl) - defines the storage model, which is also called the logical

model.

 Mapping specification language file (.msl) - defines the mapping between the storage and conceptual

models.

The Entity Framework uses these XML-based model and mapping files to transform create, read, update, and
delete operations against entities and relationships in the conceptual model to equivalent operations in the data
source. The EDM even supports mapping entities in the conceptual model to stored procedures in the data source.
For more information, see Data Modeling in the Entity Framework.

Mapping Objects to Data


Object-oriented programming poses a challenge for interacting with data storage systems. Although the
organization of classes frequently closely mirrors the organization of relational database tables, the fit is not
perfect. Multiple normalized tables frequently correspond to a single class, and relationships between classes are
represented differently from relationships between tables. For example, to represent the customer for a sales
order, an Order class uses a property that contains a reference to an instance of a Customer class, but an Order
table row in a database contains a foreign key column (or set of columns) with a value that corresponds to a
primary key value in the Customer table. A Customer class might have a property named Orders that contains a
collection of instances of the Order class, but the Customer table in a database has no comparable column.

Existing solutions have tried to bridge this gap, which is frequently called an "impedance mismatch", by only
mapping object-oriented classes and properties to relational tables and columns. Instead of taking this traditional
approach, the Entity Framework maps relational tables, columns, and foreign key constraints in logical models to
entities and relationships in conceptual models. This enables greater flexibility both in defining objects and
optimizing the logical model. The Entity Data Model tools generate extensible data classes based on the conceptual
model. These classes are partial classes that can be extended with additional members that the developer adds.
The classes that are generated for a particular conceptual model derive from base classes that provide Object
Services for materializing entities as objects and for tracking and saving changes. Developers can use these classes
to work with the entities and relationships as objects related by navigation properties. For more information about
Object Services, see Object Services Overview (Entity Framework).

Accessing and Changing Entity Data

More than just another object-relational mapping solution, the Entity Framework is fundamentally about enabling
applications to access and change data that is represented as entities and relationships in the conceptual model.
Object Services uses the EDM to translate object queries against entity types that are represented in the
conceptual model into data source-specific queries. Query results are materialized into objects that Object Services
manages. The Entity Framework provides the following ways to query an EDM and return objects:

 LINQ to Entities - provides Language-Integrated Query (LINQ) support for querying entity types that are

defined in a conceptual model. For more information, see LINQ to Entities Overview.

 Entity SQL - a storage-independent dialect of SQL that works directly with entities in the conceptual model

and that supports EDM features such as inheritance and relationships. Entity SQL is used both with object
queries and queries that are executed by using the EntityClient provider. For more information, see Entity
SQL Overview.

 Query builder methods - enables you to construct Entity SQL queries using LINQ-style query methods. For

more information, see Query Builder Methods (Entity Framework).

The Entity Framework includes the EntityClient data provider. This provider manages connections, translates entity
queries into data source-specific queries, and returns a data reader that Object Services uses to materialize entity
data into objects. When object materialization is not required, the EntityClient provider can also be used like a
standard ADO.NET data provider by enabling applications to execute Entity SQL queries and consume the returned
read-only data reader. For more information, see EntityClient Provider for the Entity Framework.

The following diagram illustrates the Entity Framework architecture for accessing data:
The Entity Framework generates a class derived from ObjectContext that represents the entity container in the
conceptual model. This object context provides the facilities for tracking changes and managing identities,
concurrency, and relationships. This class also exposes a SaveChanges method that writes inserts, updates, and
deletes to the data source. Like queries, these changes are either made by commands automatically generated by
the system or by stored procedures that are specified by the developer. For more information, see Adding,
Modifying, and Deleting Objects (Entity Framework).

Entity Data Model Tools

Together with the Entity Framework runtime, the .NET Framework 3.5 SP1 includes EDM Generator (EdmGen.exe).
This command prompt utility connects to a data source and generates an EDM based on a one-to-one mapping
between entities and tables. It also uses a conceptual model file (.csdl) to generate an object layer file that
contains classes that represent entity types and the ObjectContext. For more information, see EDM Generator
(EdmGen.exe).

Visual Studio 2008 includes rich tool support for generating and maintaining an EDM in a Visual Studio application.
The Entity Data Model Designer supports creating advanced mapping scenarios, such as table-per-type and table-
per-hierarchy inheritance and split entities that map to multiple tables. For more information, see ADO.NET Entity
Data Model Designer Overview.

Learning More

The following topics enable you to learn more about the Entity Framework:
Getting Started (Entity Framework)

Provide information about how to get up and running quickly using the Quickstart (Entity Framework),
which shows how to create a simple Entity Framework application.

Quickstart (Entity Framework)

Shows you how to use the Entity Data Model tools with Visual Studio 2008 to quickly create your first
Entity Framework application.

Application Scenarios (Entity Framework)

Provides task-based links to topics that match specific application scenarios, such as writing queries, binding
objects to data controls, or implementing business logic.

Application Scenarios (Entity Framework)


The ADO.NET Entity Framework has been designed around a core set of scenarios where it is important to work
with data as entities defined by an Entity Data Model (EDM). This topic points you to the topics relevant to these
scenarios.

In This Section

Define an Entity Data Model (Application Scenarios)

Links to topics that describe how to create an EDM, generate an EDM by using tools, define an EDM for
custom objects, and find the sample models discussed in the documentation.

Query an Entity Data Model (Application Scenarios)

Links to topics that describe how to write object queries, LINQ queries, and Entity SQL.

Program Entity Data Model Classes (Application Scenarios)

Links to topics that describe working with objects, transactions, and stored procedure support.

Bind Entity Data to Controls (Application Scenarios)

Links to topics that describe data binding.

Use Stored Procedures (Application Scenarios)

Links to topics that describe how to use stored procedures with an EDM.

Implement Business Logic (Entity Framework Scenarios)

Links to topics that describe how to implement your own business logic into an Entity Framework
application.

Entity Data Model Inheritance (Application Scenarios)


Links to topics that describe how to define and work with inheritance in an EDM.

Entity Data Model Mapping Scenarios (Application Scenarios)

Describes the mapping scenarios that are supported by the Entity Framework.

Entity Data Model Mapping Scenarios (Application Scenarios)


The Entity Framework supports various ways to map conceptual models to relational data in an Entity Data Model
(EDM). For more information, see Data Modeling in the Entity Framework.

The following Entity Data Model (EDM) mapping scenarios are currently supported by the Entity Framework.

Mapping scenario Description

Simple mapping In this mapping scenario, each entity in the conceptual model is mapped to a
single table in the storage model. This is the default mapping generated by
Entity Data Model tools. For more information, see the Quickstart (Entity
Framework).

Entity splitting In this mapping scenario, properties from a single entity in the conceptual model
are mapped to columns in two or more underlying tables. In this scenario, the
tables must share a common primary key. For more information, see How to:
Define a Model with Single Entity Mapped to Two Tables.

Horizontal In this mapping scenario, a single entity type in the conceptual model is mapped
partitioning in the to two or more tables with the same schema. The entity is mapped into the
storage model tables based on a condition defined in the mapping specification. For more
information, see How to: Define a Model with Horizontal Partition in Storage
Model. For more information on condition mapping, see Condition Element
(MappingFragment).

Horizontal In this mapping scenario, multiple entity types in the conceptual model that
partitioning in the have the same properties are mapped to a single table. A condition clause is
conceptual model used to specify which data in the table belongs to which entity type. This
mapping is similar to table-per-hierarchy inheritance mapping. For more
information, see How to: Define a Model with Table-per-Hierarchy Inheritance.

Table-per-hierarchy In this mapping scenario, all types in an inheritance hierarchy are mapped to a
inheritance single table. A condition clause is used to define the entity types. For more
information, see How to: Define a Model with Table-per-Hierarchy Inheritance
(Entity Framework).

Table-per-type In this mapping scenario, all types are all mapped to individual tables.
inheritance Properties that belong solely to a base type or derived type are stored in a table
that maps to that type. For more information, see How to: Define a Model with
Table-per-Type Inheritance (Entity Framework).

Table-per-concrete- In this mapping scenario, non-abstract types are each mapped to an individual
type inheritance table. Each of these tables must have columns that map to all of the properties
of the derived type, including the properties inherited from the base type.

Multiple entity sets In this mapping scenario, a single entity type is expressed in two or more
per type separate entity sets in the conceptual model. Each entity set is mapped to a
separate table in the storage model. For more information, see How to: Define a
Model with Multiple Entity Sets per Type (Entity Framework).

Complex types A complex type is a non-scalar property of an entity type that does not have a
key property. A complex type can contain other nested complex types. Complex
types are mapped to tables in the storage model. For more information, see
How to: Define a Model with Complex Type (Entity Framework).

Function import In this scenario, a stored procedure in the storage model is mapped to a
mapping FunctionImport element in the conceptual model. This function is executed to
return entity data using the mapped stored procedure. For more information,
see How to: Define a Model with a Stored Procedure (Entity Framework).

Modification In this scenario, stored procedures are defined in the storage model that insert,
function mapping update, and delete data. These functions are defined for an entity type to
provide the update functionality for a specific entity type. For more information,
see Stored Procedure Support (Entity Framework).

Defining query In this scenario, a query is defined in the storage model that represents a table
mapping in the data source. The query is expressed in the native query language of the
data source, such as Transact-SQL, when mapping to a SQL Server database.
This DefiningQuery element is mapped to an entity type in the conceptual
model. The query is defined in the store-specific query language. For more
information, see DefiningQuery Element (EntityContainer SSDL). When you use
a defining query, updates cannot be persisted to the data source using the
standard update process. Updates can be made by defining modification function
mappings. For more information, see Stored Procedure Support (Entity
Framework).

Query view mapping In this scenario, a read-only mapping is defined between entity types in the
conceptual model and relational tables in the storage model. This mapping is
defined based on an Entity SQL query against the storage model that returns
entities in the conceptual model. For more information, see QueryView Element
(EntitySetMapping). When you use a query view, updates cannot be persisted to
the data source using the standard update process. Updates can be made by
defining modification function mappings. For more information, see Stored
Procedure Support (Entity Framework).

AssociationSet Associations define relationships between entities. In a simple mapping with a


mapping one-to-one or one-to-many association, associations that define relationships in
the conceptual model are mapped to associations in the storage model. The
following more advanced association set mappings are also supported:
 Many-to-many associations. Both ends of the association are mapped to a
link table in the storage model.
 Self association. This supports an association between two entities of the
same type, such as an Employee with an association to another Employee.
 Associations between derived types. This mapping supports an association
between a derived type in one hierarchy and a derived type in a second
hierarchy.
For more information, see Association (EDM).

For information about the mapping scenarios that are supported by Entity Data Model tools, see ADO.NET Entity
Data Model Designer Overview.

Web Services and the Entity Data Model (Application Scenarios)

Describes considerations for creating Web services or Windows Communication Foundation (WCF) services that use
the Entity Framework.

Web Services and the Entity Data Model (Application Scenarios)


The Entity Data Model (EDM) provides a conceptual layer of abstraction over the data source. Object Services
materializes entities defined in the conceptual layer into CLR objects that can be consumed in client applications,
such as Windows Forms applications and ASP.NET applications. The Entity Framework supports serialization of
entity objects into formats that enable remote and message-based exchange of entity objects, such as Web
services and the Windows Communication Foundation (WCF). Objects can be serialized using binary serialization,
XML serialization, and WCF contract serialization for transport using binary stream or message-based protocols. For
more information, see Serializing Objects (Entity Framework). Objects can also be received from a message or
stream, deserialized, and attached to an object context. For more information, see Attaching Objects (Entity
Framework).

ADO.NET Data Services also enables you to provide dynamic access to EDM data in an XML format that can be
used by applications. This entity data is accessed by using standard Representational State Transfer (REST) HTTP
actions, such as GET, PUT, and POST. For more information, see ADO.NET Data Services Framework.

The following should be considered when creating Web services or WCF services that use the Entity Framework:

 Full-graph serialization is supported for binary serialization and DataContract serialization. XML

serialization does not serialize related objects.

 Objects are always deserialized in the Detached state. You may need to attach or add the object to an

ObjectContext, or you may just want to apply property changes to the original object. For more
information, see Attaching Objects (Entity Framework).

 Stateless services are recommended. Services should be designed such that an object context is only

maintained for the duration of a request or a response. The message exchange pattern should include
enough information so that changes can be applied without having to persist objects or re-query the data
source to retrieve the original object. For example, a service that allows a client to update objects should
require that the updated object be returned along with the original object. This enables changes to be
applied to the original object by the Web service using the ApplyPropertyChanges method without having
to retrieve the original object from the database or persist it in memory. For more information, see How
to: Apply Changes Made to a Detached Object (Entity Framework).

Entity Framework Features

Provides more detailed information on the features that compose the Entity Framework and links to topics
that discuss those features.

Entity Framework Terminology

Defines many of the terms that are introduced by the EDM and the Entity Framework and that are used in
Entity Framework documentation.

Entity Framework Resources

Provides links to conceptual topics and links to external topics and resources for building Entity Framework
applications.

Getting Started (Entity Framework)


The ADO.NET Entity Framework is designed to support data-centric applications and services, and provides a
platform for programming against data that raises the level of abstraction from the logical relational level to the
conceptual level. By enabling developers to work with data at a greater level of abstraction, the Entity Framework
supports code that is independent of any particular data storage engine or relational schema. For more
information, see Introducing the Entity Framework.

The Entity Framework supports an Entity Data Model (EDM) for defining data at both the storage and conceptual
level and a mapping between the two. It also enables developers to program directly against the data types
defined at the conceptual level as common language runtime (CLR) objects. The Entity Framework provides tools
to generate an EDM and the related CLR objects based on an existing database. This reduces much of the data
access code that used to be required to create object-based data application and services, and makes it faster to
create object-oriented data applications and services from an existing database.

The topics in this section are designed to help you understand using the Entity Framework quickly by explaining the
underlying technologies in the context of the Quickstart (Entity Framework) tutorial.

In This Section

Generating an Entity Data Model

Describes how to derive an EDM from an existing database.

Generating an Entity Data Model


Entity Framework applications and services are based on the Entity Data Model (EDM). This model represents
application data as a set of entities and relationships that are mapped to a defined data source. An EDM consists of
a conceptual model expressed as entities and relationships, a storage model that represents the schema of the
logical model, and a mapping between the two. The development phase of a project usually starts with the
conceptual model, and then the logical model is derived from the conceptual model.

To meet the needs of developers who want to derive an Entity Data Model from an existing database, the Entity
Framework provides a set of tools that generate an EDM, validate an EDM, and create programmable classes based
on the conceptual model. The EDM Generator (EdmGen.exe) command prompt utility enables you to generate a
simple model with a one-to-one mapping between entities and tables in a data source. You can also use
EdmGen.exe to generate the data classes that are based on entity types and to validate an EDM. EdmGen.exe is
part of the Entity Framework runtime components in the .NET Framework 3.5 Service Pack 1(SP1).

Visual Studio 2008 SP1 includes an integrated set of Entity Data Model tools that generates a model that includes
selected objects from a data source. You can then modify the conceptual model and mapping using the Entity Data
Model Designer to achieve the conceptual model that is required by the application. For more information, see
Entity Data Model Tools.

Considerations

The following considerations apply when you generate an EDM:

 All entities are required to have keys. If the database has a table without a primary key, the EDM tools try

to infer a key for the corresponding entity. In addition, the EDM tools generate a DefiningQuery element in
the store schema that makes the data for this entity read-only. To make the entity data updatable, you
must verify that the generated key is a valid key and then remove the DefiningQuery element.

 A table that represents a many-to-many relationship between two tables in the database may not have an

equivalent entity in the conceptual schema. When the EDM tools encounter such a table with no columns
other than the two that are foreign keys, the mapping table is represented in the conceptual schema as a
many-to-many association instead of an entity. The CourseInstructor association in the School model is
an example of this behavior. For more information, see Generating the School Entity Data Model (Entity
Framework Quickstart).

 In this release, the Entity Data Model tools only support automatic generation of an EDM based on an

existing data source. You cannot automatically generate a data source, such as a relational database,
based on the conceptual model.

The School Model

The topics in this Getting Started section refer to a sample database named School. See Creating the School
Sample Database (Entity Framework Quickstart) for the script that generates the School database on SQL Server.

School contains the following tables:

 Course

 CourseGrade

 CourseInstructor

 Department

 OfficeAssignment
 OnlineCourse

 OnsiteCourse

 Person

This example database highlights many of the complex modeling scenarios supported by the Entity Data Model
tools. The script creates the relationships between the tables and inserts sample data so that you can run the
samples to see how they work. The following shows the School EDM displayed in the ADO.NET Entity Data Model
Designer:

Mapping a Conceptual Model to a Storage Schema

Describes how the conceptual model is mapped to the storage model.

Mapping a Conceptual Model to a Storage Schema


The Entity Framework provides an object-centric view of tabular data, expressed as entity types. An application
developer only has to think about programming against the object model that is generated from the conceptual
model, rather than having to also think about the database schema and how to access database objects and
transform them into programming objects. The Entity Framework uses model schemas and mappings to transform
create, read, update, and delete operations against entities into equivalent operations in the data source.

Note

All the mapping file fragments that are shown in this section are generated by the EDM Generator
(EdmGen.exe) tool.

The Conceptual Model

The conceptual model is an Entity Data Model (EDM) schema that defines the entities and associations in the EDM.
The XML syntax that defines this model is called the conceptual schema definition language (CSDL). Entity types
defined in CSDL each have a name, a key for uniquely identifying instances, and a set of properties. The data types
assigned to properties are specified as either simple types, which are scalar properties, or as complex types, which
are types that consist of one or more scalar or complex properties. Additional properties may also specify nullability
or assign a default value. Associations define the relationships between entities. Entity Framework language
elements and terminology are explained in more detail in Entity Framework Terminology.

The following XML fragment represents part of the conceptual model for the School EDM that defines the Course
and Department entity types that are related by the FK_Course_Department association, with other entities
and associations removed.

Copy Code

<?xml version="1.0" encoding="utf-8"?>


<Schema Namespace="SchoolModel" Alias="Self"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
<EntityContainer Name="SchoolEntities">
<EntitySet Name="Course" EntityType="SchoolModel.Course" />
<EntitySet Name="Department" EntityType="SchoolModel.Department" />
...
<AssociationSet Name="FK_Course_Department"
Association="SchoolModel.FK_Course_Department">
<End Role="Department" EntitySet="Department" />
<End Role="Course" EntitySet="Course" />
</AssociationSet>
...
</EntityContainer>
<EntityType Name="Course">
<Key>
<PropertyRef Name="CourseID" />
</Key>
<Property Name="CourseID" Type="Int32" Nullable="false" />
<Property Name="Title" Type="String" Nullable="false"
MaxLength="100" Unicode="true" FixedLength="false" />
...
<NavigationProperty Name="Department"
Relationship="SchoolModel.FK_Course_Department"
FromRole="Course" ToRole="Department" />
...
</EntityType>
<EntityType Name="Department">
<Key>
<PropertyRef Name="DepartmentID" />
</Key>
<Property Name="DepartmentID" Type="Int32" Nullable="false" />
<Property Name="Name" Type="String" Nullable="false" MaxLength="50"
Unicode="true" FixedLength="false" />
...
<NavigationProperty Name="Course" Relationship="SchoolModel.FK_Course_Department"
FromRole="Department" ToRole="Course" />
</EntityType>
...
<Association Name="FK_Course_Department">
<End Role="Department" Type="SchoolModel.Department" Multiplicity="1" />
<End Role="Course" Type="SchoolModel.Course" Multiplicity="*" />
</Association>
...
</Schema>

The Storage Model

A separate data model uses store schema definition language (SSDL) to describe the logical model for persistent
data, usually stored in a relational database. The data types of properties declared in SSDL files are those of the
storage model. This storage model fragment shows an example of storage metadata for the Course and
Department tables in the School database that are related by the FK_Course_Department foreign key, with
other entities removed.

Copy Code

<?xml version="1.0" encoding="utf-8"?>


<Schema Namespace="SchoolModel.Store" Alias="Self"
Provider="System.Data.SqlClient" ProviderManifestToken="2005"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
<EntityContainer Name="dbo">
<EntitySet Name="Course" EntityType="SchoolModel.Store.Course"
store:Type="Tables" />
<EntitySet Name="Department"
EntityType="SchoolModel.Store.Department" store:Type="Tables" />
...
<AssociationSet Name="FK_Course_Department"
Association="SchoolModel.Store.FK_Course_Department">
<End Role="Department" EntitySet="Department" />
<End Role="Course" EntitySet="Course" />
</AssociationSet>
...
</EntityContainer>
<EntityType Name="Course">
<Key>
<PropertyRef Name="CourseID" />
</Key>
<Property Name="CourseID" Type="int" Nullable="false" />
<Property Name="Title" Type="nvarchar" Nullable="false"
MaxLength="100" />
...
</EntityType>
<EntityType Name="Department">
<Key>
<PropertyRef Name="DepartmentID" />
</Key>
<Property Name="DepartmentID" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar" Nullable="false"
MaxLength="50" />
...
</EntityType>
...
<Association Name="FK_Course_Department">
<End Role="Department" Type="SchoolModel.Store.Department"
Multiplicity="1" />
<End Role="Course" Type="SchoolModel.Store.Course" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Department">
<PropertyRef Name="DepartmentID" />
</Principal>
<Dependent Role="Course">
<PropertyRef Name="CourseID" />
</Dependent>
</ReferentialConstraint>
</Association>
...
</Schema>

The Mapping Specification

A mapping specification uses mapping specification language (MSL) to connect the types declared in the conceptual
model to the database metadata declared in the storage model. This MSL fragment demonstrates a one-to-one
mapping between the conceptual and storage models for the Course and Department entities in the School
model.

Copy Code

<?xml version="1.0" encoding="utf-8"?>


<Mapping Space="C-S"
xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
<EntityContainerMapping StorageEntityContainer="dbo"
CdmEntityContainer="SchoolEntities">
<EntitySetMapping Name="Course">
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel.Course)">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Title" ColumnName="Title" />
...
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="Department">
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel.Department)">
<MappingFragment StoreEntitySet="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
<ScalarProperty Name="Name" ColumnName="Name" />
...
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
...
<AssociationSetMapping Name="FK_Course_Department"
TypeName="SchoolModel.FK_Course_Department"
StoreEntitySet="Course">
<EndProperty Name="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<Condition ColumnName="DepartmentID" IsNull="false" />
</AssociationSetMapping>
...
</cs:EntityContainerMapping>
</Mapping>
Discussion

The School model that is discussed here uses a simple one-to-one mapping between a conceptual entity and a
database table but the Entity Framework supports more complex mappings. For example, the relational database
might use more than one table to store data related to each employee. One table could contain contact information
for all people and a separate related table could contain information that pertains only to employees. The Entity
Framework enables developers to define entities using inheritance hierarchies and to map one entity to data from
more than one table in a database, with full support for data updates, inserts, and deletes. For more information,
see Data Modeling in the Entity Framework.

Working with Entity Data

Demonstrates how to query an EDM and work with object data.

Working with Entity Data


The Entity Framework compiles a set of conceptual and storage schemas, together with the mappings between
them, into bidirectional pairs of Entity SQL statements called client views. These views drive query and update
processing in the runtime engine. The mapping compiler that generates the views can be invoked either at design
time or at runtime when the first query is executed against an Entity Data Model (EDM) schema.

The Entity Framework builds on top of storage-specific ADO.NET data providers by providing an EntityConnection to
an underlying data provider and relational database.

When a query is executed, it is parsed and converted into a canonical command tree, which is an object model
representation of the query. Canonical command trees represent select, update, insert, and delete commands. All
subsequent processing is performed on the command tree, which is the means of communication between the
System.Data.EntityClient provider and the underlying .NET Framework data provider, such as
System.Data.SqlClient.

The following diagram illustrates the Entity Framework architecture for accessing data:
Querying Objects

The Entity Framework tools generate a class derived from ObjectContext that represents the entity container
defined in the conceptual model. The ObjectContext class supports queries against an EDM that return entities as
objects, as well as creating, updating, and deleting entity objects. The Entity Framework supports object queries
against an EDM. Queries can be composed using Entity SQL, Language-Integrated Query (LINQ), and object query
builder methods.

In a conceptual model, entities are related to each other by associations. In the object layer, these associations are
represented by properties that expose collections of related objects based on an entity reference. For example, in
the School model, Department.Course gets an entity collection of Course objects based on the association
between Course and Department. Because referenced objects are not automatically loaded, you must call the
Load method on the entity reference to load the related object data into the object context. You can also specify a
query path that defines which related objects to load with returned objects. For more information, see Querying
Data as Objects (Entity Framework).

The following example from the quickstart shows a query that, when executed, retrieves all Department objects.
A query path definition ensures that the Course objects related to Department objects are also returned. An
Entity SQL WHERE clause orders the returned objects by Name.

Visual Basic
Copy Code

' Define a query that returns all Department objects and related
' Course objects, ordered by name.
Dim departmentQuery As ObjectQuery(Of Department) = _
schoolContext.Department.Include("Course").OrderBy("it.Name")
C#
Copy Code

// Define a query that returns all Department objects and related


// Course objects, ordered by name.
ObjectQuery<Department> departmentQuery =
schoolContext.Department.Include("Course").OrderBy("it.Name");
For more information, see Querying Data as Objects (Entity Framework).

You can define an EDM that uses stored procedures to execute queries at the data source. The result sets from
these stored procedures are mapped to entities in the conceptual model. For more information, see Stored
Procedure Support (Entity Framework).

Working with Objects

An object in an object context is an entity type representation of data in the data source. You can modify, create,
and delete objects in an object context. The object context manages identities and relationships between objects.
You can also serialize objects and bind objects to controls. For more information, see Working with Objects (Entity
Framework).

The following example from the quickstart gets a collection of Course objects related to a Department object and
binds the collection to a DataGridView control.

Visual Basic
Copy Code

' Get the object for the selected department.


Dim department As Department = _
CType(Me.departmentList.SelectedItem, Department)

' Bind the grid view to the collection of Course objects


' that are related to the selected Department object.
courseGridView.DataSource = department.Course
C#
Copy Code

// Get the object for the selected department.


Department department =
(Department)this.departmentList.SelectedItem;

// Bind the grid view to the collection of Course objects


// that are related to the selected Department object.
courseGridView.DataSource = department.Course;
The Entity Framework tracks changes to entity data and enables you to persist changes back to the data source. In
the following example from the quickstart, changes in the object context are written to the database.

Visual Basic
Copy Code

' Save object changes to the database, display a message,


' and refresh the form.
numChanges = schoolContext.SaveChanges()
C#
Copy Code

// Save object changes to the database, display a message,


// and refresh the form.
numChanges = schoolContext.SaveChanges();
For more information, see Adding, Modifying, and Deleting Objects (Entity Framework).

You can define an EDM that uses stored procedures to insert, update, and delete data at the data source. These
stored procedures are mapped to entities in the conceptual model. For more information, see Stored Procedure
Support (Entity Framework).

Quickstart (Entity Framework)

A tutorial that demonstrates how to create an Entity Framework application.

Entity Framework Resources

A guide to finding information and locating Entity Framework resources.

Programming Guide (Entity Framework)


This documentation includes task-based topics to help you learn to program with the Entity Framework.

In This Section

Application Scenarios

 Define an Entity Data Model (Application Scenarios)

 Query an Entity Data Model (Application Scenarios)

 Program Entity Data Model Classes (Application Scenarios)

 Bind Entity Data to Controls (Application Scenarios)

 Use Stored Procedures (Application Scenarios)

 More…

Entity Framework Tasks

Describes how to perform common Entity Framework tasks, including the following. For a complete list of
categories and tasks, click the "More..." link.
How to: Add, Modify, and Delete Objects (Entity Framework)

How to: Add, Modify, and Delete Objects (Entity Framework)


This topic provides an example of how to use Object Services to modify objects in an object context and save the
data to the database. The example in this topic is based on the Adventure Works Sales Model. To run the code in
this example, you must have already added the AdventureWorks Sales Model to your project and configured your
project to use the Entity Framework. To do this, complete the procedures in How to: Manually Configure an Entity
Framework Project and How to: Manually Define an Entity Data Model (Entity Framework).

Example

In this example, an object query returns a single SalesOrderHeader object based on a specified SalesOrderID.
The status for this order is changed from 5 (shipped) to 1 (in process), a new item is added to the order, and the
first existing item is deleted. The SaveChanges method is called to write changes to the database. The resulting
state of the order is then written to the console.

Visual Basic
Copy Code

' Specify the order to update.


Dim orderId As Integer = 43680

Using context As New AdventureWorksEntities()


Try
Dim order As SalesOrderHeader = _
context.SalesOrderHeader.Where( _
"it.SalesOrderID = @id", New ObjectParameter( _
"id", orderId)).First()

' Change the status and ship date of an existing order.


order.Status = 1
order.ShipDate = DateAndTime.Today

' Load items for the order, if not already loaded.


If Not order.SalesOrderDetail.IsLoaded Then
order.SalesOrderDetail.Load()
End If

' Delete the first item in the order.


context.DeleteObject(order.SalesOrderDetail.First())

' Create a new item using the static Create method


' and add it to the order.
order.SalesOrderDetail.Add( _
SalesOrderDetail.CreateSalesOrderDetail( _
1, 0, 2, 750, 1, CDec(2171.2942), 0, 0, Guid.NewGuid(), _
DateAndTime.Today))

' Save changes in the object context to the database.


Dim changes As Integer = context.SaveChanges()

Console.WriteLine(changes.ToString() + " changes saved!")


Console.WriteLine("Updated item for order: " _
+ order.SalesOrderID.ToString())

Dim item As SalesOrderDetail


For Each item In order.SalesOrderDetail
Console.WriteLine("Item ID: " _
+ item.SalesOrderDetailID.ToString() + " Product: " _
+ item.ProductID.ToString() + " Quantity: " + item.OrderQty.ToString())
Next
Catch ex As UpdateException
Console.WriteLine(ex.ToString())
End Try
End Using
C#
Copy Code

// Specify the order to update.


int orderId = 43680;

using (AdventureWorksEntities context =


new AdventureWorksEntities())
{
try
{
SalesOrderHeader order =
context.SalesOrderHeader.Where
("it.SalesOrderID = @id", new ObjectParameter(
"id", orderId)).First();

// Change the status and ship date of an existing order.


order.Status = 1;
order.ShipDate = DateTime.Today;

// Load items for the order, if not already loaded.


if (!order.SalesOrderDetail.IsLoaded)
{
order.SalesOrderDetail.Load();
}

// Delete the first item in the order.


context.DeleteObject(order.SalesOrderDetail.First());

// Create a new item using the static Create method


// and add it to the order.
order.SalesOrderDetail.Add(
SalesOrderDetail.CreateSalesOrderDetail(0,
0, 2, 750, 1, (decimal)2171.2942, 0, 0,
Guid.NewGuid(), DateTime.Today));

// Save changes in the object context to the database.


int changes = context.SaveChanges();

Console.WriteLine(changes.ToString() + " changes saved!");


Console.WriteLine("Updated item for order: "
+ order.SalesOrderID.ToString());

foreach (SalesOrderDetail item in order.SalesOrderDetail)


{
Console.WriteLine("Item ID: "
+ item.SalesOrderDetailID.ToString() + " Product: "
+ item.ProductID.ToString() + " Quantity: "
+ item.OrderQty.ToString());
}
}
catch (UpdateException ex)
{
Console.WriteLine(ex.ToString());
}
}

How to: Bind Objects to Windows Form Controls (Entity Framework)

How to: Bind Objects to Windows Form Controls (Entity


Framework)
Object Services enables you to bind Windows Form controls such as a ComboBox or DataGridView to an
EntityCollection or to an ObjectQuery result. We recommend that you not bind controls directly to an
ObjectQuery. Instead, bind controls to the result of the Execute method. For more information, see Binding
Objects to Controls (Entity Framework).

The example in this topic is based on the Adventure Works Sales Model. To run the code in this example, you must
have already added the AdventureWorks Sales Model to your project and configured your project to use the Entity
Framework. To do this, complete the procedures in How to: Manually Configure an Entity Framework Project and
How to: Manually Define an Entity Data Model (Entity Framework).

Example

The following example is from a Windows Form. When the form is loaded, an ObjectResult of SalesOrderHeader
objects is returned by calling the Execute method of the ObjectQuery. This result is bound to a combo box. When
an order is selected, the related EntityCollection of SalesOrderDetail objects is bound to a DataGridView
control.

Visual Basic
Copy Code

Imports System
Imports System.Collections.Generic
Imports System.Collections
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Imports AdventureWorksModel

Public Class Main


Public Sub New()
' Initializes the designer-generated controls.
InitializeComponent()
End Sub
Private context As AdventureWorksEntities
Private customerId As Integer = 277
Private Sub Main_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
' Initialize the object context.
context = New AdventureWorksEntities()
Try
' Create a query for orders that includes line items.
Dim orderQuery As ObjectQuery(Of SalesOrderHeader) = _
context.SalesOrderHeader _
.Where("it.CustomerID = @customerId", _
New ObjectParameter("customerId", customerId)) _
.Include("SalesOrderDetail")

' Bind the combo box to the ObjectResult of SalesOrderHeader


' that is returned when the query is executed.
Me.ordersListBox.DataSource = orderQuery.Execute(MergeOption.AppendOnly)

' Display the PO number in the combo box.


Me.ordersListBox.DisplayMember = "PurchaseOrderNumber"

Catch ex As EntitySqlException
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub ordersListBox_SelectedIndexChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles ordersListBox.SelectedIndexChanged
' Get the currently selected SalesOrderHeader object.
Dim order As SalesOrderHeader = CType(Me.ordersListBox.SelectedItem, _
SalesOrderHeader)

' Bind the items for this order to the DataGridView.


lineItemsDataGrid.DataSource = order.SalesOrderDetail
End Sub

Private Sub saveButton_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles saveButton.Click
' Get the current order.
Dim order As SalesOrderHeader = CType(ordersListBox.SelectedItem, _
SalesOrderHeader)

Try
' Save changes in the object context.
context.SaveChanges(True)

Catch ex As OptimisticConcurrencyException
' Resolve the concurrently conflict by refreshing the
' object context before saving changes.
context.Refresh(RefreshMode.ClientWins, order.SalesOrderDetail)

' Resave changes in the object context.


context.SaveChanges(True)
Catch ex As Exception
MessageBox.Show(ex.InnerException.Message, "An error has occured")
Finally
' Refresh the latest data from the database.
context.Refresh(RefreshMode.StoreWins, order)
Me.Refresh()
End Try
End Sub
End Class
C#
Copy Code

using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using AdventureWorksModel;

namespace Microsoft.Samples.Edm
{
public partial class Main : Form
{
private AdventureWorksEntities context;
private int customerId = 277;

public Main()
{
// Initializes the designer-generated controls.
InitializeComponent();
}

private void Main_Load(object sender, EventArgs e)


{
// Initialize the object context.
context = new AdventureWorksEntities();
try
{
// Create a query for orders that includes line items.
ObjectQuery<SalesOrderHeader> orderQuery = context.SalesOrderHeader
.Where("it.CustomerID = @customerId",
new ObjectParameter("customerId", customerId))
.Include("SalesOrderDetail");

// Bind the combo box to the ObjectResult of SalesOrderHeader


// that is returned when the query is executed.
this.ordersListBox.DataSource = orderQuery.Execute(MergeOption.AppendOnly);

// Display the PO number in the combo box.


this.ordersListBox.DisplayMember = "PurchaseOrderNumber";
}
catch (EntitySqlException ex)
{
MessageBox.Show(ex.Message);
}
}

private void ordersListBox_SelectedIndexChanged(object sender, EventArgs e)


{
// Get the currently selected SalesOrderHeader object.
SalesOrderHeader order = (SalesOrderHeader)this.ordersListBox.SelectedItem;

// Bind the items for this order to the DataGridView.


lineItemsDataGrid.DataSource = order.SalesOrderDetail;
}

private void saveButton_Click(object sender, EventArgs e)


{
// Get the current order.
SalesOrderHeader order = (SalesOrderHeader)ordersListBox.SelectedItem;
try
{
// Save changes in the object context.
context.SaveChanges(true);
}
catch (OptimisticConcurrencyException)
{
// Resolve the concurrently conflict by refreshing the
// object context before saving changes.
context.Refresh(RefreshMode.ClientWins, order.SalesOrderDetail);

// Resave changes in the object context.


context.SaveChanges(true);
}
catch (Exception ex)
{
MessageBox.Show(ex.InnerException.Message, "An error has occured");
}
finally
{
// Refresh the latest data from the database.
context.Refresh(RefreshMode.StoreWins, order);
this.Refresh();
}
}
}
}

How to: Execute a Query that Returns an Entity Type (Entity Framework)

How to: Execute a Query that Returns an Entity Type (Entity


Framework)
This topic provides examples of how to execute an Entity SQL query that returns a collection of instances of an
entity type. It then iterates through the collection of Products and displays the name and the ID for each Product.
The same example is shown using each of the following Entity Framework query technologies:

 LINQ to Entities

 Entity SQL with ObjectQuery<T>

 Query builder methods of ObjectQuery<T>

The examples in this topic are based on the Adventure Works Sales Model. To run the code in this example, you
must have already added the AdventureWorks Sales Model to your project and configured your project to use the
Entity Framework. To do this, complete the procedures in How to: Manually Configure an Entity Framework Project
and How to: Manually Define an Entity Data Model (Entity Framework). You can also use the Entity Data Model
Wizard to define the AdventureWorks Sales Model. For more information, see How to: Use the Entity Data Model
Wizard (Entity Framework).

Example

The following is the LINQ to Entities example.


Visual Basic
Copy Code

Using AWEntities As New AdventureWorksEntities


Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim productsQuery = _
From product In products _
Select product

Console.WriteLine("Product Names:")
For Each product In productsQuery
Console.WriteLine(product.Name)
Next
End Using
C#
Copy Code

using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())


{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productsQuery = from product in products
select product;

Console.WriteLine("Product Names:");
foreach (var prod in productsQuery)
{
Console.WriteLine(prod.Name);
}
}
The following is the Entity SQL example.

Visual Basic
Copy Code

Using advWorksContext As AdventureWorksEntities = New AdventureWorksEntities


Try
Dim queryString As String = "SELECT VALUE Product FROM AdventureWorksEntities.Product AS Product"

Dim query As New ObjectQuery(Of Product)(queryString, advWorksContext, MergeOption.NoTracking)

' Iterate through the collection of Product items.


For Each result As Product In query
Console.WriteLine("Product Name: {0} Product ID: {1}", _
result.Name, result.ProductID)
Next

Catch exception As EntityException


Console.WriteLine(exception.ToString)
Catch ex As InvalidOperationException
Console.WriteLine(ex.ToString())
End Try
End Using
C#
Copy Code

using (AdventureWorksEntities advWorksContext =


new AdventureWorksEntities())
{
try
{
string queryString =
@"SELECT VALUE Product FROM AdventureWorksEntities.Product AS Product";

ObjectQuery<Product> productQuery =
new ObjectQuery<Product>(queryString, advWorksContext, MergeOption.NoTracking);

// Iterate through the collection of Product items.


foreach (Product result in productQuery)
Console.WriteLine("Product Name: {0}; Product ID: {1}",
result.Name, result.ProductID);
}
catch (EntityException ex)
{
Console.WriteLine(ex.ToString());
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.ToString());
}
}
The following is the query builder method example.

Visual Basic
Copy Code

Using advWorksContext As AdventureWorksEntities = _


New AdventureWorksEntities
Try
Dim query As ObjectQuery(Of Product) = _
advWorksContext.Product

' Iterate through the collection of Product items.


For Each result As Product In query
Console.WriteLine("Product Name:{0}Product ID: {1}", _
result.Name, result.ProductID)
Next

Catch exception As EntitySqlException


Console.WriteLine(exception.ToString)
End Try
End Using
C#
Copy Code

using (AdventureWorksEntities advWorksContext =


new AdventureWorksEntities())
{
try
{
ObjectQuery<Product> productQuery = advWorksContext.Product;

// Iterate through the collection of Product items.


foreach (Product result in productQuery)
Console.WriteLine("Product Name: {0}; Product ID: {1}",
result.Name, result.ProductID);
}
catch (EntitySqlException ex)
{
Console.WriteLine(ex.ToString());
}
}

How to: Use Query Paths to Shape Results (Entity Framework)

How to: Use Query Paths to Shape Results (Entity Framework)


This topic provides an example of how to specify a query path that defines which related objects are returned when
a specific object is queried in an Entity Data Model (EDM). The query in this example returns a single Contact
object and all of the related SalesOrderHeader and SalesOrderDetail objects. The same example is shown using
each of the following Entity Framework query technologies:

 LINQ to Entities

 Entity SQL with ObjectQuery<T>

 Query builder methods of ObjectQuery<T>

The example in this topic is based on the Adventure Works Sales Model. To run the code in this example, you must
have already added the AdventureWorks Sales Model to your project and configured your project to use the Entity
Framework. To do this, complete the procedures in How to: Manually Configure an Entity Framework Project and
How to: Manually Define an Entity Data Model (Entity Framework). You can also use the Entity Data Model Wizard
to define the AdventureWorks Sales Model. For more information, see How to: Use the Entity Data Model Wizard
(Entity Framework).

Example

This is the LINQ to Entities example.

Visual Basic
Copy Code

Using context As New AdventureWorksEntities


' Define a LINQ query with a path that returns
' orders and items for a contact.
Dim contacts = (From contact In context.Contact _
.Include("SalesOrderHeader.SalesOrderDetail") _
Select contact).FirstOrDefault()

Try
' Execute the query and display information for each item
' in the orders that belong to the contact.
For Each order As SalesOrderHeader In contacts.SalesOrderHeader
Console.WriteLine(String.Format("PO Number: {0}", _
order.PurchaseOrderNumber))
Console.WriteLine(String.Format("Order Date: {0}", _
order.OrderDate.ToString()))
Console.WriteLine("Order items:")
For Each item As SalesOrderDetail In order.SalesOrderDetail
Console.WriteLine(String.Format("Product:{0}" _
+ "Quantity: {1}", item.ProductID.ToString(), _
item.OrderQty.ToString()))
Next
Next
Catch ex As EntitySqlException
Console.WriteLine(ex.ToString())
Catch ex As EntityCommandExecutionException
Console.WriteLine(ex.ToString())
End Try
End Using
C#
Copy Code

using (AdventureWorksEntities context =


new AdventureWorksEntities())
{
// Define a LINQ query with a path that returns
// orders and items for a contact.
var contacts = (from contact in context.Contact
.Include("SalesOrderHeader.SalesOrderDetail")
select contact).FirstOrDefault();

try
{
// Execute the query and display information for each item
// in the orders that belong to the contact.
foreach (SalesOrderHeader order in contacts
.SalesOrderHeader)
{
Console.WriteLine(String.Format("PO Number: {0}",
order.PurchaseOrderNumber));
Console.WriteLine(String.Format("Order Date: {0}",
order.OrderDate.ToString()));
Console.WriteLine("Order items:");
foreach (SalesOrderDetail item in order.SalesOrderDetail)
{
Console.WriteLine(String.Format("Product: {0} "
+ "Quantity: {1}", item.ProductID.ToString(),
item.OrderQty.ToString()));
}
}
}
catch (EntitySqlException ex)
{
Console.WriteLine(ex.ToString());
}
catch (EntityCommandExecutionException ex)
{
Console.WriteLine(ex.ToString());
}
}
This is the Entity SQL example.

Visual Basic
Copy Code

Using context As New AdventureWorksEntities


' Define an object query with a path that returns
' orders and items for a specific contact.
Dim queryString As String = _
"SELECT VALUE TOP(1) Contact FROM " + _
"AdventureWorksEntities.Contact AS Contact"

' Define the object query with the query string.


Dim contactQuery As New ObjectQuery(Of Contact)(queryString, _
context, MergeOption.NoTracking)

Dim contact As Contact = _


contactQuery.Include("SalesOrderHeader.SalesOrderDetail") _
.FirstOrDefault()

Try
' Execute the query and display information for each item
' in the orders that belong to the first contact.
For Each order As SalesOrderHeader In contact.SalesOrderHeader
Console.WriteLine(String.Format("PO Number: {0}", _
order.PurchaseOrderNumber))
Console.WriteLine(String.Format("Order Date: {0}", _
order.OrderDate.ToString()))
Console.WriteLine("Order items:")
For Each item As SalesOrderDetail In order.SalesOrderDetail
Console.WriteLine(String.Format("Product:{0}" _
+ "Quantity: {1}", item.ProductID.ToString(), _
item.OrderQty.ToString()))
Next
Next
Catch ex As EntitySqlException
Console.WriteLine(ex.ToString())
Catch ex As EntityCommandExecutionException
Console.WriteLine(ex.ToString())
End Try
End Using
C#
Copy Code

using (AdventureWorksEntities context =


new AdventureWorksEntities())
{
// Define an object query with a path that returns
// orders and items for a specific contact.
string queryString =
@"SELECT VALUE TOP(1) Contact FROM " +
"AdventureWorksEntities.Contact AS Contact";

// Define the object query with the query string.


ObjectQuery<Contact> contactQuery = new ObjectQuery<Contact>(queryString,
context, MergeOption.NoTracking);

Contact contact =
contactQuery.Include("SalesOrderHeader.SalesOrderDetail")
.FirstOrDefault();

try
{
// Execute the query and display information for each item
// in the orders that belong to the first contact.
foreach (SalesOrderHeader order in contact
.SalesOrderHeader)
{
Console.WriteLine(String.Format("PO Number: {0}",
order.PurchaseOrderNumber));
Console.WriteLine(String.Format("Order Date: {0}",
order.OrderDate.ToString()));
Console.WriteLine("Order items:");
foreach (SalesOrderDetail item in order.SalesOrderDetail)
{
Console.WriteLine(String.Format("Product: {0} "
+ "Quantity: {1}", item.ProductID.ToString(),
item.OrderQty.ToString()));
}
}
}
catch (EntitySqlException ex)
{
Console.WriteLine(ex.ToString());
}
catch (EntityCommandExecutionException ex)
{
Console.WriteLine(ex.ToString());
}
}
This is the query builder method example.

Visual Basic
Copy Code

Using context As New AdventureWorksEntities


' Create an object query with a path that returns orders and items for a contact.
Dim contact As Contact = _
context.Contact.Include("SalesOrderHeader.SalesOrderDetail") _
.FirstOrDefault()
Try
' Execute the query and display information for each item
' in the orders that belong to the returned contact.
Dim order As SalesOrderHeader
For Each order In contact.SalesOrderHeader
Console.WriteLine(String.Format("PO Number: {0}", _
order.PurchaseOrderNumber))
Console.WriteLine(String.Format("Order Date: {0}", _
order.OrderDate.ToString()))
Console.WriteLine("Order items:")
Dim item As SalesOrderDetail
For Each item In order.SalesOrderDetail
Console.WriteLine(String.Format("Product:{0}" _
+ "Quantity: {1}", item.ProductID.ToString(), _
item.OrderQty.ToString()))
Next
Next
Catch ex As EntitySqlException
Console.WriteLine(ex.ToString())
Catch ex As EntityCommandExecutionException
Console.WriteLine(ex.ToString())
End Try
End Using
C#
Copy Code

using (AdventureWorksEntities context =


new AdventureWorksEntities())
{
// Define an object query with a path that returns
// orders and items for a specific contact.
Contact contact =
context.Contact.Include("SalesOrderHeader.SalesOrderDetail")
.FirstOrDefault();
try
{
// Execute the query and display information for each item
// in the orders that belong to the first contact.
foreach (SalesOrderHeader order in contact
.SalesOrderHeader)
{
Console.WriteLine(String.Format("PO Number: {0}",
order.PurchaseOrderNumber));
Console.WriteLine(String.Format("Order Date: {0}",
order.OrderDate.ToString()));
Console.WriteLine("Order items:");
foreach (SalesOrderDetail item in order.SalesOrderDetail)
{
Console.WriteLine(String.Format("Product: {0} "
+ "Quantity: {1}", item.ProductID.ToString(),
item.OrderQty.ToString()));
}
}
}
catch (EntitySqlException ex)
{
Console.WriteLine(ex.ToString());
}
catch (EntityCommandExecutionException ex)
{
Console.WriteLine(ex.ToString());
}
}

Working with Custom Objects (Entity Framework Tasks)

Working with Custom Objects (Entity Framework Tasks)


The topics in this section show you how to map custom data classes to an Entity Data Model (EDM). For more
information, see Implementing Custom Data Class Interfaces (Entity Framework).

In This Section

How to: Customize an Entity Data Model to Work with Custom Objects (Entity Framework)

How to: Customize an Entity Data Model to Work with Custom


Objects (Entity Framework)
If you want to use custom data classes with an Entity Data Model (EDM), the entity types and properties defined in
the conceptual schema definition language (CSDL) file must match the custom data classes. The Entity Framework
tools generate a set of mapping files in which the entity types and entity sets in the CSDL file match the tables in
the database. The process to update these mapping files for custom data classes is as follows:

1. Update the CSDL file to match the custom data classes.

2. Update the mapping in the mapping specification language (MSL) file.

3. Update the store schema definition language (SSDL) file, if necessary.

4. Validate the updated mapping files.


5. Stop Visual Studio from generating the object layer.

To run the example in this topic, you must have already used the EDM Generator (EdmGen.exe) utility or the Entity
Data Model Wizard to generate the EDM mapping files. For more information, see How to: Use EdmGen.exe to
Generate an Entity Data Model (Entity Framework). If you are using the Entity Data Model Designer in Visual
Studio, you must also disable object layer generation based on the updated CSDL file. Otherwise, you will have
duplicate data classes in your project.

To update the CSDL file to reflect the custom data objects

1. Open the CSDL file in Visual Studio or in notepad.exe.

2. Rename the EntityType and EntitySet elements to reflect the names of the custom data classes.

3. Remove the EntityType and EntitySet elements for any entity that does not have a corresponding custom
data class.

4. Rename the Property elements of each type to match the name of the properties in the custom data class.

5. Remove the Property elements for any properties that do not exist in the custom data class.

6. Save the changes to the CSDL file.

To update the MSL file to map custom data objects to objects in the data source

1. Open the MSL file in Visual Studio or in notepad.exe.

2. Rename the EntitySetMapping element and the TypeName attribute to reflect the names of the custom
classes.

3. Remove the EntitySetMapping element for any entity that does not have a corresponding custom data class.

4. Rename the ScalarProperty elements of each type to match the names of the properties in the custom data
class.

5. Remove the ScalarProperty element for any properties that do not exist in the custom data class.

6. Rename the EndProperty element in the AssociationSetMapping to reflect the names of the custom
classes.

7. Rename the ScalarProperty elements in each AssociationSetMapping to match the names of the
properties in the custom data class.

Note

Do not change any of the ColumnName properties.

8. Remove the AssociationSetMapping element for associations between any entities that do not exist in the
custom data classes.

9. Save the changes to the MSL file.


To update the SSDL file to remove entities that do not exist in custom data classes

1. Open the SSDL file in Visual Studio or in notepad.exe.

2. Remove the EntityType elements for any entities that are not mapped to custom data classes.

3. Remove the ScalarProperty elements for any properties that are not mapped to properties of the custom
data classes.

4. Save the changes to the SSDL file.

To validate the updated mapping files

1. Run the EdmGen.exe utility in the directory that contains the mapping files. Use the following command:

Copy Code

%windir%\Microsoft.NET\Framework\v3.5\edmgen.exe /mode:ValidateArtifacts
/inssdl:.\YourModel.ssdl /inmsl:.\YourModel.msl /incsdl:.\YourModel.csdl

Note

Remove line breaks and replace YourModel with the name used for your mapping files.

2. Review the output and repair any validation errors.

To regenerate, save, and remove autogenerated object code

1. In the Solution Explorer in Visual Studio, right-click the CSDL file and select Run Custom Tool.

This regenerates the object layer based on the modified CSDL file.

2. Expand the CSDL file node, open the designer file, and save the file as a different file name.

This saves the autogenerated object layer file. Code from this file is used in the topic How to: Use Object
Services with Custom Objects (Entity Framework).

3. Exclude the designer file from the project.

4. Select the CSDL file and clear the Custom Tool value in the Properties window.

This will stop the object layer file from being regenerated. To generate this file in the future, type
EntityModelCodeGenerator for Custom Tool in the Properties window and repeat step 1.

Example

The following CSDL file has been customized to support the Orders and LineItem custom data classes.

Copy Code

<Schema Namespace="Microsoft.Samples.Edm" Alias="Self"


xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
<EntityContainer Name="SalesOrdersEntities">
<EntitySet Name="LineItem" EntityType="Microsoft.Samples.Edm.LineItem" />
<EntitySet Name="Order" EntityType="Microsoft.Samples.Edm.Order" />
<AssociationSet Name="FK_LineItem_Order_OrderId"
Association="Microsoft.Samples.Edm.FK_LineItem_Order_OrderId">
<End Role="Order" EntitySet="Order" />
<End Role="LineItem" EntitySet="LineItem" />
</AssociationSet>
</EntityContainer>
<EntityType Name="LineItem">
<Key>
<PropertyRef Name="LineItemId" />
</Key>
<Property Name="LineItemId" Type="Int32" Nullable="false" />
<Property Name="TrackingNumber" Type="String" MaxLength="25" />
<Property Name="Quantity" Type="Int16" Nullable="false" />
<Property Name="Product" Type="Int32" Nullable="false" />
<Property Name="Price" Type="Decimal" Nullable="false" Precision="19" Scale="4" />
<Property Name="Discount" Type="Decimal" Nullable="false" Precision="19" Scale="4" />
<NavigationProperty Name="Order" Relationship="Microsoft.Samples.Edm.FK_LineItem_Order_OrderId"
FromRole="LineItem" ToRole="Order" />
</EntityType>
<EntityType Name="Order">
<Key>
<PropertyRef Name="OrderId" />
</Key>
<Property Name="OrderId" Type="Int32" Nullable="false" />
<Property Name="OrderDate" Type="DateTime" Nullable="false" />
<Property Name="DueDate" Type="DateTime" Nullable="false" />
<Property Name="ShipDate" Type="DateTime" />
<Property Name="Status" Type="Byte" Nullable="false" />
<Property Name="Customer" Type="Int32" Nullable="false" />
<Property Name="SubTotal" Type="Decimal" Nullable="false" Precision="19" Scale="4" />
<Property Name="TaxAmt" Type="Decimal" Nullable="false" Precision="19" Scale="4" />
<Property Name="Freight" Type="Decimal" Nullable="false" Precision="19" Scale="4" />
<Property Name="ExtendedInfo" Type="Self.OrderInfo" Nullable="false" />
<NavigationProperty Name="LineItem"
Relationship="Microsoft.Samples.Edm.FK_LineItem_Order_OrderId"
FromRole="Order" ToRole="LineItem" />
</EntityType>
<ComplexType Name="OrderInfo">
<Property Name="OrderNumber" Type="String" Nullable="false" MaxLength="25" />
<Property Name="PurchaseOrder" Type="String" MaxLength="25" />
<Property Name="AccountNumber" Type="String" MaxLength="15" />
<Property Name="Comment" Type="String" MaxLength="128" />
</ComplexType>
<Association Name="FK_LineItem_Order_OrderId">
<End Role="Order" Type="Microsoft.Samples.Edm.Order" Multiplicity="1" />
<End Role="LineItem" Type="Microsoft.Samples.Edm.LineItem" Multiplicity="*" />
</Association>
</Schema>
The following MSL file has been customized to map the Orders and LineItem custom data classes to the
SalesOrderHeader and SalesOrderDetail tables in the AdventureWorks database.

Copy Code

<Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">


<EntityContainerMapping StorageEntityContainer="Sales" CdmEntityContainer="SalesOrdersEntities">
<EntitySetMapping Name="LineItem" StoreEntitySet="SalesOrderDetail"
TypeName="Microsoft.Samples.Edm.LineItem">
<ScalarProperty Name="LineItemId" ColumnName="SalesOrderDetailID" />
<ScalarProperty Name="TrackingNumber" ColumnName="CarrierTrackingNumber" />
<ScalarProperty Name="Quantity" ColumnName="OrderQty" />
<ScalarProperty Name="Product" ColumnName="ProductID" />
<ScalarProperty Name="Price" ColumnName="UnitPrice" />
<ScalarProperty Name="Discount" ColumnName="UnitPriceDiscount" />
</EntitySetMapping>
<EntitySetMapping Name="Order" StoreEntitySet="SalesOrderHeader"
TypeName="Microsoft.Samples.Edm.Order">
<ScalarProperty Name="OrderId" ColumnName="SalesOrderID" />
<ScalarProperty Name="OrderDate" ColumnName="OrderDate" />
<ScalarProperty Name="DueDate" ColumnName="DueDate" />
<ScalarProperty Name="ShipDate" ColumnName="ShipDate" />
<ScalarProperty Name="Status" ColumnName="Status" />
<ScalarProperty Name="Customer" ColumnName="CustomerID" />
<ScalarProperty Name="SubTotal" ColumnName="SubTotal" />
<ScalarProperty Name="TaxAmt" ColumnName="TaxAmt" />
<ScalarProperty Name="Freight" ColumnName="Freight" />
<ComplexProperty Name ="ExtendedInfo" TypeName ="Microsoft.Samples.Edm.OrderInfo">
<ScalarProperty Name="OrderNumber" ColumnName="SalesOrderNumber" />
<ScalarProperty Name="PurchaseOrder" ColumnName="PurchaseOrderNumber" />
<ScalarProperty Name="AccountNumber" ColumnName="AccountNumber" />
<ScalarProperty Name="Comment" ColumnName="Comment" />
</ComplexProperty>
</EntitySetMapping>
<AssociationSetMapping Name="FK_LineItem_Order_OrderId"
TypeName="Microsoft.Samples.Edm.FK_LineItem_Order_OrderId" StoreEntitySet="SalesOrderDetail">
<EndProperty Name="Order">
<ScalarProperty Name="OrderId" ColumnName="SalesOrderID" />
</EndProperty>
<EndProperty Name="LineItem">
<ScalarProperty Name="LineItemId" ColumnName="SalesOrderDetailID" />
</EndProperty>
<Condition ColumnName="SalesOrderID" IsNull="false" />
</AssociationSetMapping>
</EntityContainerMapping>
</Mapping>
The following SSDL file has been customized to support the Orders and LineItem custom data classes using the
SalesOrderHeader and SalesOrderDetail tables in the AdventureWorks database.

Copy Code

<Schema Namespace="Microsoft.Samples.Edm.Store" Alias="Self" Provider="System.Data.SqlClient"


ProviderManifestToken="2005" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
<EntityContainer Name="Sales">
<EntitySet Name="SalesOrderDetail" EntityType="Microsoft.Samples.Edm.Store.SalesOrderDetail" />
<EntitySet Name="SalesOrderHeader" EntityType="Microsoft.Samples.Edm.Store.SalesOrderHeader" />
<AssociationSet Name="FK_LineItem_Orders_OrderId"
Association="Microsoft.Samples.Edm.Store.FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID">
<End Role="SalesOrderHeader" EntitySet="SalesOrderHeader" />
<End Role="SalesOrderDetail" EntitySet="SalesOrderDetail" />
</AssociationSet>
</EntityContainer>
<EntityType Name="SalesOrderDetail">
<Key>
<PropertyRef Name="SalesOrderDetailID" />
</Key>
<Property Name="SalesOrderID" Type="int" Nullable="false" />
<Property Name="SalesOrderDetailID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="CarrierTrackingNumber" Type="nvarchar" MaxLength="25" />
<Property Name="OrderQty" Type="smallint" Nullable="false" />
<Property Name="ProductID" Type="int" Nullable="false" />
<Property Name="UnitPrice" Type="money" Nullable="false" />
<Property Name="UnitPriceDiscount" Type="money" Nullable="false" />
</EntityType>
<EntityType Name="SalesOrderHeader">
<Key>
<PropertyRef Name="SalesOrderID" />
</Key>
<Property Name="SalesOrderID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="OrderDate" Type="datetime" Nullable="false" />
<Property Name="DueDate" Type="datetime" Nullable="false" />
<Property Name="ShipDate" Type="datetime" />
<Property Name="Status" Type="tinyint" Nullable="false" />
<Property Name="SalesOrderNumber" Type="nvarchar" Nullable="false" StoreGeneratedPattern="Computed"
MaxLength="25" />
<Property Name="PurchaseOrderNumber" Type="nvarchar" MaxLength="25" />
<Property Name="AccountNumber" Type="nvarchar" MaxLength="15" />
<Property Name="CustomerID" Type="int" Nullable="false" />
<Property Name="SubTotal" Type="money" Nullable="false" />
<Property Name="TaxAmt" Type="money" Nullable="false" />
<Property Name="Freight" Type="money" Nullable="false" />
<Property Name="Comment" Type="nvarchar" MaxLength="128" />
</EntityType>
<Association Name="FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID">
<End Role="SalesOrderHeader" Type="Microsoft.Samples.Edm.Store.SalesOrderHeader" Multiplicity="1" />
<End Role="SalesOrderDetail" Type="Microsoft.Samples.Edm.Store.SalesOrderDetail" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="SalesOrderHeader">
<PropertyRef Name="SalesOrderID" />
</Principal>
<Dependent Role="SalesOrderDetail">
<PropertyRef Name="SalesOrderID" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema>

How to: Map Custom Objects to Entities (Entity Framework)

How to: Map Custom Objects to Entities (Entity Framework)


When you use custom data classes with an Entity Data Model (EDM), you must apply EDM attributes that map the
custom classes and properties to entities defined in the conceptual model. The names of the classes and properties
must match the names of entity types and properties in the conceptual model. You must also update the custom
data class to do one of the following:

 Inherit from the EntityObject Base Class

 Implement Custom Data Class Interfaces

For more information, see Object-Entity Mapping Attributes (Entity Framework).

To add EDM attributes to custom data classes

1. Add the following using statements (Imports in Visual Basic) to the code page:

Visual Basic
Copy Code
Imports System.Data
Imports System.Data.Objects.DataClasses
Imports System.Data.Metadata.Edm
Imports Microsoft.Samples.Edm
C#
Copy Code
using System.Data;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using Microsoft.Samples.Edm;
2. Insert EdmSchemaAttribute just above the namespace declaration.

3. Add one instance of EdmRelationshipAttribute for each association. This attribute specifies the name and
namespace of the association, the names of the roles in the association, the types in the association, and the
multiplicity at each end of the association, such as one-to-one or one-to-many. Do this just above the
namespace declaration.

4. Add EdmEntityTypeAttribute to each class that maps to an entity type.

5. Add EdmScalarPropertyAttribute to the property of each class that represents the entity key, which is used to
uniquely identify objects. Specify a value of true for the EntityKeyProperty property of this attribute.

6. Add EdmScalarPropertyAttribute to the remaining properties. If the property does not accept null values,
specify a value of false for the IsNullable property on this attribute.

Example

This example shows EDM attributes applied to Order and LineItem classes. These custom classes are mapped to
the SalesOrderHeader and SalesOrderDetail tables in the AdventureWorks database. Both classes inherit from
EntityObject.

Visual Basic
Copy Code
Option Explicit On
Option Strict On

Imports System
Imports System.Data.SqlTypes
Imports System.Collections.Generic
Imports System.Text
Imports System.Data
Imports System.Data.Objects.DataClasses
Imports System.Data.Metadata.Edm
Imports Microsoft.Samples.Edm

<Assembly: EdmSchemaAttribute()>
<Assembly: EdmRelationshipAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "Order", _
RelationshipMultiplicity.One, GetType(Order), "LineItem", _
RelationshipMultiplicity.Many, GetType(LineItem))>
Namespace Microsoft.Samples.Edm

<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", Name:="Order")> _
Public Class Order
Inherits EntityObject
' Define private property variables.
Private _orderId As Integer
Private _orderDate As DateTime
Private _dueDate As DateTime
Private _shipDate As DateTime
Private _status As Byte
Private _customer As Integer
Private _subTotal As Decimal
Private _tax As Decimal
Private _freight As Decimal
Private _totalDue As Decimal
Private _extendedInfo As OrderInfo

'Default Constructor.
Sub New()

End Sub
' Public properties of the Order object.
<EdmScalarPropertyAttribute(EntityKeyProperty:=True, IsNullable:=False)> _
Public Property OrderId() As Integer
Get
Return _orderId
End Get
Set(ByVal value As Integer)
ReportPropertyChanging("OrderId")
_orderId = value
ReportPropertyChanged("OrderId")
End Set
End Property
' Navigation property that returns a collection of line items.
<EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "LineItem")> _
Public ReadOnly Property LineItem() As EntityCollection(Of LineItem)
Get
Return CType(Me, IEntityWithRelationships).RelationshipManager _
.GetRelatedCollection(Of LineItem) _
("FK_LineItem_Order_OrderId", "LineItem")
End Get
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property OrderDate() As Date
Get
Return _orderDate
End Get
Set(ByVal value As DateTime)
ReportPropertyChanging("OrderDate")
_orderDate = value
ReportPropertyChanged("OrderDate")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property DueDate() As Date
Get
Return _dueDate
End Get
Set(ByVal value As Date)
ReportPropertyChanging("DueDate")
_dueDate = value
ReportPropertyChanged("DueDate")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property ShipDate() As Date
Get
Return _shipDate
End Get
Set(ByVal value As Date)
ReportPropertyChanging("ShipDate")
_shipDate = value
ReportPropertyChanged("ShipDate")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Status() As Byte
Get
Return _status
End Get
Set(ByVal value As Byte)
If _status <> value Then
ReportPropertyChanging("Status")
_status = value
ReportPropertyChanged("Status")
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Customer() As Integer
Get
Return _customer
End Get
Set(ByVal value As Integer)
ReportPropertyChanging("Customer")
_customer = value
ReportPropertyChanged("Customer")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property SubTotal() As Decimal
Get
Return _subTotal
End Get
Set(ByVal value As Decimal)
If _subTotal <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString, "SubTotal"))
End If
ReportPropertyChanging("SubTotal")
_subTotal = value
ReportPropertyChanged("SubTotal")

' Recalculate the order total.


CalculateOrderTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property TaxAmt() As Decimal
Get
Return _tax
End Get
Set(ByVal value As Decimal)
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Tax"))
End If
ReportPropertyChanging("TaxAmt")
_tax = value
ReportPropertyChanged("TaxAmt")

' Recalculate the order total.


CalculateOrderTotal()
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Freight() As Decimal
Get
Return _freight
End Get
Set(ByVal value As Decimal)
If _freight <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Freight"))
End If
ReportPropertyChanging("Freight")
_freight = value
ReportPropertyChanging("Freight")

' Recalculate the order total.


CalculateOrderTotal()
End If
End Set
End Property
Public ReadOnly Property TotalDue() As Decimal
Get
Return _totalDue
End Get
End Property
<EdmComplexPropertyAttribute()> _
Public Property ExtendedInfo() As OrderInfo
Get
Return _extendedInfo
End Get
Set(ByVal value As OrderInfo)
ReportPropertyChanging("ExtendedInfo")
_extendedInfo = value
ReportPropertyChanged("ExtendedInfo")
End Set
End Property
Private Sub CalculateOrderTotal()
' Update the total due as a sum of the other cost properties.
_totalDue = _subTotal + _tax + _freight
End Sub
End Class
<Global.System.Data.Objects.DataClasses.EdmComplexTypeAttribute( _
NamespaceName:="Microsoft.Samples.Edm", Name:="OrderInfo")> _
Partial Public Class OrderInfo
Inherits Global.System.Data.Objects.DataClasses.ComplexObject
Private _orderNumber As String
Private _purchaseOrder As String
Private _accountNumber As String
Private _comment As String
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property OrderNumber() As String
Get
Return _orderNumber
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"OrderNumber", "25"))
End If
ReportPropertyChanging("OrderNumber")
_orderNumber = value
ReportPropertyChanged("OrderNumber")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property PurchaseOrder() As String
Get
Return _purchaseOrder
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"PurchaseOrder", "25"))
End If
If _purchaseOrder <> value Then
ReportPropertyChanging("PurchaseOrder")
_purchaseOrder = value
ReportPropertyChanged("PurchaseOrder")
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property AccountNumber() As String
Get
Return _accountNumber
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 15 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"AccountNumber", "15"))
End If
ReportPropertyChanging("AccountNumber")
_accountNumber = value
ReportPropertyChanged("AccountNumber")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property Comment() As String
Get
Return _comment
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 128 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"Comment", "128"))
End If
If _comment <> value Then
ReportPropertyChanging("Comment")
_comment = value
ReportPropertyChanged("Comment")
End If
End Set
End Property
End Class
<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", _
Name:="LineItem")> _
Public Class LineItem
Inherits EntityObject

' Define private property variables.


Dim _lineItemId As Integer
Dim _trackingNumber As String
Dim _quantity As Short
Dim _product As Integer
Dim _price As Decimal
Dim _discount As Decimal
Dim _total As Decimal
Sub New()

End Sub
' Defines a navigation property to the Order class.
<EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "Order")> _
Public Property Order() As Order
Get
Return CType(Me, _
IEntityWithRelationships).RelationshipManager _
.GetRelatedReference(Of Order) _
("FK_LineItem_Order_OrderId", "Order").Value
End Get
Set(ByVal value As Order)
CType(Me, _
IEntityWithRelationships).RelationshipManager _
.GetRelatedReference(Of Order) _
("FK_LineItem_Order_OrderId", "Order").Value = value
End Set
End Property
<EdmScalarPropertyAttribute(EntityKeyProperty:=True, IsNullable:=False)> _
Public Property LineItemId() As Integer
Get
Return _lineItemId
End Get
Set(ByVal value As Integer)
ReportPropertyChanging("LineItemId")
_lineItemId = value
ReportPropertyChanged("LineItemId")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property TrackingNumber() As String
Get
Return _trackingNumber
End Get
Set(ByVal value As String)
If _trackingNumber <> value Then
' Validate the value before setting it.
If value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"TrackingNumber", "25"))
End If
ReportPropertyChanging("TrackingNumber")
_trackingNumber = value
ReportPropertyChanged("TrackingNumber")
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Quantity() As Short
Get
Return _quantity
End Get
Set(ByVal value As Short)
If _quantity <> value Then
' Validate the value before setting it.
If value < 1 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Quantity"))
End If
ReportPropertyChanging("Quantity")
_quantity = value
ReportPropertyChanged("Quantity")

' Update the line total.


CalculateLineTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Product() As Integer
Get
Return _product
End Get
Set(ByVal value As Integer)
' Validate the value before setting it.
If value < 1 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Product"))
End If
ReportPropertyChanging("Product")
_product = value
ReportPropertyChanged("Product")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Price() As Decimal
Get
Return _price
End Get
Set(ByVal value As Decimal)
If _price <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Price"))
End If
ReportPropertyChanging("Price")
_price = value
ReportPropertyChanged("Price")

' Update the line total.


CalculateLineTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Discount() As Decimal
Get
Return _discount
End Get
Set(ByVal value As Decimal)
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Discount"))
End If
ReportPropertyChanging("Discount")
_discount = value
ReportPropertyChanged("Discount")
End Set
End Property
Public ReadOnly Property Total() As Decimal
Get
Return _total
End Get
End Property
Private Sub CalculateLineTotal()
_total = (_quantity * (_price - _discount))
End Sub
End Class
End Namespace
C#
Copy Code
using System;
using System.Data.SqlTypes;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using Microsoft.Samples.Edm;

[assembly: EdmSchemaAttribute()]
[assembly: EdmRelationshipAttribute("Microsoft.Samples.Edm",
"FK_LineItem_Order_OrderId", "Order",
RelationshipMultiplicity.One, typeof(Order),"LineItem",
RelationshipMultiplicity.Many, typeof(LineItem))]
namespace Microsoft.Samples.Edm
{
[EdmEntityTypeAttribute(NamespaceName="Microsoft.Samples.Edm",Name="Order")]
public class Order : EntityObject
{
// Define private property variables.
private int _orderId;
private DateTime _orderDate;
private DateTime _dueDate;
private DateTime _shipDate;
private byte _status;
private int _customer;
private decimal _subTotal;
private decimal _tax;
private decimal _freight;
private decimal _totalDue;
private OrderInfo _ExtendedInfo;

// Public properties of the Order object.


[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int OrderId
{
get
{
return _orderId;
}
set
{
ReportPropertyChanging("OrderId");
_orderId = value;
ReportPropertyChanged("OrderId");
}
}

// Navigation property that returns a collection of line items.


[EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm","FK_LineItem_Order_OrderId",
"LineItem")]
public System.Data.Objects.DataClasses.EntityCollection<LineItem> LineItem
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedCollection<LineItem>("FK_LineItem_Order_OrderId", "LineItem");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public DateTime OrderDate
{
get
{
return _orderDate;
}
set
{
ReportPropertyChanging("OrderDate");
_orderDate = value;
ReportPropertyChanged("OrderDate");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public DateTime DueDate
{
get
{
return _dueDate;
}
set
{
ReportPropertyChanging("DueDate");
_dueDate = value;
ReportPropertyChanged("DueDate");
}
}
[EdmScalarPropertyAttribute()]
public DateTime ShipDate
{
get
{
return _shipDate;
}
set
{
ReportPropertyChanging("ShipDate");
_shipDate = value;
ReportPropertyChanged("ShipDate");

}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public byte Status
{
get
{
return _status;
}
set
{
if (_status != value)
{
ReportPropertyChanging("Status");
_status = value;
ReportPropertyChanged("Status");
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public int Customer
{
get
{
return _customer;
}
set
{
ReportPropertyChanging("Customer");
_customer = value;
ReportPropertyChanged("Customer");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal SubTotal
{
get
{
return _subTotal;
}
set
{
if (_subTotal != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "SubTotal" }));
}

ReportPropertyChanging("SubTotal");
_subTotal = value;
ReportPropertyChanged("SubTotal");

// Recalculate the order total.


CalculateOrderTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal TaxAmt
{
get
{
return _tax;
}
set
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Tax" }));
}

ReportPropertyChanging("TaxAmt");
_tax = value;
ReportPropertyChanged("TaxAmt");

// Recalculate the order total.


CalculateOrderTotal();
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Freight
{
get
{
return _freight;
}
set
{
if (_freight != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Freight" }));
}

ReportPropertyChanging("Freight");
_freight = value;
ReportPropertyChanging("Freight");
// Recalculate the order total.
CalculateOrderTotal();
}
}

}
public decimal TotalDue
{
get
{
return _totalDue;
}
}

[EdmComplexPropertyAttribute()]
public OrderInfo ExtendedInfo
{
get
{
return _ExtendedInfo;
}
set
{
this.ReportPropertyChanging("ExtendedInfo");
_ExtendedInfo = value;
this.ReportPropertyChanged("ExtendedInfo");
}
}
private void CalculateOrderTotal()
{
// Update the total due as a sum of the other cost properties.
_totalDue = _subTotal + _tax + _freight;
}
}
[EdmComplexTypeAttribute(NamespaceName =
"Microsoft.Samples.Edm", Name = "OrderInfo")]
public partial class OrderInfo : ComplexObject
{
private string _orderNumber;
private string _purchaseOrder;
private string _accountNumber;
private string _comment;

[EdmScalarPropertyAttribute(IsNullable = false)]
public string OrderNumber
{
get
{
return _orderNumber;
}
set
{
// Validate the value before setting it.
if (value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "OrderNumber", "25" }));
}

ReportPropertyChanging("OrderNumber");
_orderNumber = value;
ReportPropertyChanged("OrderNumber");
}
}
[EdmScalarPropertyAttribute()]
public string PurchaseOrder
{
get
{
return _purchaseOrder;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "PurchaseOrder", "25" }));
}
if (_purchaseOrder != value)
{
ReportPropertyChanging("PurchaseOrder");
_purchaseOrder = value;
ReportPropertyChanged("PurchaseOrder");
}
}
}
[EdmScalarPropertyAttribute()]
public string AccountNumber
{
get
{
return _accountNumber;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 15)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "AccountNumber", "15" }));
}
ReportPropertyChanging("AccountNumber");
_accountNumber = value;
ReportPropertyChanged("AccountNumber");
}
}
[EdmScalarPropertyAttribute()]
public string Comment
{
get
{
return _comment;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 128)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "Comment", "128" }));
}
if (_comment != value)
{
ReportPropertyChanging("Comment");
_comment = value;
ReportPropertyChanged("Comment");
}
}
}
}

[EdmEntityTypeAttribute(NamespaceName = "Microsoft.Samples.Edm", Name = "LineItem")]


public class LineItem : EntityObject
{
// Define private property variables.
int _lineItemId;
string _trackingNumber;
short _quantity;
int _product;
decimal _price;
decimal _discount;
decimal _total;

// Default constructor.
public LineItem()
{

// Defines a navigation property to the Order class.


[EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", "FK_LineItem_Order_OrderId",
"Order")]
public Order Order
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<Order>("FK_LineItem_Order_OrderId", "Order").Value;
}
set
{
((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<Order>("FK_LineItem_Order_OrderId", "Order").Value = value;
}
}
[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int LineItemId
{
get
{
return _lineItemId;
}
set
{
ReportPropertyChanging("LineItemId");
_lineItemId = value;
ReportPropertyChanged("LineItemId");
}
}
[EdmScalarPropertyAttribute()]
public string TrackingNumber
{
get
{
return _trackingNumber;
}
set
{
if (_trackingNumber != value)
{
// Validate the value before setting it.
if (value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] {value,"TrackingNumber", "25"}));
}
ReportPropertyChanging("TrackingNumber");
_trackingNumber = value;
ReportPropertyChanged("TrackingNumber");
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public short Quantity
{
get
{
return _quantity;
}
set
{
if (_quantity != value)
{
// Validate the value before setting it.
if (value < 1)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Quantity" }));
}
ReportPropertyChanging("Quantity");
_quantity = value;
ReportPropertyChanged("Quantity");

// Update the line total.


CalculateLineTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public int Product
{
get
{
return _product;
}
set
{
// Validate the value before setting it.
if (value < 1)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Product" }));
}
ReportPropertyChanging("Product");
_product = value;
ReportPropertyChanged("Product");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Price
{
get
{
return _price;
}
set
{
if (_price != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Price" }));
}

ReportPropertyChanging("Price");
_price = value;
ReportPropertyChanged("Price");

// Update the line total.


CalculateLineTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Discount
{
get
{
return _discount;
}
set
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Discount" }));
}
ReportPropertyChanging("Discount");
_discount = value;
ReportPropertyChanged("Discount");
}
}

public decimal Total


{
get
{
return _total;
}
}

private void CalculateLineTotal()


{
_total = (_quantity * (_price - _discount));
}
}
}

How to: Inherit from the EntityObject and ComplexObject Base Classes (Entity Framework)

How to: Inherit from the EntityObject and ComplexObject Base


Classes (Entity Framework)
When you use custom data classes with an Entity Data Model (EDM), you must update the custom data classes to
inherit from EntityObject and ComplexObject. You must also apply EDM attributes that map the custom classes and
properties to entity types and complex types defined in the conceptual schema definition language (CSDL) file. For
more information, see How to: Map Custom Objects to Entities (Entity Framework).

Instead of inheriting from EntityObject or ComplexObject, you can also implement custom data class interfaces.
For more information, see Implementing Custom Data Class Interfaces (Entity Framework).

To inherit from EntityObject

1. Modify the definition of each custom data class so that it inherits from EntityObject, as in the following
example:

Visual Basic
Copy Code
<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm",
Name:="Order")> _
Public Class Order
Inherits EntityObject
C#
Copy Code
[EdmEntityTypeAttribute(NamespaceName="Microsoft.Samples.Edm",Name="Order")
]
public class Order : EntityObject
2. In the settable scalar properties of each data class, add a call to ReportPropertyChanging before you set the
property value, and add a call to ReportPropertyChanged after the property is set. This is shown in the
following example:

Visual Basic
Copy Code
Set(ByVal value As Integer)
ReportPropertyChanging("OrderId")
_orderId = value
ReportPropertyChanged("OrderId")
End Set
C#
Copy Code
set
{
ReportPropertyChanging("OrderId");
_orderId = value;
ReportPropertyChanged("OrderId");
}

To inherit from ComplexObject

1. Modify the definition of each custom complex data class so that it inherits from ComplexObject, as in the
following example:

Visual Basic
Copy Code
<Global.System.Data.Objects.DataClasses.EdmComplexTypeAttribute( _
NamespaceName:="Microsoft.Samples.Edm", Name:="OrderInfo")> _
Partial Public Class OrderInfo
Inherits Global.System.Data.Objects.DataClasses.ComplexObject
C#
Copy Code
[EdmComplexTypeAttribute(NamespaceName =
"Microsoft.Samples.Edm", Name = "OrderInfo")]
public partial class OrderInfo : ComplexObject
2. In the settable scalar properties of each complex data class, add a call to ReportPropertyChanging before you
set the property value, and add a call to ReportPropertyChanged after the property is set. This is shown in the
following example:

Visual Basic
Copy Code
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"PurchaseOrder", "25"))
End If
If _purchaseOrder <> value Then
ReportPropertyChanging("PurchaseOrder")
_purchaseOrder = value
ReportPropertyChanged("PurchaseOrder")
End If
C#
Copy Code
// Validate the value before setting it.
if ((value != null) && value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "PurchaseOrder", "25" }));
}
if (_purchaseOrder != value)
{
ReportPropertyChanging("PurchaseOrder");
_purchaseOrder = value;
ReportPropertyChanged("PurchaseOrder");
}

Example

This example shows the custom data classes Order and LineItem and the complex data class OrderInfo. These
custom classes are mapped to the SalesOrderHeader and SalesOrderDetail tables in the AdventureWorks
database. The Order and LineItem classes inherit from EntityObject, and the complex OrderInfo data class
inherits from ComplexObject.

Visual Basic
Copy Code
Option Explicit On
Option Strict On

Imports System
Imports System.Data.SqlTypes
Imports System.Collections.Generic
Imports System.Text
Imports System.Data
Imports System.Data.Objects.DataClasses
Imports System.Data.Metadata.Edm
Imports Microsoft.Samples.Edm

<Assembly: EdmSchemaAttribute()>
<Assembly: EdmRelationshipAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "Order", _
RelationshipMultiplicity.One, GetType(Order), "LineItem", _
RelationshipMultiplicity.Many, GetType(LineItem))>
Namespace Microsoft.Samples.Edm

<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", Name:="Order")> _
Public Class Order
Inherits EntityObject
' Define private property variables.
Private _orderId As Integer
Private _orderDate As DateTime
Private _dueDate As DateTime
Private _shipDate As DateTime
Private _status As Byte
Private _customer As Integer
Private _subTotal As Decimal
Private _tax As Decimal
Private _freight As Decimal
Private _totalDue As Decimal
Private _extendedInfo As OrderInfo

'Default Constructor.
Sub New()

End Sub
' Public properties of the Order object.
<EdmScalarPropertyAttribute(EntityKeyProperty:=True, IsNullable:=False)> _
Public Property OrderId() As Integer
Get
Return _orderId
End Get
Set(ByVal value As Integer)
ReportPropertyChanging("OrderId")
_orderId = value
ReportPropertyChanged("OrderId")
End Set
End Property
' Navigation property that returns a collection of line items.
<EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "LineItem")> _
Public ReadOnly Property LineItem() As EntityCollection(Of LineItem)
Get
Return CType(Me, IEntityWithRelationships).RelationshipManager _
.GetRelatedCollection(Of LineItem) _
("FK_LineItem_Order_OrderId", "LineItem")
End Get
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property OrderDate() As Date
Get
Return _orderDate
End Get
Set(ByVal value As DateTime)
ReportPropertyChanging("OrderDate")
_orderDate = value
ReportPropertyChanged("OrderDate")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property DueDate() As Date
Get
Return _dueDate
End Get
Set(ByVal value As Date)
ReportPropertyChanging("DueDate")
_dueDate = value
ReportPropertyChanged("DueDate")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property ShipDate() As Date
Get
Return _shipDate
End Get
Set(ByVal value As Date)
ReportPropertyChanging("ShipDate")
_shipDate = value
ReportPropertyChanged("ShipDate")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Status() As Byte
Get
Return _status
End Get
Set(ByVal value As Byte)
If _status <> value Then
ReportPropertyChanging("Status")
_status = value
ReportPropertyChanged("Status")
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Customer() As Integer
Get
Return _customer
End Get
Set(ByVal value As Integer)
ReportPropertyChanging("Customer")
_customer = value
ReportPropertyChanged("Customer")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property SubTotal() As Decimal
Get
Return _subTotal
End Get
Set(ByVal value As Decimal)
If _subTotal <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString, "SubTotal"))
End If
ReportPropertyChanging("SubTotal")
_subTotal = value
ReportPropertyChanged("SubTotal")

' Recalculate the order total.


CalculateOrderTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property TaxAmt() As Decimal
Get
Return _tax
End Get
Set(ByVal value As Decimal)
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Tax"))
End If
ReportPropertyChanging("TaxAmt")
_tax = value
ReportPropertyChanged("TaxAmt")

' Recalculate the order total.


CalculateOrderTotal()
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Freight() As Decimal
Get
Return _freight
End Get
Set(ByVal value As Decimal)
If _freight <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Freight"))
End If
ReportPropertyChanging("Freight")
_freight = value
ReportPropertyChanging("Freight")

' Recalculate the order total.


CalculateOrderTotal()
End If
End Set
End Property
Public ReadOnly Property TotalDue() As Decimal
Get
Return _totalDue
End Get
End Property
<EdmComplexPropertyAttribute()> _
Public Property ExtendedInfo() As OrderInfo
Get
Return _extendedInfo
End Get
Set(ByVal value As OrderInfo)
ReportPropertyChanging("ExtendedInfo")
_extendedInfo = value
ReportPropertyChanged("ExtendedInfo")
End Set
End Property
Private Sub CalculateOrderTotal()
' Update the total due as a sum of the other cost properties.
_totalDue = _subTotal + _tax + _freight
End Sub
End Class
<Global.System.Data.Objects.DataClasses.EdmComplexTypeAttribute( _
NamespaceName:="Microsoft.Samples.Edm", Name:="OrderInfo")> _
Partial Public Class OrderInfo
Inherits Global.System.Data.Objects.DataClasses.ComplexObject
Private _orderNumber As String
Private _purchaseOrder As String
Private _accountNumber As String
Private _comment As String
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property OrderNumber() As String
Get
Return _orderNumber
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"OrderNumber", "25"))
End If
ReportPropertyChanging("OrderNumber")
_orderNumber = value
ReportPropertyChanged("OrderNumber")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property PurchaseOrder() As String
Get
Return _purchaseOrder
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"PurchaseOrder", "25"))
End If
If _purchaseOrder <> value Then
ReportPropertyChanging("PurchaseOrder")
_purchaseOrder = value
ReportPropertyChanged("PurchaseOrder")
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property AccountNumber() As String
Get
Return _accountNumber
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 15 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"AccountNumber", "15"))
End If
ReportPropertyChanging("AccountNumber")
_accountNumber = value
ReportPropertyChanged("AccountNumber")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property Comment() As String
Get
Return _comment
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 128 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"Comment", "128"))
End If
If _comment <> value Then
ReportPropertyChanging("Comment")
_comment = value
ReportPropertyChanged("Comment")
End If
End Set
End Property
End Class
<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", _
Name:="LineItem")> _
Public Class LineItem
Inherits EntityObject

' Define private property variables.


Dim _lineItemId As Integer
Dim _trackingNumber As String
Dim _quantity As Short
Dim _product As Integer
Dim _price As Decimal
Dim _discount As Decimal
Dim _total As Decimal
Sub New()

End Sub
' Defines a navigation property to the Order class.
<EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "Order")> _
Public Property Order() As Order
Get
Return CType(Me, _
IEntityWithRelationships).RelationshipManager _
.GetRelatedReference(Of Order) _
("FK_LineItem_Order_OrderId", "Order").Value
End Get
Set(ByVal value As Order)
CType(Me, _
IEntityWithRelationships).RelationshipManager _
.GetRelatedReference(Of Order) _
("FK_LineItem_Order_OrderId", "Order").Value = value
End Set
End Property
<EdmScalarPropertyAttribute(EntityKeyProperty:=True, IsNullable:=False)> _
Public Property LineItemId() As Integer
Get
Return _lineItemId
End Get
Set(ByVal value As Integer)
ReportPropertyChanging("LineItemId")
_lineItemId = value
ReportPropertyChanged("LineItemId")
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property TrackingNumber() As String
Get
Return _trackingNumber
End Get
Set(ByVal value As String)
If _trackingNumber <> value Then
' Validate the value before setting it.
If value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"TrackingNumber", "25"))
End If
ReportPropertyChanging("TrackingNumber")
_trackingNumber = value
ReportPropertyChanged("TrackingNumber")
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Quantity() As Short
Get
Return _quantity
End Get
Set(ByVal value As Short)
If _quantity <> value Then
' Validate the value before setting it.
If value < 1 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Quantity"))
End If
ReportPropertyChanging("Quantity")
_quantity = value
ReportPropertyChanged("Quantity")

' Update the line total.


CalculateLineTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Product() As Integer
Get
Return _product
End Get
Set(ByVal value As Integer)
' Validate the value before setting it.
If value < 1 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Product"))
End If
ReportPropertyChanging("Product")
_product = value
ReportPropertyChanged("Product")
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Price() As Decimal
Get
Return _price
End Get
Set(ByVal value As Decimal)
If _price <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Price"))
End If
ReportPropertyChanging("Price")
_price = value
ReportPropertyChanged("Price")

' Update the line total.


CalculateLineTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Discount() As Decimal
Get
Return _discount
End Get
Set(ByVal value As Decimal)
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Discount"))
End If
ReportPropertyChanging("Discount")
_discount = value
ReportPropertyChanged("Discount")
End Set
End Property
Public ReadOnly Property Total() As Decimal
Get
Return _total
End Get
End Property
Private Sub CalculateLineTotal()
_total = (_quantity * (_price - _discount))
End Sub
End Class
End Namespace
C#
Copy Code
using System;
using System.Data.SqlTypes;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using Microsoft.Samples.Edm;

[assembly: EdmSchemaAttribute()]
[assembly: EdmRelationshipAttribute("Microsoft.Samples.Edm",
"FK_LineItem_Order_OrderId", "Order",
RelationshipMultiplicity.One, typeof(Order),"LineItem",
RelationshipMultiplicity.Many, typeof(LineItem))]
namespace Microsoft.Samples.Edm
{
[EdmEntityTypeAttribute(NamespaceName="Microsoft.Samples.Edm",Name="Order")]
public class Order : EntityObject
{
// Define private property variables.
private int _orderId;
private DateTime _orderDate;
private DateTime _dueDate;
private DateTime _shipDate;
private byte _status;
private int _customer;
private decimal _subTotal;
private decimal _tax;
private decimal _freight;
private decimal _totalDue;
private OrderInfo _ExtendedInfo;

// Public properties of the Order object.


[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int OrderId
{
get
{
return _orderId;
}
set
{
ReportPropertyChanging("OrderId");
_orderId = value;
ReportPropertyChanged("OrderId");
}
}

// Navigation property that returns a collection of line items.


[EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm","FK_LineItem_Order_OrderId",
"LineItem")]
public System.Data.Objects.DataClasses.EntityCollection<LineItem> LineItem
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedCollection<LineItem>("FK_LineItem_Order_OrderId", "LineItem");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public DateTime OrderDate
{
get
{
return _orderDate;
}
set
{
ReportPropertyChanging("OrderDate");
_orderDate = value;
ReportPropertyChanged("OrderDate");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public DateTime DueDate
{
get
{
return _dueDate;
}
set
{
ReportPropertyChanging("DueDate");
_dueDate = value;
ReportPropertyChanged("DueDate");
}
}
[EdmScalarPropertyAttribute()]
public DateTime ShipDate
{
get
{
return _shipDate;
}
set
{
ReportPropertyChanging("ShipDate");
_shipDate = value;
ReportPropertyChanged("ShipDate");

}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public byte Status
{
get
{
return _status;
}
set
{
if (_status != value)
{
ReportPropertyChanging("Status");
_status = value;
ReportPropertyChanged("Status");
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public int Customer
{
get
{
return _customer;
}
set
{
ReportPropertyChanging("Customer");
_customer = value;
ReportPropertyChanged("Customer");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal SubTotal
{
get
{
return _subTotal;
}
set
{
if (_subTotal != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "SubTotal" }));
}

ReportPropertyChanging("SubTotal");
_subTotal = value;
ReportPropertyChanged("SubTotal");

// Recalculate the order total.


CalculateOrderTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal TaxAmt
{
get
{
return _tax;
}
set
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Tax" }));
}

ReportPropertyChanging("TaxAmt");
_tax = value;
ReportPropertyChanged("TaxAmt");

// Recalculate the order total.


CalculateOrderTotal();
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Freight
{
get
{
return _freight;
}
set
{
if (_freight != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Freight" }));
}

ReportPropertyChanging("Freight");
_freight = value;
ReportPropertyChanging("Freight");

// Recalculate the order total.


CalculateOrderTotal();
}
}

}
public decimal TotalDue
{
get
{
return _totalDue;
}
}

[EdmComplexPropertyAttribute()]
public OrderInfo ExtendedInfo
{
get
{
return _ExtendedInfo;
}
set
{
this.ReportPropertyChanging("ExtendedInfo");
_ExtendedInfo = value;
this.ReportPropertyChanged("ExtendedInfo");
}
}
private void CalculateOrderTotal()
{
// Update the total due as a sum of the other cost properties.
_totalDue = _subTotal + _tax + _freight;
}
}
[EdmComplexTypeAttribute(NamespaceName =
"Microsoft.Samples.Edm", Name = "OrderInfo")]
public partial class OrderInfo : ComplexObject
{
private string _orderNumber;
private string _purchaseOrder;
private string _accountNumber;
private string _comment;

[EdmScalarPropertyAttribute(IsNullable = false)]
public string OrderNumber
{
get
{
return _orderNumber;
}
set
{
// Validate the value before setting it.
if (value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "OrderNumber", "25" }));
}

ReportPropertyChanging("OrderNumber");
_orderNumber = value;
ReportPropertyChanged("OrderNumber");
}
}
[EdmScalarPropertyAttribute()]
public string PurchaseOrder
{
get
{
return _purchaseOrder;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "PurchaseOrder", "25" }));
}
if (_purchaseOrder != value)
{
ReportPropertyChanging("PurchaseOrder");
_purchaseOrder = value;
ReportPropertyChanged("PurchaseOrder");
}
}
}
[EdmScalarPropertyAttribute()]
public string AccountNumber
{
get
{
return _accountNumber;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 15)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "AccountNumber", "15" }));
}
ReportPropertyChanging("AccountNumber");
_accountNumber = value;
ReportPropertyChanged("AccountNumber");
}
}
[EdmScalarPropertyAttribute()]
public string Comment
{
get
{
return _comment;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 128)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "Comment", "128" }));
}
if (_comment != value)
{
ReportPropertyChanging("Comment");
_comment = value;
ReportPropertyChanged("Comment");
}
}
}
}

[EdmEntityTypeAttribute(NamespaceName = "Microsoft.Samples.Edm", Name = "LineItem")]


public class LineItem : EntityObject
{
// Define private property variables.
int _lineItemId;
string _trackingNumber;
short _quantity;
int _product;
decimal _price;
decimal _discount;
decimal _total;
// Default constructor.
public LineItem()
{

// Defines a navigation property to the Order class.


[EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", "FK_LineItem_Order_OrderId",
"Order")]
public Order Order
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<Order>("FK_LineItem_Order_OrderId", "Order").Value;
}
set
{
((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<Order>("FK_LineItem_Order_OrderId", "Order").Value = value;
}
}
[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int LineItemId
{
get
{
return _lineItemId;
}
set
{
ReportPropertyChanging("LineItemId");
_lineItemId = value;
ReportPropertyChanged("LineItemId");
}
}
[EdmScalarPropertyAttribute()]
public string TrackingNumber
{
get
{
return _trackingNumber;
}
set
{
if (_trackingNumber != value)
{
// Validate the value before setting it.
if (value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] {value,"TrackingNumber", "25"}));
}
ReportPropertyChanging("TrackingNumber");
_trackingNumber = value;
ReportPropertyChanged("TrackingNumber");
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public short Quantity
{
get
{
return _quantity;
}
set
{
if (_quantity != value)
{
// Validate the value before setting it.
if (value < 1)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Quantity" }));
}
ReportPropertyChanging("Quantity");
_quantity = value;
ReportPropertyChanged("Quantity");

// Update the line total.


CalculateLineTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public int Product
{
get
{
return _product;
}
set
{
// Validate the value before setting it.
if (value < 1)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Product" }));
}
ReportPropertyChanging("Product");
_product = value;
ReportPropertyChanged("Product");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Price
{
get
{
return _price;
}
set
{
if (_price != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Price" }));
}

ReportPropertyChanging("Price");
_price = value;
ReportPropertyChanged("Price");

// Update the line total.


CalculateLineTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Discount
{
get
{
return _discount;
}
set
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Discount" }));
}
ReportPropertyChanging("Discount");
_discount = value;
ReportPropertyChanged("Discount");
}
}

public decimal Total


{
get
{
return _total;
}
}

private void CalculateLineTotal()


{
_total = (_quantity * (_price - _discount));
}
}
}

How to: Implement Custom Data Class Interfaces (Entity Framework)

How to: Implement Custom Data Class Interfaces (Entity


Framework)
When you use custom data classes with an Entity Data Model (EDM), the classes must implement the following
custom data class interfaces:

 IEntityWithChangeTracker. Enables change tracking.


 IEntityWithKey. Optional. Exposes an entity key.

 IEntityWithRelationships. Required for entities with associations.

For more information, see Implementing Custom Data Class Interfaces (Entity Framework). You must also apply
EDM attributes that map the custom classes and properties to entities defined in the conceptual schema definition
language (CSDL) file. For more information, see How to: Map Custom Objects to Entities (Entity Framework).

Instead of directly implementing data class interfaces, you can also inherit from EntityObject. This is the
recommended way to use custom data classes with an EDM. For more information, see Customizing Objects (Entity
Framework) and How to: Inherit from the EntityObject and ComplexObject Base Classes (Entity Framework).

To implement custom data class interfaces

1. Modify the definition of each custom data class so that it implements the IEntityWithChangeTracker,
IEntityWithKey, and IEntityWithRelationships interfaces, as in the following example:

Visual Basic
Copy Code
<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", Name:="Order")> _
Public Class Order
Implements IEntityWithRelationships, IEntityWithChangeTracker, IEntityWithKey
C#
Copy Code
[EdmEntityTypeAttribute(NamespaceName = "Microsoft.Samples.Edm", Name = "Order")]
public class Order : IEntityWithRelationships, IEntityWithChangeTracker, IEntityWithKey
2. (Optional) Explicitly implement the EntityKey property in each custom data class, as in the following example:

Visual Basic
Copy Code
Dim _entityKey As EntityKey = Nothing

' Define the EntityKey property for the class.


Property EntityKey() As EntityKey Implements IEntityWithKey.EntityKey
Get
Return _entityKey
End Get
Set(ByVal value As EntityKey)
' Set the EntityKey property, if it is not set.
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName)
_entityKey = value
_changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName)
Else
_entityKey = value
End If
End Set
End Property
C#
Copy Code
EntityKey _entityKey = null;

// Define the EntityKey property for the class.


EntityKey IEntityWithKey.EntityKey
{
get
{
return _entityKey;
}
set
{
// Set the EntityKey property, if it is not set.
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName);
_entityKey = value;
_changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName);
}
else
{
_entityKey = value;
}
}
}
3. Explicitly implement the SetChangeTracker method in each custom data class, as in the following example:

Visual Basic
Copy Code
Dim _changeTracker As IEntityChangeTracker = Nothing

' Specify the IEntityChangeTracker to use for tracking changes.


Private Sub SetChangeTracker(ByVal changeTracker As IEntityChangeTracker) _
Implements IEntityWithChangeTracker.SetChangeTracker
_changeTracker = changeTracker

' Every time the change tracker is set, we must also set all the
' complex type change trackers.
If Not _extendedInfo Is Nothing Then
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker)
End If
End Sub
C#
Copy Code
IEntityChangeTracker _changeTracker = null;

// Specify the IEntityChangeTracker to use for tracking changes.


void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
{
_changeTracker = changeTracker;

// Every time the change tracker is set, we must also set all the
// complex type change trackers.
if (_extendedInfo != null)
{
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
}
}
4. This method is required to report changes in data properties to Object Services.

5. For IEntityWithRelationships, explicitly implement the RelationshipManager property, as in the following


example:

Visual Basic
Copy Code
Dim _relationships As RelationshipManager = Nothing

' Define a relationship manager for the class.


ReadOnly Property RelationshipManager() As RelationshipManager _
Implements IEntityWithRelationships.RelationshipManager
Get
If _relationships Is Nothing Then
_relationships = RelationshipManager.Create(Me)
End If
Return _relationships
End Get
End Property
C#
Copy Code
RelationshipManager _relationships = null;

// Define a relationship manager for the class.


RelationshipManager IEntityWithRelationships.RelationshipManager
{
get
{
if (null == _relationships)
_relationships = RelationshipManager.Create(this);
return _relationships;
}
}
6. In the settable scalar properties of each data class, add a call to EntityMemberChanging before setting the
property value, and add a call to EntityMemberChanged after the property is set. This is shown in the following
example:

Visual Basic
Copy Code
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Status() As Byte
Get
Return _status
End Get
Set(ByVal value As Byte)
If _status <> value Then
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Status")
_status = value
_changeTracker.EntityMemberChanged("Status")
Else
_status = value
End If
End If
End Set
End Property
C#
Copy Code
[EdmScalarPropertyAttribute(IsNullable = false)]
public byte Status
{
get
{
return _status;
}
set
{
if (_status != value)
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Status");
_status = value;
_changeTracker.EntityMemberChanged("Status");
}
else
{
_status = value;
}
}
}
}
7. In the settable complex properties of each data class, add a call to EntityComplexMemberChanging before
setting the property value, and add a call to EntityComplexMemberChanged after the property is set.

Example

This example shows the custom data classes Order and LineItem and a complex type OrderInfo. These custom
classes are mapped to the SalesOrderHeader and SalesOrderDetail tables in the AdventureWorks database.
Both entity classes implement all three custom data class interfaces. The OrderInfo complex type class
demonstrates a suggested change tracking implementation.

Visual Basic
Copy Code
Option Explicit On
Option Strict On

Imports System
Imports System.Data.SqlTypes
Imports System.Collections.Generic
Imports System.Text
Imports System.Data
Imports System.Data.Objects.DataClasses
Imports System.Data.Metadata.Edm
Imports Microsoft.Samples.Edm

<Assembly: EdmSchemaAttribute()>
<Assembly: EdmRelationshipAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "Order", _
RelationshipMultiplicity.One, GetType(Order), "LineItem", _
RelationshipMultiplicity.Many, GetType(LineItem))>
Namespace Microsoft.Samples.Edm

<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", Name:="Order")> _
Public Class Order
Implements IEntityWithRelationships, IEntityWithChangeTracker, IEntityWithKey

' Define private property variables.


Private _orderId As Integer
Private _orderDate As DateTime
Private _dueDate As DateTime
Private _shipDate As DateTime
Private _status As Byte
Private _customer As Integer
Private _subTotal As Decimal
Private _tax As Decimal
Private _freight As Decimal
Private _totalDue As Decimal
Private _extendedInfo As OrderInfo

#Region "ExplicitImplementation"
Dim _changeTracker As IEntityChangeTracker = Nothing

' Specify the IEntityChangeTracker to use for tracking changes.


Private Sub SetChangeTracker(ByVal changeTracker As IEntityChangeTracker) _
Implements IEntityWithChangeTracker.SetChangeTracker
_changeTracker = changeTracker

' Every time the change tracker is set, we must also set all the
' complex type change trackers.
If Not _extendedInfo Is Nothing Then
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker)
End If
End Sub

Dim _entityKey As EntityKey = Nothing

' Define the EntityKey property for the class.


Property EntityKey() As EntityKey Implements IEntityWithKey.EntityKey
Get
Return _entityKey
End Get
Set(ByVal value As EntityKey)
' Set the EntityKey property, if it is not set.
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName)
_entityKey = value
_changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName)
Else
_entityKey = value
End If
End Set
End Property

Dim _relationships As RelationshipManager = Nothing

' Define a relationship manager for the class.


ReadOnly Property RelationshipManager() As RelationshipManager _
Implements IEntityWithRelationships.RelationshipManager
Get
If _relationships Is Nothing Then
_relationships = RelationshipManager.Create(Me)
End If
Return _relationships
End Get
End Property
#End Region

' Public properties of the Order object.


<EdmScalarPropertyAttribute(EntityKeyProperty:=True, IsNullable:=False)> _
Public Property OrderId() As Integer
Get
Return _orderId
End Get
Set(ByVal value As Integer)
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("OrderId")
_orderId = value
_changeTracker.EntityMemberChanged("OrderId")
Else
_orderId = value
End If
End Set
End Property
' Navigation property that returns a collection of line items.
<EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", "FK_LineItem_Order_OrderId",
"LineItem")> _
Public ReadOnly Property LineItem() As EntityCollection(Of LineItem)
Get
Return CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedCollection(Of LineItem)
_
("FK_LineItem_Order_OrderId", "LineItem")
End Get
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property OrderDate() As Date
Get
Return _orderDate
End Get
Set(ByVal value As DateTime)
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("OrderDate")
_orderDate = value
_changeTracker.EntityMemberChanged("OrderDate")
Else
_orderDate = value
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property DueDate() As Date
Get
Return _dueDate
End Get
Set(ByVal value As Date)
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("DueDate")

_changeTracker.EntityMemberChanged("DueDate")
Else
_dueDate = value
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property ShipDate() As Date
Get
Return _shipDate
End Get
Set(ByVal value As Date)
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("ShipDate")
_changeTracker.EntityMemberChanged("ShipDate")
Else
_shipDate = value
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Status() As Byte
Get
Return _status
End Get
Set(ByVal value As Byte)
If _status <> value Then
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Status")
_status = value
_changeTracker.EntityMemberChanged("Status")
Else
_status = value
End If
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Customer() As Integer
Get
Return _customer
End Get
Set(ByVal value As Integer)
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Customer")
_customer = value
_changeTracker.EntityMemberChanged("Customer")
Else
_customer = value
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property SubTotal() As Decimal
Get
Return _subTotal
End Get
Set(ByVal value As Decimal)
If _subTotal <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString, "SubTotal"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("SubTotal")
_subTotal = value
_changeTracker.EntityMemberChanged("SubTotal")
Else
_subTotal = value
End If
' Recalculate the order total.
CalculateOrderTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property TaxAmt() As Decimal
Get
Return _tax
End Get
Set(ByVal value As Decimal)
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Tax"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("TaxAmt")
_tax = value
_changeTracker.EntityMemberChanged("TaxAmt")
Else
_tax = value
End If

' Recalculate the order total.


CalculateOrderTotal()
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Freight() As Decimal
Get
Return _freight
End Get
Set(ByVal value As Decimal)
If _freight <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Freight"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Freight")
_freight = value
_changeTracker.EntityMemberChanging("Freight")
Else
_freight = value
End If
' Recalculate the order total.
CalculateOrderTotal()
End If
End Set
End Property
Public ReadOnly Property TotalDue() As Decimal
Get
Return _totalDue
End Get
End Property
<EdmComplexPropertyAttribute()> _
Public Property ExtendedInfo() As OrderInfo
Get
Return _extendedInfo
End Get
Set(ByVal value As OrderInfo)

' For a complex type any changes in the complex type


' properties all get tracked together.
' The change tracker may be Nothing during object materialization.
If Not _changeTracker Is Nothing Then

' Since this is a complex property, we need to reset the change


' tracker on the complex type.
If Not _extendedInfo Is Nothing Then
' Reset the change tracker.
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", Nothing)
End If

' Report the change.


_changeTracker.EntityMemberChanging("ExtendedInfo")
_extendedInfo = value
_changeTracker.EntityMemberChanging("ExtendedInfo")

Else
_extendedInfo = value
End If

' Rest the change tracker. Complex type property cannot be Nothing.
If Not _extendedInfo Is Nothing Then
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker)
End If
End Set
End Property
Private Sub CalculateOrderTotal()
' Update the total due as a sum of the other cost properties.
_totalDue = _subTotal + _tax + _freight
End Sub
End Class
' Base class for complex types that implements change tracking.
Public MustInherit Class ComplexTypeChangeTracker
Protected _complexChangeTracker As IEntityChangeTracker = Nothing
Private _rootComplexPropertyName As String

' Gets an IEntityChangeTracker to call for properties change.


' You must do this in order to track changes.
Public Overridable Sub SetComplexChangeTracker( _
ByVal rootComplexPropertyName As String, _
ByVal complexChangeTracker As IEntityChangeTracker)
_rootComplexPropertyName = rootComplexPropertyName
_complexChangeTracker = complexChangeTracker
End Sub

' Protected method that is called before the change for change tracking
' each of the scalar properties in the complex type.
Protected Sub ReportMemberChanging(ByVal scalarPropertyName As String)
If Not _complexChangeTracker Is Nothing Then
_complexChangeTracker.EntityComplexMemberChanging( _
_rootComplexPropertyName, Me, scalarPropertyName)
End If
End Sub

' Protected method that is called after the change for change tracking
' each of the scalar properties in the complex type.
Protected Sub ReportMemberChanged(ByVal scalarPropertyName As String)
If Not _complexChangeTracker Is Nothing Then
_complexChangeTracker.EntityComplexMemberChanged( _
_rootComplexPropertyName, Me, scalarPropertyName)
End If
End Sub
End Class
<EdmComplexTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", Name:="OrderInfo")> _
Partial Public Class OrderInfo
Inherits ComplexTypeChangeTracker
Private _orderNumber As String
Private _purchaseOrder As String
Private _accountNumber As String
Private _comment As String
Private _extendedInfo As OrderInfo

Public Overrides Sub SetComplexChangeTracker(ByVal rootComplexPropertyName As String, _


ByVal changeTracker As IEntityChangeTracker)

' Call SetChangeTracker on the base class to set the change tracker
' and the name of the root complex type property on the entity.
MyBase.SetComplexChangeTracker(rootComplexPropertyName, changeTracker)
End Sub
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property OrderNumber() As String
Get
Return _orderNumber
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"OrderNumber", "25"))
End If
' Report the change if the change tracker exists.
If Not _complexChangeTracker Is Nothing Then
ReportMemberChanging("OrderNumber")
_orderNumber = value
ReportMemberChanged("OrderNumber")
Else
_orderNumber = value
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property PurchaseOrder() As String
Get
Return _purchaseOrder
End Get
Set(ByVal value As String)
If (value <> Nothing) AndAlso value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"PurchaseOrder", "25"))
End If
If _purchaseOrder <> value Then
' Report the change if the change tracker exists.
If Not _complexChangeTracker Is Nothing Then
ReportMemberChanging("PurchaseOrder")
_purchaseOrder = value
ReportMemberChanged("PurchaseOrder")
Else
_purchaseOrder = value
End If
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property AccountNumber() As String
Get
Return _accountNumber
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 15 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"AccountNumber", "15"))
End If
' Report the change if the change tracker exists.
If Not _complexChangeTracker Is Nothing Then
ReportMemberChanging("AccountNumber")
_accountNumber = value
ReportMemberChanged("AccountNumber")
Else
_accountNumber = value
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property Comment() As String
Get
Return _comment
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 128 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"Comment", "128"))
End If
If _comment <> value Then
' Report the change if the change tracker exists.
If Not _complexChangeTracker Is Nothing Then
ReportMemberChanging("Comment")
_comment = value
ReportMemberChanged("Comment")
Else
_comment = value
End If
End If
End Set
End Property
End Class
<EdmEntityTypeAttribute(NamespaceName:="Microsoft.Samples.Edm", Name:="LineItem")> _
Public Class LineItem
Implements IEntityWithRelationships, IEntityWithChangeTracker, IEntityWithKey

' Define private property variables.


Dim _lineItemId As Integer
Dim _trackingNumber As String
Dim _quantity As Short
Dim _product As Integer
Dim _price As Decimal
Dim _discount As Decimal
Dim _total As Decimal

Dim _changeTracker As IEntityChangeTracker = Nothing

' Specify the IEntityChangeTracker to use for tracking changes.


Private Sub SetChangeTracker(ByVal changeTracker As IEntityChangeTracker) _
Implements IEntityWithChangeTracker.SetChangeTracker
_changeTracker = changeTracker
End Sub

Dim _entityKey As EntityKey = Nothing

' Define the EntityKey property for the class.


Property EntityKey() As EntityKey Implements IEntityWithKey.EntityKey
Get
Return _entityKey
End Get
Set(ByVal value As EntityKey)
' Set the EntityKey property, if it is not set.
' Changing an existing value will cause an exception.
If _entityKey Is Nothing Then
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName)
_entityKey = value
_changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName)
Else
_entityKey = value
End If
End If
End Set
End Property

Dim _relationships As RelationshipManager = Nothing

' Define a relationship manager for the class.


ReadOnly Property RelationshipManager() As RelationshipManager _
Implements IEntityWithRelationships.RelationshipManager
Get
If _relationships Is Nothing Then
_relationships = RelationshipManager.Create(Me)
End If
Return _relationships
End Get
End Property
' Defines a navigation property to the Order class.
<EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", _
"FK_LineItem_Order_OrderId", "Order")> _
Public Property Order() As Order
Get
Return CType(Me, _
IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order) _
("FK_LineItem_Order_OrderId", "Order").Value
End Get
Set(ByVal value As Order)
CType(Me, _
IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order) _
("FK_LineItem_Order_OrderId", "Order").Value = value
End Set
End Property
<EdmScalarPropertyAttribute(EntityKeyProperty:=True, IsNullable:=False)> _
Public Property LineItemId() As Integer
Get
Return _lineItemId
End Get
Set(ByVal value As Integer)
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("LineItemId")
_lineItemId = value
_changeTracker.EntityMemberChanged("LineItemId")
Else
_lineItemId = value
End If
End Set
End Property
<EdmScalarPropertyAttribute()> _
Public Property TrackingNumber() As String
Get
Return _trackingNumber
End Get
Set(ByVal value As String)
If _trackingNumber <> value Then
' Validate the value before setting it.
If value.Length > 25 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"TrackingNumber", "25"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("TrackingNumber")
_trackingNumber = value
_changeTracker.EntityMemberChanged("TrackingNumber")
Else
_trackingNumber = value
End If
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Quantity() As Short
Get
Return _quantity
End Get
Set(ByVal value As Short)
If _quantity <> value Then
' Validate the value before setting it.
If value < 1 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Quantity"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Quantity")
_quantity = value
_changeTracker.EntityMemberChanged("Quantity")
Else
_quantity = value
End If
' Update the line total.
CalculateLineTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Product() As Integer
Get
Return _product
End Get
Set(ByVal value As Integer)
' Validate the value before setting it.
If value < 1 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Product"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Product")
_product = value
_changeTracker.EntityMemberChanged("Product")
Else
_product = value
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Price() As Decimal
Get
Return _price
End Get
Set(ByVal value As Decimal)
If _price <> value Then
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Price"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Price")
_price = value
_changeTracker.EntityMemberChanged("Price")
Else
_price = value
End If
' Update the line total.
CalculateLineTotal()
End If
End Set
End Property
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Discount() As Decimal
Get
Return _discount
End Get
Set(ByVal value As Decimal)
' Validate the value before setting it.
If value < 0 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidNegative, _
value.ToString(), "Discount"))
End If
' Report the change if the change tracker exists.
If Not _changeTracker Is Nothing Then
_changeTracker.EntityMemberChanging("Discount")
_discount = value
_changeTracker.EntityMemberChanged("Discount")
Else
_discount = value
End If
End Set
End Property
Public ReadOnly Property Total() As Decimal
Get
Return _total
End Get
End Property
Private Sub CalculateLineTotal()
_total = (_quantity * (_price - _discount))
End Sub
End Class
End Namespace
C#
Copy Code
using System;
using System.Data.SqlTypes;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Data.Metadata.Edm;
using Microsoft.Samples.Edm;
//using Microsoft.Samples.Edm;

[assembly: EdmSchemaAttribute()]
[assembly: EdmRelationshipAttribute("Microsoft.Samples.Edm",
"FK_LineItem_Order_OrderId", "Order",
RelationshipMultiplicity.One, typeof(Order), "LineItem",
RelationshipMultiplicity.Many, typeof(LineItem))]
namespace Microsoft.Samples.Edm
{
[EdmEntityTypeAttribute(NamespaceName = "Microsoft.Samples.Edm", Name = "Order")]
public class Order : IEntityWithRelationships, IEntityWithChangeTracker, IEntityWithKey
{
// Define private property variables.
private int _orderId;
private DateTime _orderDate;
private DateTime _dueDate;
private DateTime _shipDate;
private byte _status;
private int _customer;
private decimal _subTotal;
private decimal _tax;
private decimal _freight;
private decimal _totalDue;
private OrderInfo _extendedInfo;

#region ExplicitImplementation
IEntityChangeTracker _changeTracker = null;

// Specify the IEntityChangeTracker to use for tracking changes.


void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
{
_changeTracker = changeTracker;

// Every time the change tracker is set, we must also set all the
// complex type change trackers.
if (_extendedInfo != null)
{
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
}
}

EntityKey _entityKey = null;

// Define the EntityKey property for the class.


EntityKey IEntityWithKey.EntityKey
{
get
{
return _entityKey;
}
set
{
// Set the EntityKey property, if it is not set.
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName);
_entityKey = value;
_changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName);
}
else
{
_entityKey = value;
}
}
}

RelationshipManager _relationships = null;

// Define a relationship manager for the class.


RelationshipManager IEntityWithRelationships.RelationshipManager
{
get
{
if (null == _relationships)
_relationships = RelationshipManager.Create(this);
return _relationships;
}
}
#endregion

// Public properties of the Order object.


[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int OrderId
{
get
{
return _orderId;
}
set
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("OrderId");
_orderId = value;
_changeTracker.EntityMemberChanged("OrderId");
}
else
{
_orderId = value;
}
}
}

// Navigation property that returns a collection of line items.


[EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", "FK_LineItem_Order_OrderId",
"LineItem")]
public System.Data.Objects.DataClasses.EntityCollection<LineItem> LineItem
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedCollection<LineItem>("FK_LineItem_Order_OrderId", "LineItem");
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public DateTime OrderDate
{
get
{
return _orderDate;
}
set
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("OrderDate");
_orderDate = value;
_changeTracker.EntityMemberChanged("OrderDate");
}
else
{
_orderDate = value;
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public DateTime DueDate
{
get
{
return _dueDate;
}
set
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("DueDate");
_dueDate = value;
_changeTracker.EntityMemberChanged("DueDate");
}
else
{
_dueDate = value;
}
}
}
[EdmScalarPropertyAttribute()]
public DateTime ShipDate
{
get
{
return _shipDate;
}
set
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("ShipDate");
_shipDate = value;
_changeTracker.EntityMemberChanged("ShipDate");
}
else
{
_shipDate = value;
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public byte Status
{
get
{
return _status;
}
set
{
if (_status != value)
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Status");
_status = value;
_changeTracker.EntityMemberChanged("Status");
}
else
{
_status = value;
}
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public int Customer
{
get
{
return _customer;
}
set
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Customer");
_customer = value;
_changeTracker.EntityMemberChanged("Customer");
}
else
{
_customer = value;
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal SubTotal
{
get
{
return _subTotal;
}
set
{
if (_subTotal != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "SubTotal" }));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("SubTotal");
_subTotal = value;
_changeTracker.EntityMemberChanged("SubTotal");
}
else
{
_subTotal = value;
}

// Recalculate the order total.


CalculateOrderTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal TaxAmt
{
get
{
return _tax;
}
set
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Tax" }));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("TaxAmt");
_tax = value;
_changeTracker.EntityMemberChanged("TaxAmt");
}
else
{
_tax = value;
}
// Recalculate the order total.
CalculateOrderTotal();
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Freight
{
get
{
return _freight;
}
set
{
if (_freight != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Freight" }));
}

// Report the change if the change tracker exists.


if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Freight");
_freight = value;
_changeTracker.EntityMemberChanging("Freight");
}
else
{
_freight = value;
}

// Recalculate the order total.


CalculateOrderTotal();
}
}
}
public decimal TotalDue
{
get
{
return _totalDue;
}
}
[EdmComplexPropertyAttribute()]
public OrderInfo ExtendedInfo
{
get
{
return _extendedInfo;
}
set
{
// For a complex type any changes in the complex type
// properties all get tracked together.
// The change tracker may be null during object materialization.
if (_changeTracker != null)
{
// Since this is a complex property, we need to reset the change
// tracker on the complex type.
if (_extendedInfo != null)
{
// Reset the change tracker.
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", null);
}

// Report the change.


_changeTracker.EntityMemberChanging("ExtendedInfo");
_extendedInfo = value;
_changeTracker.EntityMemberChanged("ExtendedInfo");
}
else
{
_extendedInfo = value;
}

// Reset the change tracker. Complex type property cannot be null.


if (_extendedInfo != null)
{
_extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
}
}
}
private void CalculateOrderTotal()
{
// Update the total due as a sum of the other cost properties.
_totalDue = _subTotal + _tax + _freight;
}
}
// Base class for complex types that implements change tracking.
public abstract class ComplexTypeChangeTracker
{
protected IEntityChangeTracker _complexChangeTracker = null;
private string _rootComplexPropertyName;

// Gets an IEntityChangeTracker to call for properties change.


// You must do this in order to track changes.
virtual public void SetComplexChangeTracker(string rootComplexPropertyName, IEntityChangeTracker
complexChangeTracker)
{
_rootComplexPropertyName = rootComplexPropertyName;
_complexChangeTracker = complexChangeTracker;
}

// Protected method that is called before the change for change tracking
// each of the scalar properties in the complex type.
protected void ReportMemberChanging(string scalarPropertyName)
{
if (null != _complexChangeTracker)
{
_complexChangeTracker.EntityComplexMemberChanging(_rootComplexPropertyName,
this, scalarPropertyName);
}
}

// Protected method that is called after the change for change tracking
// each of the scalar properties in the complex type.
protected void ReportMemberChanged(string scalarPropertyName)
{
if (null != _complexChangeTracker)
{
_complexChangeTracker.EntityComplexMemberChanged(_rootComplexPropertyName,
this, scalarPropertyName);
}
}
}
[EdmComplexTypeAttribute(NamespaceName = "Microsoft.Samples.Edm", Name = "OrderInfo")]
public partial class OrderInfo : ComplexTypeChangeTracker
{
private string _orderNumber;
private string _purchaseOrder;
private string _accountNumber;
private string _comment;

override public void SetComplexChangeTracker(string rootComplexPropertyName, IEntityChangeTracker


changeTracker)
{
// Call SetChangeTracker on the base class to set the change tracker
// and the name of the root complex type property on the entity.
base.SetComplexChangeTracker(rootComplexPropertyName, changeTracker);
}

[EdmScalarPropertyAttribute(IsNullable = false)]
public string OrderNumber
{
get
{
return _orderNumber;
}
set
{
// Validate the value before setting it.
if (value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "OrderNumber", "25" }));
}
// Report the change if the change tracker exists.
if (_complexChangeTracker != null)
{
ReportMemberChanging("OrderNumber");
_orderNumber = value;
ReportMemberChanged("OrderNumber");
}
else
{
_orderNumber = value;
}
}
}
[EdmScalarPropertyAttribute()]
public string PurchaseOrder
{
get
{
return _purchaseOrder;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "PurchaseOrder", "25" }));
}
if (_purchaseOrder != value)
{
// Report the change if the change tracker exists.
if (_complexChangeTracker != null)
{
ReportMemberChanging("PurchaseOrder");
_purchaseOrder = value;
ReportMemberChanged("PurchaseOrder");
}
else
{
_purchaseOrder = value;
}
}
}
}
[EdmScalarPropertyAttribute()]
public string AccountNumber
{
get
{
return _accountNumber;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 15)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "AccountNumber", "15" }));
}
if (_purchaseOrder != value)
{
// Report the change if the change tracker exists.
if (_complexChangeTracker != null)
{
ReportMemberChanging("AccountNumber");
_accountNumber = value;
ReportMemberChanged("AccountNumber");
}
else
{
_accountNumber = value;
}
}
}
}
[EdmScalarPropertyAttribute()]
public string Comment
{
get
{
return _comment;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 128)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "Comment", "128" }));
}
if (_comment != value)
{
// Report the change if the change tracker exists.
if (_complexChangeTracker != null)
{
ReportMemberChanging("Comment");
_comment = value;
ReportMemberChanged("Comment");
}
else
{
_comment = value;
}
}
}
}
}

[EdmEntityTypeAttribute(NamespaceName = "Microsoft.Samples.Edm",
Name = "LineItem")]
public class LineItem : IEntityWithRelationships,
IEntityWithChangeTracker, IEntityWithKey
{
// Define private property variables.
int _lineItemId;
string _trackingNumber;
short _quantity;
int _product;
decimal _price;
decimal _discount;
decimal _total;

IEntityChangeTracker _changeTracker = null;

// Specify the IEntityChangeTracker to use for tracking changes.


void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
{
_changeTracker = changeTracker;
}

EntityKey _entityKey = null;


// Define the EntityKey property for the class.
EntityKey IEntityWithKey.EntityKey
{
get
{
return _entityKey;
}
set
{
// Set the EntityKey property, if it is not set.
// Changing an existing value will cause an exception.
if (_entityKey == null)
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName);
_entityKey = value;
_changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName);
}
else
{
_entityKey = value;
}
}
}
}

RelationshipManager _relationships = null;

// Define a relationship manager for the class.


RelationshipManager IEntityWithRelationships.RelationshipManager
{
get
{
if (null == _relationships)
_relationships = RelationshipManager.Create(this);
return _relationships;
}
}

// Defines a navigation property to the Order class.


[EdmRelationshipNavigationPropertyAttribute("Microsoft.Samples.Edm", "FK_LineItem_Order_OrderId",
"Order")]
public Order Order
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<Order>("FK_LineItem_Order_OrderId", "Order").Value;
}
set
{
((IEntityWithRelationships)(this)).RelationshipManager.
GetRelatedReference<Order>("FK_LineItem_Order_OrderId", "Order").Value = value;
}
}
[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public int LineItemId
{
get
{
return _lineItemId;
}
set
{
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("LineItemId");
_lineItemId = value;
_changeTracker.EntityMemberChanged("LineItemId");
}
else
{
_lineItemId = value;
}
}
}
[EdmScalarPropertyAttribute()]
public string TrackingNumber
{
get
{
return _trackingNumber;
}
set
{
if (_trackingNumber != value)
{
// Validate the value before setting it.
if (value.Length > 25)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] {value,"TrackingNumber", "25"}));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("TrackingNumber");
_trackingNumber = value;
_changeTracker.EntityMemberChanged("TrackingNumber");
}
else
{
_trackingNumber = value;
}
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public short Quantity
{
get
{
return _quantity;
}
set
{
if (_quantity != value)
{
// Validate the value before setting it.
if (value < 1)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Quantity" }));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Quantity");
_quantity = value;
_changeTracker.EntityMemberChanged("Quantity");
}
else
{
_quantity = value;
}

// Update the line total.


CalculateLineTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public int Product
{
get
{
return _product;
}
set
{
// Validate the value before setting it.
if (value < 1)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Product" }));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Product");
_product = value;
_changeTracker.EntityMemberChanged("Product");
}
else
{
_product = value;
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Price
{
get
{
return _price;
}
set
{
if (_price != value)
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Price" }));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Price");
_price = value;
_changeTracker.EntityMemberChanged("Price");
}
else
{
_price = value;
}

// Update the line total.


CalculateLineTotal();
}
}
}
[EdmScalarPropertyAttribute(IsNullable = false)]
public decimal Discount
{
get
{
return _discount;
}
set
{
// Validate the value before setting it.
if (value < 0)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidNegative,
new string[2] { value.ToString(), "Discount" }));
}
// Report the change if the change tracker exists.
if (_changeTracker != null)
{
_changeTracker.EntityMemberChanging("Discount");
_discount = value;
_changeTracker.EntityMemberChanged("Discount");
}
else
{
_discount = value;
}
}
}

public decimal Total


{
get
{
return _total;
}
}
private void CalculateLineTotal()
{
_total = (_quantity * (_price - _discount));
}
}
}

How to: Use Object Services with Custom Objects (Entity Framework)

How to: Use Object Services with Custom Objects (Entity


Framework)
Even if you use custom data classes, you can still take advantage of Object Services functionality provided by data
classes generated by Entity Framework tools. This includes the following functionalities:

 The ability to instantiate an ObjectContext specific to your Entity Data Model (EDM), including predefined

connections.

 Properties that return type-specific ObjectQuery objects.

 Custom methods that add an object to a specific entity set.

The simplest way to take full advantage of Object Services features is to add the generated class that inherits from
ObjectContext to your project. This class is generated by the Entity Framework tools, based on your EDM.

To use generated object code in a C# project

1. From the command prompt, navigate to the location of the conceptual schema definition language (CSDL) file
for your model, and run the following command with the line breaks removed:

Copy Code

%windir%\Microsoft.NET\Framework\v3.5\edmgen.exe
/mode:EntityClassGeneration
/incsdl:.\YourModel.csdl /outobjectlayer:.\YourModel.context.cs
/language:CSharp
2. This regenerates the object layer in C#, based on the specified CSDL file.

3. Open the newly generated code file, copy the class that inherits from ObjectContext, and paste this class into
your custom data class code file.

Note

You can also remove the data classes and add the remaining class to your project.

To use generated object code in a Visual Basic project

1. From the command prompt, navigate to the location of the CSDL file for your model, and run the following
command with the line breaks removed:

Copy Code
%windir%\Microsoft.NET\Framework\v3.5\edmgen.exe
/mode:EntityClassGeneration
/incsdl:.\YourModel.csdl /outobjectlayer:.\YourModel.context.vb
/language:VB
2. This regenerates the object layer in Visual Basic, based on the specified CSDL file.

3. Open the newly generated code file, copy the class that inherits from ObjectContext, and paste this class into
your custom data class code file.

Note

You can also remove the data classes and add the remaining class to your project.

Example

This example shows the generated object context code that supports the Order and LineItem custom data
classes.

Visual Basic
Copy Code

Option Strict Off


Option Explicit On

<Assembly: Global.System.Data.Objects.DataClasses.EdmSchemaAttribute("9f3bb474-6454-4ff1-911e-
4a8be227e97c")>

Namespace Microsoft.Samples.Edm
'''<summary>
'''There are no comments for SalesOrdersEntities in the schema.
'''</summary>
Partial Public Class SalesOrdersEntities
Inherits Global.System.Data.Objects.ObjectContext
'''<summary>
'''Initializes a new SalesOrdersEntities object using the connection string found in the 'SalesOrdersEntities'
section of the application configuration file.
'''</summary>
Public Sub New()
MyBase.New("name=SalesOrdersEntities", "SalesOrdersEntities")
End Sub
'''<summary>
'''Initialize a new SalesOrdersEntities object.
'''</summary>
Public Sub New(ByVal connectionString As String)
MyBase.New(connectionString, "SalesOrdersEntities")
End Sub
'''<summary>
'''Initialize a new SalesOrdersEntities object.
'''</summary>
Public Sub New(ByVal connection As Global.System.Data.EntityClient.EntityConnection)
MyBase.New(connection, "SalesOrdersEntities")
End Sub
'''<summary>
'''There are no comments for LineItem in the schema.
'''</summary>
<Global.System.ComponentModel.BrowsableAttribute(False)> _
Public ReadOnly Property LineItem() As Global.System.Data.Objects.ObjectQuery(Of LineItem)
Get
If (Me._LineItem Is Nothing) Then
Me._LineItem = MyBase.CreateQuery(Of LineItem)("[LineItem]")
End If
Return Me._LineItem
End Get
End Property
Private _LineItem As Global.System.Data.Objects.ObjectQuery(Of LineItem) = Nothing
'''<summary>
'''There are no comments for Order in the schema.
'''</summary>
<Global.System.ComponentModel.BrowsableAttribute(False)> _
Public ReadOnly Property Order() As Global.System.Data.Objects.ObjectQuery(Of Order)
Get
If (Me._Order Is Nothing) Then
Me._Order = MyBase.CreateQuery(Of Order)("[Order]")
End If
Return Me._Order
End Get
End Property
Private _Order As Global.System.Data.Objects.ObjectQuery(Of Order) = Nothing
'''<summary>
'''There are no comments for LineItem in the schema.
'''</summary>
Public Sub AddToLineItem(ByVal lineItem As LineItem)
MyBase.AddObject("LineItem", lineItem)
End Sub
'''<summary>
'''There are no comments for Order in the schema.
'''</summary>
Public Sub AddToOrder(ByVal order As Order)
MyBase.AddObject("Order", order)
End Sub
End Class
End Namespace
C#
Copy Code

[assembly: System.Data.Objects.DataClasses.EdmSchemaAttribute()]

namespace Microsoft.Samples.Edm
{

/// <summary>
/// There are no comments for SalesOrdersEntities in the schema.
/// </summary>
public partial class SalesOrdersEntities : global::System.Data.Objects.ObjectContext
{
/// <summary>
/// Initializes a new SalesOrdersEntities object using the connection string found in the 'SalesOrdersEntities'
section of the application configuration file.
/// </summary>
public SalesOrdersEntities() :
base("name=SalesOrdersEntities", "SalesOrdersEntities")
{
}
/// <summary>
/// Initialize a new SalesOrdersEntities object.
/// </summary>
public SalesOrdersEntities(string connectionString) :
base(connectionString, "SalesOrdersEntities")
{
}
/// <summary>
/// Initialize a new SalesOrdersEntities object.
/// </summary>
public SalesOrdersEntities(global::System.Data.EntityClient.EntityConnection connection) :
base(connection, "SalesOrdersEntities")
{
}
/// <summary>
/// There are no comments for LineItem in the schema.
/// </summary>
[global::System.ComponentModel.BrowsableAttribute(false)]
public global::System.Data.Objects.ObjectQuery<LineItem> LineItem
{
get
{
if ((this._LineItem == null))
{
this._LineItem = base.CreateQuery<LineItem>("[LineItem]");
}
return this._LineItem;
}
}
private global::System.Data.Objects.ObjectQuery<LineItem> _LineItem = null;
/// <summary>
/// There are no comments for Order in the schema.
/// </summary>
[global::System.ComponentModel.BrowsableAttribute(false)]
public global::System.Data.Objects.ObjectQuery<Order> Order
{
get
{
if ((this._Order == null))
{
this._Order = base.CreateQuery<Order>("[Order]");
}
return this._Order;
}
}
private global::System.Data.Objects.ObjectQuery<Order> _Order = null;
/// <summary>
/// There are no comments for LineItem in the schema.
/// </summary>
public void AddToLineItem(LineItem lineItem)
{
base.AddObject("LineItem", lineItem);
}
/// <summary>
/// There are no comments for Order in the schema.
/// </summary>
public void AddToOrder(Order order)
{
base.AddObject("Order", order);
}
}
}
 More...

Security Considerations (Entity Framework)

Describes security considerations for Entity Framework applications.

Security Considerations (Entity Framework)


This topic describes security considerations that are specific to developing, deploying, and running Entity
Framework applications. You should also follow recommendations for creating secure .NET Framework applications.
For more information, see Security Overview (ADO.NET).

General Security Considerations

The following security considerations apply to all applications that use the Entity Framework.

Use only trusted data source providers.


To communicate with the data source, a provider must do the following:

 Receive the connection string from the Entity Framework.

 Translate the command tree to the data source's native query language.

 Assemble and return result sets.

During the logon operation, information that is based on the user password is passed to the server through the
network libraries of the underlying data source. A malicious provider can steal user credentials, generate malicious
queries, or tamper with the result set.

Encrypt your connection to protect sensitive data.


The Entity Framework does not directly handle data encryption. If users access data over a public network, your
application should establish an encrypted connection to the data source to increase security. For more information,
see the security-related documentation for your data source. For a SQL Server data source, see Encrypting
Connections to SQL Server.

Secure the connection string.


Protecting access to your data source is one of the most important goals when securing an application. A
connection string presents a potential vulnerability if it is not secured or if it is improperly constructed. When you
store connection information in plain text or persist it in memory, you risk compromising your entire system. The
following are the recommended methods for securing connection strings:

 Use Windows Authentication with a SQL Server data source.

When you use Windows Authentication to connect to a SQL Server data source, the connection string does
not contain logon and password information.

 Encrypt configuration file sections using protected configuration.


ASP.NET provides a feature called protected configuration that enables you to encrypt sensitive
information in a configuration file. Although primarily designed for ASP.NET, you can also use protected
configuration to encrypt sections of configuration files in Windows applications. For a detailed description
of the new protected configuration capabilities, see Encrypting Configuration Information Using Protected
Configuration.

 Store connection strings in secured configuration files.

You should never embed connection strings in your source code. You can store connection strings in
configuration files, which eliminates the need to embed them in your application's code. By default, the
Entity Data Model Wizard stores connection strings in the application configuration file. You must secure
this file to prevent unauthorized access.

 Use connection string builders when dynamically creating connections.

If you must construct connection strings at runtime, use the EntityConnectionStringBuilder class. This
string builder class helps prevent connection string injection attacks by validating and escaping invalid
input information. For more information, see How to: Build an EntityConnection Connection String (Entity
Framework). Also use the appropriate string builder class to construct the data source connection string
that is part of the Entity Data Model (EDM) connection string. For information about connection string
builders for ADO.NET providers, see Connection String Builders (ADO.NET).

For more information, see Protecting Connection Information (ADO.NET).

Do not expose an EntityConnection to untrusted users.


An EntityConnection object exposes the connection string of the underlying connection. A user with access to an
EntityConnection object can also change the ConnectionState of the underlying connection. The
EntityConnection class is not thread safe.

Do not pass connections outside the security context.


After a connection has been established, you must not pass it outside the security context. For example, one
thread with permission to open a connection should not store the connection in a global location. If the connection
is available in a global location, then another malicious thread can use the open connection without having that
permission explicitly granted to it.

Be aware that logon information and passwords may be visible in a memory dump.
When data source logon and password information is supplied in the connection string, this information is
maintained in memory until garbage collection reclaims the resources. This makes it impossible to determine when
a password string is no longer in memory. If an application crashes, a memory dump file may contain sensitive
security information, and the user running the application and any user with administrative access to the computer
can view the memory dump file. Use Windows Authentication for connections to Microsoft SQL Server.

Grant users only the necessary permissions in the data source.


A data source administrator should grant only the necessary permissions to users. Even though Entity SQL does
not support DML statements that modify data, such as INSERT, UPDATE, or DELETE, users can still access the
connection to the data source. A malicious user could use this connection to execute DML statements in the native
language of the data source.

Run applications with the minimum permissions.


When you allow a managed application to run with full-trust permission, the .NET Framework does not limit the
application's access to your computer. This may enable a security vulnerability in your application to compromise
the entire system. To use code access security and other security mechanisms in the .NET Framework, you should
run applications by using partial-trust permissions and with the minimum set of permissions that are needed to
enable the application to function. The following code access permissions are the minimum permissions your Entity
Framework application needs:

 FileIOPermission: Write to open the specified metadata files or PathDiscovery to search a directory for

metadata files.

 ReflectionPermission: RestrictedMemberAccess to support LINQ to Entities queries.

 DistributedTransactionPermission: Unrestricted to enlist in a System.Transactions Transaction.

 SecurityPermission: SerializationFormatter to serialize exceptions by using the ISerializable interface.

 Permission to open a database connection and execute commands against the database, such as

SqlClientPermission for a SQL Server database.

For more information, see Code Access Security and ADO.NET.

Do not install untrusted applications.


The Entity Framework does not enforce any security permissions and will invoke any user-supplied data object
code in process regardless of whether it is trusted or not. Ensure that authentication and authorization of the client
is performed by the data store and by your application.

Restrict access to all configuration files.


An administrator must restrict write access to all files that specify configuration for an application, including to
enterprisesec.config, security.config, machine.conf, and the application configuration file <application>.exe.config.

The provider invariant name is modifiable in the app.config. The client application must take responsibility for
accessing the underlying provider through the standard provider factory model by using a strong name.

Restrict permissions to the Entity Data Model and mapping files.


An administrator must restrict write access to the model and mapping files (.csdl, .ssdl, and .msl) to only users
who modify the model or mappings. The Entity Framework only requires read access to these files at run time. An
administrator should also restrict access to object layer and pre-compiled view source code files that are generated
by the Entity Data Model tools.

Security Considerations for Queries

The following security considerations apply when querying an EDM. These considerations apply to Entity SQL
queries using EntityClient and to object queries using LINQ, Entity SQL, and query builder methods.

Prevent SQL injection attacks.


Applications frequently take external input (from a user or another external agent) and perform actions based on
that input. Any input that is directly or indirectly derived from the user or an external agent might have content
that uses the syntax of the target language in order to perform unauthorized actions. When the target language is
a Structured Query Language (SQL), such as Transact-SQL, this manipulation is known as a SQL injection attack. A
malicious user can inject commands directly into the query and drop a database table, cause a denial of service, or
otherwise change the nature of the operation being performed.

 Entity SQL injection attacks:


SQL injection attacks can be performed in Entity SQL by supplying malicious input to values that are used
in a query predicate and in parameter names. To avoid the risk of SQL injection, you should never
combine user input with Entity SQL command text.

Entity SQL queries accept parameters everywhere that literals are accepted. You should use
parameterized queries instead of injecting literals from an external agent directly into the query.

 LINQ to Entities injection attacks:

Although query composition is possible in LINQ to Entities, it is performed through the object model
API. Unlike Entity SQL queries, LINQ to Entities queries are not composed by using string manipulation or
concatenation, and they are not susceptible to traditional SQL injection attacks.

Prevent very large result sets.


A very large result set could cause the client system to shut down if the client is performing operations that
consume resources proportional to the size of the result set. Unexpectedly large result sets can occur under the
following conditions:

 In queries against a large database that do not include appropriate filter conditions.

 In queries that create Cartesian joins on the server.

 In nested Entity SQL queries.

When accepting user input, you must make sure that the input cannot cause result sets to become larger than
what the system can handle. You can also use the Take method in LINQ to Entities or the LIMIT operator in Entity
SQL to limit the size of the result set.

Security Considerations for Object Services

The following security considerations apply when generating and working with entity types.

Do not share an ObjectContext across application domains.


Sharing an ObjectContext with more than one application domain may expose information in the connection string.
Instead, you should transfer serialized objects or object graphs to the other application domain and then attach
those objects to an ObjectContext in that application domain. For more information, see Serializing Objects
(Entity Framework).

Prevent type safety violations.


If type safety is violated, Object Services cannot guarantee the integrity of data in objects. Type safety violations
could occur if you allow untrusted applications to run with full-trust code access security.

Handle exceptions in hosted applications.


In a hosted environment, such as ASP.NET, you should access methods and properties of an ObjectContext within
a try-catch block. Catching exceptions prevents unhandled exceptions from exposing entries in the
ObjectStateManager to users of your application.

Security Considerations for ASP.NET Applications

The following should be considered when you work with paths in ASP.NET applications.
Verify whether your host performs path checks.
When the |DataDirectory| (enclosed in pipe symbols) substitution string is used, ADO.NET verifies that the
resolved path is supported. For example, ".." is not allowed behind DataDirectory. That same check for resolving
the Web application root operator (~) is performed by the process hosting ASP.NET. IIS performs this check;
however, hosts other than IIS may not verify that the resolved path is supported. You should know the behavior of
the host on which you deploy an Entity Framework application.

Do not make assumptions about resolved path names.


Although the values to which the root operator (~) and the DataDirectory substitution string resolve should
remain constant during the application's runtime, the Entity Framework does not restrict the host from modifying
these values.

Verify the path length before deployment.


Before deploying an Entity Framework application, you should ensure that the values of the root operator (~) and
DataDirectory substitution string do not exceed the limits of the path length in the operating system. ADO.NET
data providers do not ensure that the path length is within valid limits.

Security Considerations for ADO.NET Metadata

The following security considerations apply when generating and working with EDM model and mapping files.

Do not expose sensitive information through logging.


ADO.NET metadata service components do not log any private information. If there are results that cannot be
returned because of access restrictions, database management systems and file systems should return zero results
instead of raising an exception that could contain sensitive information.

Do not accept MetadataWorkspace objects from untrusted sources.


Applications should not accept instances of the MetadataWorkspace class from untrusted sources. Instead, you
should explicitly construct and populate a workspace from such a source.

Performance Considerations for Entity Framework Applications

Describes performance considerations for Entity Framework applications.

Performance Considerations for Entity Framework Applications


This topic describes performance characteristics of the ADO.NET Entity Framework and provides some
considerations to help improve the performance of Entity Framework applications.

Stages of Query Execution

In order to better understand the performance of queries in the Entity Framework, it is helpful to understand the
operations that occur when a query executes against an Entity Data Model (EDM) and returns data as objects. The
following table describes this series of operations.

Relative
Operation Cost Frequency Comments

Loading Moderate Once in each EDM model and mapping metadata is loaded into a
metadata application domain. MetadataWorkspace. This metadata is cached
globally and is available to other instances of
ObjectContext in the same application domain. For
more information, see Metadata Workspace
Overview.

Opening the Moderate1 As needed. Because an open connection to the database


database consumes a valuable resource, Object Services
connection opens and closes the database connection only as
needed. You can also explicitly open the
connection. For more information, see Managing
Connections in Object Services (Entity Framework).

Generating High Once in each Before the Entity Framework can execute a query
views application domain. against an EDM or save changes to the data
(Can be pre- source, it must generate a set of local query views
generated.) to access the database. Because of the high cost of
generating these views, you can pre-generate the
views and add them to the project at design-time.
For more information, see How to: Pre-Generate
Views to Improve Query Performance (Entity
Framework).

Preparing the Moderate2 Once for each Includes the costs to compose the query command,
query unique query. generate a command tree based on the EDM
metadata, and define the shape of the returned
data. Because Entity SQL query commands are
cached, later executions of the same query take
even less time. You can also use compiled LINQ
queries to reduce this cost in later executions. For
more information, see Compiled Queries (LINQ to
Entities). For general information about LINQ query
execution, see LINQ to Entities Flow of Execution.

Executing the Low2 Once for each The cost of executing the command against the
query query. data source by using the ADO.NET data provider.
Because most data sources cache query plans,
later executions of the same query may take even
less time.

Loading and Low3 Once for each Types are loaded and validated against the types
validating ObjectContext that the EDM defines.
types instance.

Tracking Low3 Once for each An EntityKey is generated for each tracked object
object that a query that the query returns. This key is used to create
returns. 4 an ObjectStateEntry in the ObjectStateManager.
The MergeOption that is used to execute the query
defines the tracking behavior. If the query uses the
NoTracking merge option or if the object already
exists in the ObjectContext and the query uses
the AppendOnly or PreserveChanges merge
options, this stage does not affect performance.
For more information, see Change Tracking and
Identity Resolution (Entity Framework).

Materializing Moderate3 Once for each The process of reading the returned DbDataReader
the objects object that a query object and creating objects and setting property
returns. 4 values that are based on the values in each
instance of the DbDataRecord class. If the object
already exists in the ObjectContext and the query
uses the AppendOnly or PreserveChanges
merge options, this stage does not affect
performance. For more information, see Change
Tracking and Identity Resolution (Entity
Framework).

1
When a data source provider implements connection pooling, the cost of opening a connection is distributed
across the pool. The .NET Provider for SQL Server supports connection pooling.

2
Cost increases with increased query complexity.

3
Total cost increases proportional to the number of objects returned by the query.

4
This overhead is not required for EntityClient queries because EntityClient queries return an EntityDataReader
instead of objects. For more information, see EntityClient Provider for the Entity Framework.

Additional Considerations

The following are other considerations that may affect performance of Entity Framework applications.

Query Execution
Because queries can be resource intensive, consider at what point in your code and on what computer a query is
executed.

Deferred versus immediate execution.


When you create an ObjectQuery or LINQ query, the query may not be executed immediately. Query execution is
deferred until the results are needed, such as during a foreach (C#) or For Each (Visual Basic) enumeration or
when it is assigned to fill a List collection. Query execution begins immediately when you call the Execute method
on an ObjectQuery or when you call a LINQ method that returns a singleton query, such as First or Any. For more
information, see Object Queries (Entity Framework) and Query Execution (LINQ to Entities).

Client-side execution of LINQ queries.


Although the execution of a LINQ query occurs on the computer that hosts the data source, some parts of a LINQ
query may be evaluated on the client computer. For more information, see the Store Execution section of Query
Execution (LINQ to Entities).

Query and Mapping Complexity


The complexity of individual queries and of the mapping in the entity model will have a significant effect on query
performance.

Mapping complexity
Models that are more complex than a simple one-to-one mapping between entities in the conceptual model and
tables in the storage model generate more complex commands than models that have a one-to-one mapping.
Query complexity
Queries that require a large number of joins in the commands that are executed against the data source or that
return a large amount of data may affect performance in the following ways:

 Queries against an EDM that seem simple may result in the execution of more complex queries against the

data source. This can occur because the Entity Framework translates a query against an EDM into an
equivalent query against the data source. When a single entity set in the conceptual model maps to more
than one table in the data source, or when a relationship between entities is mapped to a join table, the
query command executed against the data source query may require one or more joins.

Note

Use the ToTraceString method of the ObjectQuery or EntityCommand classes to view the commands
that are executed against the data source for a given query. For more information, see How to: View
the Store Commands (Entity Framework).

 Nested Entity SQL queries may create joins on the server and can return a large number of rows.

The following is an example of a nested query in a projection clause:

Copy Code

SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c ) As Inner2


FROM AdventureWorksModel.JobCandidate AS c ) As Inner1
FROM AdventureWorksModel.EmployeeDepartmentHistory AS c
In addition, such queries cause the query pipeline to generate a single query with duplication of objects
across nested queries. Because of this, a single column may be duplicated multiple times. On some
databases, including SQL Server, this can cause the TempDB table to grow very large, which can decrease
server performance. Care should be taken when you execute nested queries.

 Any queries that return a large amount of data can cause decreased performance if the client is

performing operations that consume resources in a way that is proportional to the size of the result set. In
such cases, you should consider limiting the amount of data returned by the query. For more information,
see How to: Page Through Query Results (Entity Framework).

Any commands automatically generated by the Entity Framework may be more complex than similar commands
written explicitly by a database developer. If you need explicit control over the commands executed against your
data source, consider defining a mapping to a table-valued function or stored procedure. For more information, see
Stored Procedure Support (Entity Framework).

Relationships
For optimal query performance, you must define relationships between entities both as associations in the entity
model and as logical relationships in the data source.

Query Paths
By default, when you execute an ObjectQuery, related objects are not returned (although objects that represent
the relationships themselves are). You can load related objects in one of two ways: either by setting the query path
before the ObjectQuery is executed, or by calling the Load method on the navigation property that the object
exposes. When you consider which option to use, be aware that there is a tradeoff between the number of requests
against the database and the amount of data returned in a single query. For more information, see Shaping Query
Results (Entity Framework).
Using query paths
Query paths define the graph of objects that a query returns. When you define a query path, only a single request
against the database is required to return all objects that the path defines. Using query paths can result in complex
commands being executed against the data source from seemingly simple object queries. This occurs because one
or more joins are required to return related objects in a single query. This complexity is greater in queries against
a complex entity model, such as an entity with inheritance or a path that includes many-to-many relationships.

Note

Use the ToTraceString method to see the command that will be generated by an ObjectQuery. For
more information, see How to: View the Store Commands (Entity Framework).

When a query path includes too many related objects or the objects contain too much row data, the data source
might be unable to complete the query. This occurs if the query requires intermediate temporary storage that
exceeds the capabilities of the data source. When this occurs, you can reduce the complexity of the data source
query by explicitly loading related objects.

Explicitly loading related objects


You can explicitly load related objects by calling the Load method on a navigation property that returns an
EntityCollection or EntityReference. Explicitly loading objects requires a round-trip to the database every time Load
is called.

Note

if you call Load while looping through a collection of returned objects, such as when you use the
foreach statement (For Each in Visual Basic), the data source-specific provider must support multiple
active results sets on a single connection. For a SQL Server database, you must specify a value of
MultipleActiveResultSets = true in the provider connection string.

Although explicitly loading related objects will reduce the number of joins and reduced the amount of redundant
data, Load requires repeated connections to the database, which can become costly when explicitly loading a large
number of objects.

Saving Changes
When you call the SaveChanges method on an ObjectContext, a separate create, update, or delete command is
generated for every added, updated, or deleted object in the context. These commands are executed on the data
source in a single transaction. As with queries, the performance of create, update, and delete operations depends
on the complexity of the mapping in the EDM.

Distributed Transactions
Operations in an explicit transaction that require resources that are managed by the distributed transaction
coordinator (DTC) will be much more expensive than a similar operation that does not require the DTC. Promotion
to the DTC will occur in the following situations:

 An explicit transaction with an operation against a SQL Server 2000 database or other data source that

always promote explicit transactions to the DTC.

 An explicit transaction with an operation against SQL Server 2005 when the connection is managed by

Object Services. This occurs because SQL Server 2005 promotes to a DTC whenever a connection is
closed and reopened within a single transaction, which is the default behavior of Object Services. This DTC
promotion does not occur when using SQL Server 2008. To avoid this promotion when using SQL Server
2005, you must explicitly open and close the connection within the transaction. For more information, see
Managing Connections in Object Services (Entity Framework).

An explicit transaction is used when one or more operations are executed inside a System.Transactions transaction.
For more information, see Managing Transactions in Object Services (Entity Framework).

Strategies for Improving Performance

You can improve the overall performance of queries in the Entity Framework by using the following strategies.

Pre-generate views.
Generating views based on an entity model is a significant cost the first time that an application executes a query.
Use the EdmGen.exe utility to pre-generate views as a Visual Basic or C# code file that can be added to the project
during design. Pre-generated views are validated at runtime to ensure that they are consistent with the current
version of the specified entity model. For more information, see How to: Pre-Generate Views to Improve Query
Performance (Entity Framework).

Use compiled LINQ queries.


When you have an application that executes structurally similar queries many times in the Entity Framework, you
can frequently increase performance by compiling the query one time and executing it several times with different
parameters. For example, an application might have to retrieve all the customers in a particular city; the city is
specified at runtime by the user in a form. LINQ to Entities supports using compiled queries for this purpose.

For more information, see Compiled Queries (LINQ to Entities).

Return the correct amount of data.


In some scenarios, specifying a query path using the Include method is much faster because it requires fewer
round trips to the database. However, in other scenarios, additional round trips to the database to load related
objects may be faster because the simpler queries with fewer joins result in less redundancy of data. Because of
this, we recommend that you test the performance of various ways to retrieve related objects. For more
information, see Shaping Query Results (Entity Framework).

To avoid returning too much data in a single query, consider paging the results of the query into more manageable
groups. For more information, see How to: Page Through Query Results (Entity Framework).

Limit the scope of the ObjectContext.


In most cases, you should create an ObjectContext instance within a using statement (Using…End Using in Visual
Basic). This can increase performance by ensuring that the resources associated with the object context are
disposed automatically when the code exits the statement block. However, when controls are bound to objects
managed by the object context, the ObjectContext instance should be maintained as long as the binding is
needed and disposed of manually. For more information, see Managing Resources in Object Services (Entity
Framework).

Consider opening the database connection manually.


When your application executes a series of object queries or frequently calls SaveChanges to persist create,
update, and delete operations to the data source, Object Services must continuously open and close the connection
to the data source. In these situations, consider manually opening the connection at the start of these operations
and either closing or disposing of the connection when the operations are complete. For more information, see
Managing Connections in Object Services (Entity Framework).
Consider using the NoTracking merge option for queries.
There is a cost required to track returned objects in the object context. Detecting changes to objects and ensuring
that multiple requests for the same logical entity return the same object instance requires that objects be attached
to an ObjectContext instance. If you do not plan to make updates or deletes to objects and do not require
identity management , consider using the NoTracking merge options when you execute queries.

Implement IEntityWithKey for custom data classes.


Object Services enables you to define your own custom data classes for use with an EDM. Although it is not a
requirement for using custom data classes, implementing the IEntityWithKey interface can help improve query
performance because Object Services uses the EntityKey property that the IEntityWithKey interface defines to
more efficiently retrieve an object from the object state manager. For more information, see Implementing Custom
Data Class Interfaces (Entity Framework).

Performance Data

Some performance data for the Entity Framework is published in the following posts on the ADO.NET team blog:

 Exploring the Performance of the ADO.NET Entity Framework - Part 1

 Exploring the Performance of the ADO.NET Entity Framework – Part 2

 ADO.NET Entity Framework Performance Comparison

Migration Considerations (Entity Framework)

Describes considerations for migrating an existing application to use the Entity Framework.

Migration Considerations (Entity Framework)


The ADO.NET Entity Framework provides several benefits to an existing application. One of the most important of
these benefits is the ability to use the Entity Data Model (EDM) to separate data structures used by the application
from the schema in the data source. This enables you to easily make future changes to the storage model or to the
data source itself without making compensating changes to the application. For more information about the
benefits of using the Entity Framework, see Introducing the Entity Framework.

To take advantage of the benefits of the Entity Framework, you can migrate an existing application to the Entity
Framework. Some tasks are common to all migrated applications. These common tasks include upgrading the
application to use the .NET Framework version 3.5 Service Pack 1 (SP1), defining the EDM, and configuring the
Entity Framework. When you migrate an application to the Entity Framework, there are additional considerations
that apply. These considerations depend on the type of application being migrated and on the specific functionality
of the application. This topic provides information to help you choose the best approach to use when you upgrade
an existing application.

General Migration Considerations

The following considerations apply when you migrate any application to the Entity Framework:

 Any application that uses the .NET Framework 3.5 can be migrated to the Entity Framework, as long as

the data provider for the data source that is used by the application supports the Entity Framework.
 The Entity Framework may not support all the functionality of a data source provider, even if that provider

supports the Entity Framework.

 For a large or complex application, you are not required to migrate the whole application to the Entity

Framework at one time. However, any part of the application that does not use the Entity Framework
must still be changed when the data source changes.

 The data provider connection used by the Entity Framework can be shared with other parts of your

application because the Entity Framework uses ADO.NET data providers to access the data source. For
example, the SqlClient provider is used by the Entity Framework to access a SQL Server database. For
more information, see EntityClient Provider for the Entity Framework.

Common Migration Tasks

The path to migrate an existing application to the Entity Framework depends both on the type of application and on
the existing data access strategy. However, you must always perform the following tasks when you migrate an
existing application to the Entity Framework.

Note

All of these tasks are performed automatically when you use the Entity Data Model tools with Visual
Studio 2008. For more information, see How to: Use the Entity Data Model Wizard (Entity Framework).

1. Upgrade the application.

A project created by using an earlier version of Visual Studio and the .NET Framework must be upgraded to
use Visual Studio 2008 SP1 and the .NET Framework 3.5 SP1. For more information, see Visual Studio
Conversion Wizard.

2. Define the Entity Data Model (EDM).

The EDM defines entities in the conceptual model; structures in the data source, such as tables, stored
procedures, and views; and the mapping between the entities and data source structures. For more
information, see How to: Manually Define an Entity Data Model (Entity Framework).

Types that are defined in the storage model must match the name of objects in the data source. If the existing
application exposes data as objects, you must ensure that the entities and properties that are defined in the
conceptual model match the names of these existing data classes and properties. For more information, see
How to: Customize an Entity Data Model to Work with Custom Objects (Entity Framework).

Note

The Entity Data Model Designer can be used to rename entities in the conceptual model to match
existing objects. For more information, see ADO.NET Entity Data Model Designer Overview.

3. Define the connection string.

The Entity Framework uses a specially formatted connection string when executing queries against an EDM.
This connection string encapsulates information about the EDM mapping files and the connection to the data
source. For more information, see How to: Define the Connection String (Entity Framework).
4. Configure the Visual Studio project.

References to Entity Framework assemblies and the EDM must be added to the Visual Studio project. You can
add these mapping files to the project to ensure that they are deployed with the application in the location that
is indicated in the connection string. For more information, see How to: Manually Configure an Entity
Framework Project.

Considerations for Applications with Existing Objects

When you migrate an application to the Entity Framework, the way that you migrate existing data classes depends
on the business logic, custom methods, and property validation implemented in these classes. You should select
one of the following approaches for working with existing data classes.

 If your data classes only get and set object properties, replace your existing data classes with the entity

types generated by the Entity Data Model tools. For more information, see How to: Use the Entity Data
Model Wizard (Entity Framework).

 If your data classes implement custom validation code or other business logic, migrate this logic into the

partial classes that are generated by the Entity Data Model tools. The Entity Data Model tools generate
entity types as partial classes. This enables you to reuse methods and properties by converting your
existing classes into partial classes. When you do this, you must remove from the existing application any
properties that are duplicated in the generated class. For more information about how to create partial
classes, see Customizing Objects (Entity Framework).

For each property of a data class, the Entity Framework tools generate partial methods named
OnPropertyNameChanging and OnPropertyNameChanged. You can move your existing property
validation code into these partial methods. For more information, see How to: Execute Business Logic
During Property Changes (Entity Framework).

 If you have a significant amount of custom code in your existing data classes or you want to keep your

existing data classes for other reasons, you should do one of the following:

 Modify your data classes to inherit from the EntityObject or the ComplexObject class. All generated

EDM types inherit from EntityObject or ComplexObject, and the Entity Framework enables you to
use custom data classes by inheriting from these base classes. This is the recommended way to use
custom data classes with an EDM. For more information, see Customizing Objects (Entity Framework).

 Modify your data classes to implement a set of interfaces. Do this if your data classes cannot inherit

from EntityObject or ComplexObject. For more information about how to implement these
interfaces, see Customizing Objects (Entity Framework).

Note

When you use existing data classes or partial classes with an EDM, the class names must match the
entity names that are defined in the conceptual model. Use the Entity Designer to rename entities. For
more information, see How to: Create and Modify Entity Types.

Considerations for Applications that Use ADO.NET Providers


ADO.NET providers, such as SqlClient, enable you to query a data source to return tabular data. Data can also be
loaded into an ADO.NET DataSet. The following list describes considerations for upgrading an application that uses
an existing ADO.NET provider:

Displaying tabular data by using a data reader.

You may consider executing an Entity SQL query using the EntityClient provider and enumerating through
the returned EntityDataReader object. Do this only if your application displays tabular data using a data
reader and does not require the facilities provided by Object Services for materializing data into objects,
tracking changes, and making updates. You can continue to use existing data access code that makes
updates to the data source, but you can use the existing connection accessed from the StoreConnection
property of EntityConnection. For more information, see EntityClient Provider for the Entity Framework.

Working with DataSets.

In the Entity Framework, Object Services provides many of the same functionalities provided by DataSet,
including in-memory persistence, change tracking, data binding, and serializing objects as XML data. For
more information, see Object Services Overview (Entity Framework).

If Object Services does not provide the functionality of DataSet needed by your application, you can still
take advantage of the benefits of LINQ queries by using LINQ to DataSet. For more information, see LINQ
to DataSet.

Considerations for Applications that Bind Data to Controls

The .NET Framework lets you encapsulate data in a data source, such as a DataSet or an ASP.NET data source
control, and then bind user interface elements to those data controls. The following list describes considerations for
binding controls to Entity Framework data.

Binding data to controls.

When you query the EDM, Object Services returns the data as objects that are instances of entity types.
These objects can be bound directly to controls, and this binding supports updates. This means that
changes to data in a control, such as a row in a DataGridView, automatically get saved to the database
when the SaveChanges method is called.

If your application enumerates the results of a query to display data in a DataGridView or other type of
control that supports data binding, you can modify your application to bind the control to the result of an
ObjectQuery.

For more information, see Binding Objects to Controls (Entity Framework).

ASP.NET data source controls.

The Entity Framework includes a data source control designed to simplify data binding in ASP.NET Web
applications. For more information, see Entity Framework Data Source Control.

Other Considerations

The following are considerations that may apply when you migrate specific types of applications to the Entity
Framework.

Applications that expose data services.


Web services and applications that are based on the Windows Communication Foundation (WCF) expose
data from an underlying data source by using an XML request/response messaging format. The Entity
Framework supports the serialization of entity objects by using binary, XML, or WCF data contract
serialization. Binary and WCF serialization both support full serialization of object graphs. For more
information, see Web Services and the Entity Data Model (Application Scenarios).

Applications that use XML data.

Object serialization enables you to create Entity Framework data services. These services provide data to
applications that consume XML data, such as AJAX-based Internet applications. In these cases, consider
using ADO.NET Data Services. These data services are based on the EDM and provide dynamic access to
entity data by using standard Representational State Transfer (REST) HTTP actions, such as GET, PUT,
and POST. For more information, see ADO.NET Data Services Framework.

The Entity Framework does not support a native-XML data type. This means that when an entity is
mapped to a table with an XML column, the equivalent entity property for the XML column is a string.
Objects can be disconnected and serialized as XML. For more information, see Serializing Objects (Entity
Framework).

If your application requires the ability to query XML data, you can still take advantage of the benefits of
LINQ queries by using LINQ to XML. For more information, see LINQ to XML.

Applications that maintain state.

ASP.NET Web applications must frequently maintain the state of a Web page or of a user session. Objects
in an ObjectContext instance can be stored in the client view state or in the session state on the server,
and later retrieved and reattached to a new object context. For more information, see Attaching Objects
(Entity Framework).

Deployment Considerations (Entity Framework)

Describes considerations for deploying Entity Framework applications.

Deployment Considerations (Entity Framework)


This topic provides information about deploying applications that use the ADO.NET Entity Framework for data
access. For more information about the Entity Framework, see Getting Started (Entity Framework).

The Entity Framework provides a set of tools that integrate with and make it easier to develop in Visual Studio. For
more information, see Entity Data Model Tools. This topic does not describe how to use specific technologies to
deploy an Entity Framework–based application.

Visual Studio provides facilities for distributing and deploying applications, such as ClickOnce deployment. For more
information, see Deploying Applications and Components in the Visual Studio documentation.

The following considerations apply when you deploy an application that uses the Entity Framework:

 The Entity Framework is a component of the .NET Framework starting with the .NET Framework 3.5

Service Pack 1 (SP1). You must ensure that the .NET Framework 3.5 SP1 or a later version is installed
when deploying an Entity Framework–based application.
 When an Entity Data Model (EDM) is generated by the Entity Data Model Wizard, EDM connection strings

are created in the application configuration file. By default, model and mapping files are deployed as
embedded application resources. Use the Metadata Artifact Processing property of the Entity Designer file
to control whether model and mapping files are deployed as embedded resources. For more information,
see Deployment Tasks with the Entity Designer.

 Ensure that the model and mapping files (.csdl, .ssdl, .msl) are deployed with the application and in the

location specified by the EDM connection string. For more information, see Connection Strings (Entity
Framework).

 When you embed EDM model and mapping files as application resources, you must recompile and

redeploy the application every time the EDM is updated.

 Because the Entity Framework is a component of the .NET Framework, it can be redistributed with your

application as permitted by the .NET Framework license agreement. For more information, see
Redistributing the .NET Framework.

Feature Reference (Entity Framework)


This section provides detailed documentation of the primary features of the ADO.NET Entity Framework.

In This Section

Entity Framework Features

Provides an overview of the Entity Framework features and an architectural diagram.

Entity Framework Features


An Entity Framework application requires creating a conceptual model defining the entities and relationships, a
logical model that represents the underlying relational model, and the mappings between the two. A programmable
object model is then generated from the conceptual model.

The following features and components of the Entity Framework work together to provide an end-to-end
programming environment.

 The Entity Data Model (EDM) is the centerpiece of the Entity Framework. It specifies the design schema,

which is used to build the programmable classes used by application code. Storage structures for
persisting data are represented a storage schema, and a mapping specification connects the design
schema with the storage schema. Conceptual entities can be materialized as objects or can be read in
serialized form using a data reader. Developers can extend these objects as needed to support varying
application needs. For more information, see Entity Data Model.

 The Object Services component enables programmers to work with the common language runtime (CLR)

classes generated from the conceptual model. It also provides infrastructure support for the Entity
Framework, providing services such as state management, change tracking, identity resolution, loading
and navigating relationships, propagation of object changes to database modifications, and query support
for Entity SQL. For more information, see Object Services (Entity Framework).
 LINQ to Entities provides Language-Integrated Query (LINQ) support for querying entities. LINQ to

Entities enables developers to write queries against the database using one of the supported .NET
Framework programming languages such as Visual Basic or Visual C#. For more information, see LINQ to
Entities.

 Entity SQL is a SQL-like storage-independent language, designed to query and manipulate rich object

graphs of objects based on the Entity Data Model (EDM). For more information, see Entity SQL Language.

 The EntityClient provider extends the ADO.NET provider model by accessing data in terms of conceptual

entities and relationships. It executes queries that use Entity SQL. Entity SQL provides the underlying
query language that enables EntityClient to communicate with the database. For more information, see
EntityClient Provider for the Entity Framework.

 The ADO.NET metadata component manages metadata for the design time and runtime needs of the

Entity Framework. All metadata associated with models and mappings are exposed through metadata
interfaces that are independent of the mechanism used for metadata storage. The current storage
mechanism uses file that are based on three XML dialects: conceptual schema definition language (CSDL),
store schema definition language (SSDL), and mapping specification language (MSL). For more
information, see ADO.NET Metadata.

 The Entity Framework includes an evolving set of tools that generate mappings and partial classes that

represent the entities in the conceptual model. For more information, see Entity Data Model Tools.

 The Entity Framework includes an updated SqlClient Data Provider that supports canonical command

trees. For more information, see .NET Framework Data Provider for SQL Server (SqlClient) for the Entity
Framework.

Architectural Diagram

The following diagram shows how the various user accessible programming interfaces relate in the Entity
Framework. A downward arrow indicates a query against the data source, and an upward arrow indicates returned
data. Object Services generates a canonical command tree that represents a LINQ to Entities or Entity SQL
operation against the conceptual model. The EntityClient provider transforms this canonical command tree, based
on the EDM, into a new canonical command tree that is an equivalent operation against the data source.
Entity Data Model

 Data Modeling in the Entity Framework

 EDM Specifications

 Schemas and Mapping Specification (Entity Framework)

 ADO.NET Metadata

Object Services

 Object Services Overview (Entity Framework)

 Querying Data as Objects (Entity Framework)

 Working with Objects (Entity Framework)

 Managing the Object Context (Entity Framework)

 Customizing Objects (Entity Framework)


LINQ to Entities

 LINQ to Entities Overview

 Querying with LINQ to Entities

 Reference (LINQ to Entities)

Entity SQL Language Reference

 Entity SQL Overview

 Entity SQL Reference

Entity Services and Data Providers

 Connection Strings (Entity Framework)

 EntityClient Provider for the Entity Framework

 .NET Framework Data Provider for SQL Server (SqlClient) for the Entity Framework

Related Sections

 Entity Data Model Tools

Samples (Entity Framework)


The ADO.NET Entity Data Model (EDM) includes sample applications and sample data models. See the Data Access
and Storage page for additional sample applications and information.

In This Section

Sample Data Models

 AdventureWorks Complete Model (EDM)

 AdventureWorks Sales Model (EDM)

 School Model (EDM)

 Northwind Model (EDM)

Sample Applications

 Human Resources Skills WinApp (EDM Sample Application)

 Human Resources ASP.NET Web Site (EDM Sample Application)


 Books Authors Web Service (EDM Sample Application)

 Annotation and Research Collaboration Tool (EDM Sample Application)

 Adventure Works Data Binding (EDM Sample Application)

Entity Framework Terminology


This topic defines terms frequently referenced in Entity Framework documentation. Links are provided to relevant
topics where additional information is available.

Term Definition

alias An attribute of the Schema element in CSDL and SSDL schemas that can be
substituted for the full namespace to shorten element references in the schema.

association The definition of a relationship between entity types.


For more information, see Association (EDM).

association set A logical container for instances of associations of the same type.
For more information, see Association Sets (EDM).

base type A type in the Entity Data Model from which one or more derived types inherit
some of their properties.
For more information, see Inheritance (EDM).

command tree A common, programmatic representation of all Entity Framework queries


composed of one or more expressions.
For more information, see Entity Framework Features.

complex type A .NET Framework class that represents a complex property as defined in the
conceptual model. Complex types enable scalar properties to be organized within
entities. Complex objects are instances of complex types. For more information,
see Complex Type Objects (Entity Framework).

ComplexType The specification for a data type that represents a non-scalar property of an
entity type that does not have a key property.
For more information, see Complex Type (EDM).

conceptual model An abstract specification for the entity types, complex types, associations, entity
containers, entity sets, and association sets in the domain of an application built
on an Entity Data Model. The conceptual model is defined in CSDL in the
conceptual model file.
For more information, see Data Modeling in the Entity Framework.
conceptual model An XML file that is the conceptual model, expressed in CSDL. This file has a .csdl
file extension.

conceptual schema An XML-based language that is used to define the entity types, associations,
definition language entity containers, entity sets, and association sets of a conceptual model.
(CSDL) For more information, see Conceptual Schema (CSDL).

constraint Restricts the possible values of a property and makes sure that a value is valid.
For more information, see Type Constraints (EDM).

container A logical grouping of entity and association sets.


For more information, see Entity Containers (EDM).

concurrency A process that allows multiple users to access and change shared data at the
same time. The Entity Framework implements an optimistic concurrency model.
For more information, see Saving Changes and Managing Concurrency (Entity
Framework).

data binding The process or method for configuring controls on a form or Web page to fetch
data from or write data to entity objects.
For more information, see Binding Objects to Controls (Entity Framework).

direction Refers to the asymmetrical nature of some associations. Direction is specified with
FromRole and ToRole attributes of a NavigationProperty or
ReferentialConstraint element in a schema.
For more information, see Entity Data Model Relationships and Association
Element (SSDL).

end A participating entity in an association.


For more information, see Role Attribute (Association CSDL) and Role Attribute
(Association SSDL).

entity A concept in the domain of an application from which a data type is defined.
For more information, see Entities and Relationships (EDM).

EntityClient System.Data.EntityClient is a storage-independent ADO.NET data provider that


contains classes such as EntityConnection, EntityCommand, and
EntityDataReader. Works with Entity SQL and connects to storage specific
ADO.NET data providers, such as SqlClient.
For more information, see EntityClient Provider for the Entity Framework.

entity container Specifies entity sets and association sets that will be implemented in a specified
namespace.
For more information, see Entity Containers (EDM).

Entity Data Model A model that enables application data to be represented as a set of entities and
relationships that are mapped to a defined data source.
(EDM) For more information, see Data Modeling in the Entity Framework.

Entity Data Model An XML file that encapsulates the conceptual model, storage model, and mapping
Designer file that compose an EDM. This file also contains information that is used by the
Entity Data Model Designer. This file has an .edmx extension.
For more information, see ADO.NET Entity Data Model Designer Overview.

Entity Framework A set of technologies that supports development of data-oriented software


applications by enabling developers to work with conceptual models that are
mapped to logical schemas in data sources.
For more information, see Introducing the Entity Framework.

entity set A logical container for entities of a given type and its subtypes. Entity sets are
mapped to tables in a database.
For more information, see Entity Sets (EDM).

Entity SQL A storage-independent dialect of SQL that works directly with conceptual entity
schemas and that supports Entity Data Model features such as inheritance and
relationships.
For more information, see Entity SQL Language.

entity type A .NET Framework class that represents an entity as defined in the conceptual
model. Entity types may have scalar, complex, and navigation properties. Objects
are instances of entity types. For more information, see Object Services Overview
(Entity Framework).

EntityType The specification for a data type that includes a key and a named set of
properties and represents a top-level item in a conceptual model or storage
model.
For more information, see Entity Type (EDM).

key The attribute of an entity type that specifies which property or set of properties is
used to identify unique instances of the entity type. Represented in the object
layer by the EntityKey class.
For more information, see Key Attribute (EntityType CSDL) and Key Attribute
(EntityType SSDL).

LINQ to Entities A query syntax that defines a set of query operators that allow traversal, filter,
and projection operations to be expressed in a direct, declarative way in Visual
C# and Visual Basic.
For more information, see LINQ to Entities.

mapping A specification of the correspondences between items in a conceptual model and


items in a storage model.
For more information, see Mapping Specification (MSL).

mapping file An XML file that is the mapping between the conceptual model and the storage
model, expressed in MSL. This file has a .msl extension.

mapping An XML-based language that is used to map items defined in a conceptual model
specification to items in a storage model.
language (MSL) For more information, see Mapping Specification (MSL).

metadata type A set of classes that enable you to interact with EDM metadata. This type
hierarchy hierarchy provides programmatic support to represent the same concepts
discussed in the EDM Specifications.
For more information, see Metadata Type Hierarchy Overview.

metadata A class that represents the metadata runtime service component that provides
workspace support for retrieving metadata.
For more information, see Metadata Workspace.

modification Stored procedures that are used to insert, update, and delete data that is in the
functions data source. These functions are used in place of Entity Framework generated
commands. Modification functions are defined by the Function element in the
storage model. The ModificationFunctionMapping element maps these
modification functions to insert, update, and delete operations against entities
that are defined in the conceptual model. For more information, see Stored
Procedure Support (Entity Framework).

multiplicity The number of entities that can exist on each side of a relationship, as defined by
an association. Also known as cardinality.
For more information, see Entity Data Model Relationships.

multiple entity sets The ability for an entity type to be defined in more than one entity set.
per type For more information, see Entity Sets (EDM).

navigation A property of an entity type that represents a relationship to another entity type,
property as defined by an association. Navigation properties are used to return related
objects as an EntityCollection or an EntityReference, depending on the multiplicity
at the other end of the association.
For more information, see Navigation Properties (EDM).

query path A string representation of a path that specifies which related objects to return
when an object query is executed. A query path is defined by calling the Include
method on an ObjectQuery.
For more information, see Shaping Query Results (Entity Framework).

object context Represents the entity container defined in the conceptual model. It contains a
connection to the underlying data source and provides services such as change
tracking and identity resolution. An object context is represented by an instance
of the ObjectContext class.
For more information, see Object Services Overview (Entity Framework).

object query A query executed against an EDM within an object context that returns data as
objects.
For more information, see Object Queries (Entity Framework).

object-relational A technique for transforming data from a relational database into data types that
mapping can be used in object-oriented software applications.
Object Services uses the EDM to provide such object-relational behavior by
exposing entities in the conceptual model as entity types in an Entity Framework
application.

For more information, see Object Services Overview (Entity Framework).

Object Services Services provided by the Entity Framework that enable application code to
operate on entities like .NET Framework objects.
For more information, see Object Services Overview (Entity Framework).

referential A constraint that is defined in an EDM that indicates that an entity has a
constraint dependent relationship to another entity. This constraint means that an instance
of a dependent entity cannot exist without a corresponding instance of the
principle entity
For more information, see Referential Constraints (Entity Framework).

relationship A logical connection between entities.


For more information, see Entity Data Model Relationships.

role The name given to each End of an association to clarify the semantics of the
relationship.
For more information, see Role Attribute (Association CSDL) and Role Attribute
(Association SSDL).

scalar property A property of an entity that maps to a single field in the storage model.

simple type A primitive type that is used for defining properties in the conceptual model.
For more information, see Simple Types (EDM).

split entity An entity type that is mapped to two separate types in the storage model.
For more information, see How to: Define a Model with Single Entity Mapped to
Two Tables.

storage model A definition for the logical model of data in a supported data source, such as a
relational database. The storage model is defined in SSDL in the storage model
file.
For more information, see Data Modeling in the Entity Framework.

storage model file An XML file that is the storage model, expressed in SSDL. This file has a .ssdl
extension.

store schema An XML-based language that is used to define the entity types, associations,
definition language entity containers, entity sets, and association sets of a storage model that
(SSDL) frequently corresponds to a database schema.
For more information, see Storage Metadata Schema (SSDL).

table-per- A method of modeling a type hierarchy in a database that includes the attributes
hierarchy of all the types in the hierarchy in one table.
For more information, see How to: Define a Model with Table-per-Hierarchy
Inheritance (Entity Framework).

table-per-type A method of modeling a type hierarchy in a database that uses multiple tables
with one-to-one relationships to model the various types.
For more information, see How to: Define a Model with Table-per-Type
Inheritance (Entity Framework).

You might also like