Browse DevX
Sign up for e-mail newsletters from DevX


ADO.NET vNext Part 2: Using the Object Services Layer

The Entity Data Model and ADO.NET vNext let you deal with tabular data as objects, eliminating much of the effort endemic to older data-retrieval and modification code.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

he first installment of this three-part article series introduced the core concepts of ADO.NET vNext, including the Entity Data Model (EDM), Entity SQL, and the Mapping Provider. It also discussed the steps involved in executing entity SQL queries against the EDM and retrieving output in the form of rows and values (contained in a DbDataReader object). This installment builds on Part 1, focusing on a layer named the Object Services Layer that lets you work with database tables and columns as objects, and properties. The object services layer is an abstraction on top of EDM that leverages the same EDM infrastructure to execute queries against a database.

The object services layer provides a strongly typed object abstraction, letting you work with the database layer in terms of programming language objects rather than only the tabular "rows-and-columns" model thereby handling the impedance mismatch problem. The object services layer provides a cache of objects and the ability to perform CRUD operations on these objects. There are two ways to execute queries through the object services layer—via LINQ or Entity SQL. This installment focuses on query execution using Entity SQL and the next installment discusses using of LINQ for query execution in detail.

To follow along you'll need to have Visual Studio Professional, and you'll need to download and install these three items: As you can see in Figure 1, the object service layer leverages the combination of EDM and mapping provider and exposes a set of strongly typed objects to the client application.

Figure 1. ADO.NET Object Services Layer: The Object Services layer allows each client application to have an object representation of the entities in the EDM.
As Figure 1 shows, the object services layer integrates seamlessly with the rest of the stack, providing developers with the choice of exposing entities as .NET objects. In addition, the object services layer also handles object identity resolution, change tracking, and persistence.

Retrieving Data as Objects
In Part 1 of this series, you saw the code required to retrieve data in the form of a DbDataReader object (as rows and columns). One shortcoming of this approach is its late bound nature, which is not only error-prone but also difficult to program with. The object services layer lets you shift away from late binding to an early bound implementation where you can work with database entities as traditional .NET objects.

Here's an example that uses the object services layer to retrieve data from the ProductSubcategory entity in EDM.

private void btnGetCategories_Click(object sender, EventArgs e) { string connectionString = Properties.Settings.Default.AdventureWorksConnectionString; ObjectContext context = new ObjectContext(connectionString, "AdventureWorksModel.AdventureWorks"); Query<ProductSubcategory> categories = context.GetQuery

( "SELECT VALUE p FROM AdventureWorksModel.AdventureWorks." + "ProductSubcategory AS p "); lstCategories.Items.Clear(); foreach (ProductSubcategory category in categories) { lstCategories.Items.Add(category.Name); } }

The preceding code begins by retrieving a connection string from the custom settings originally stored using the property pages of the project (Settings tab). These custom settings are stored in the app.config file and available at runtime.

For the purposes of this example I created a setting named AdventureWorksConnectionString in the Property pages. Visual Studio adds strongly typed properties to a Settings class created by the designer so that you can access those settings programmatically with code like this:

string connectionString = Properties.Settings.Default.AdventureWorksConnectionString;

Next, to execute queries using the object services layer, you need to create an instance of the ObjectContext object that acts as the entry point to the object services layer.

ObjectContext context = new ObjectContext(connectionString, "AdventureWorksModel.AdventureWorks");

Author's Note: The above code uses Entity SQL with the object services layer to retrieve data, but it's equally possible to use LINQ queries in conjunction with object services layer. I'll cover that in the next installment.

The results of the execution is a Query<Type> object, which is returned as an output from the call to ObjectContext.GetQuery().

Query<ProductSubcategory> categories = context.GetQuery<ProductSubcategory>( "SELECT VALUE p FROM AdventureWorksModel.AdventureWorks." + "ProductSubcategory AS p ");

Note that the object services layer uses the same map connection (pointing to the same EDM schema and mappings) as well as the same query language; however, it now returns the results in object form. The query result is an object of type ProductSubcategory—a type generated during the creation of the EDM. For more information on the creation of the EDM, see Part 1 of this article series. When it creates the entity data model, the entity data model wizard also generates an object representation for each table belonging to entity data model. If you open up the AdventureWorksModel.Model.cs file (which contains all the generated classes), you will find the ProductSubcategory class declared as follows:

[System.Data.Objects.EntityTypeAttribute (SchemaName="AdventureWorksModel", TypeName="ProductSubcategory")] public partial class ProductSubcategory : global::System.Data.Objects.Entity { public ProductSubcategory() { if ((((object)(this)).GetType() == typeof(global::AdventureWorksModel.ProductSubcategory))) { this.DoFinalConstruction(); } } public ProductSubcategory(int productSubcategoryID, string name, global::System.Guid rowguid, global::System.DateTime modifiedDate) { this.ProductSubcategoryID = productSubcategoryID; this.Name = name; this.rowguid = rowguid; this.ModifiedDate = modifiedDate; if ((((object)(this)).GetType() == typeof(global::AdventureWorksModel.ProductSubcategory))) { this.DoFinalConstruction(); } } [System.Data.Objects.ScalarFieldAttribute()] [System.Data.Objects.NullableConstraintAttribute(false)] public int ProductSubcategoryID { get{return this._ProductSubcategoryID; } set { ----- } } private int _ProductSubcategoryID;

Figure 2. Output Generated by Executing Queries through the Object Services Layer: The ListBox displays the names of all the products sub categories contained in the AdventureWorks database.
---- ---- }
Note that ProductSubcategory is a partial class that makes it possible for you to add custom business logic in a separate class file without having to modify the tool-generated class. For the complete code of the ProductSubcategory class, please refer to the downloadable code for this article.

After retrieving the collection of ProductSubcategory objects, you can then loop through the collection to display the results in the ListBox, as shown in Figure 2.

foreach (ProductSubcategory category in categories) { lstCategories.Items.Add(category.Name); }

Note the ease with which you can retrieve the values from the collection, once you have the results available in a strongly typed object.

Thanks for your registration, follow us on our social networks to keep up-to-date