Click to Rate and Give Feedback
MSDN
MSDN Library
Visual Studio 2008
Visual Studio
Accessing Data
ADO.NET
 Data Model
Data Model (ADO.NET Data Services Framework)

ADO.NET Data Services natively support ADO.NET Entity Framework models and provide an extension by which a data model can be defined using common language runtime (CLR) objects. Terminology of the Entity Data Model (EDM), specifically conceptual schema definition language (CSDL), and terms such as EntitySet, EntityType, Property, Association, AssociationSet, and so forth are used to describe data deployed by the ADO.NET Data Services Framework. For definitions of these terms, see ADO.NET Data Services Framework Terminology. For more information about CSDL, see EDM Specifications.

By reducing data sources to a single grammar and uniform URI conventions, ADO.NET Data Services can deploy consistent representations of data regardless of the underlying data source. When an Entity Framework model is used, setting up a data service is trivial. For CLR-based models, a mapping between CLR objects and EDM types has been defined.

EDM Data Model

In the Entity Data Model (EDM), ADO.NET Data Services interact with the Object Services layer of the Entity Framework, which is a CLR programmable context. Although the programming context and conceptual model can be crafted manually, the recommended approach is to use the Entity Data Model Tools integrated with Microsoft Visual Studio 2008 beginning with SP1.

For an example that uses the EDM and Entity Framework tools, see Data Service Quick Start (ADO.NET Data Services Framework).

CLR-based Data Model

ADO.NET Data Services work by converting URI requests to operations on the data that is addressed by the URI syntax. When the data model is based on the Entity Framework, URIs are converted to Object Services method calls. The Entity Framework ObjectContext deploys data sets based on ObjectQuery<T>, and ObjectQuery<T> implements IQueryable<T>.

Any technology or data provider that deploys its data by implementing a derivative of IQueryable <T> can be deployed as an ADO.NET data service. The AsQueryable extension method, available beginning with the .NET Framework 3.5, can be used with objects that implement IEnumerable<T>. All classes in the .NET Framework that implement IEnumerable<T> can be extended by calling the AsQueryable extension method. This means that most lists, arrays, and collections can be efficiently deployed as ADO.NET Data Services.

LINQ queries also work with data sources that implement IEnumerable<T> or IQueryable<T>. When the underlying data model for an ADO.NET Data Service is defined using CLR objects, request URIs are converted to LINQ queries. A complete mapping is defined between CLR objects and ADO.NET Data Services resources. The mapping of CLR objects to ADO.NET entity sets enables ADO.NET Data Services to deploy any data source that can be read into memory as an array, list, or collection.

Example 1: CLR Classes to ADO.NET Data Service Resources

The following example shows the mapping between CLR constructs and ADO.NET Data Services resources. Classes implementing the IQueryable<T> interface are represented as entity sets.

The following example uses the AsQueryable extension method to convert an array of Customers to the IQueryable<T> format. The array of Customers is here simply constructed, but application data could be read from almost any source.

The code example is annotated to indicate how CLR types map to ADO.NET Data Services resources types.

namespace Accounting  // Namespace
{
    public class DataModel  // EntityContainer
    {
        public IQueryable<Customer> Customers  // EntitySet
        {
            get
            {
                return new Customer[] { new Customer(1, "name1"),
                               new Customer(2,
                              "name2") }.AsQueryable<Customer>();
            }
        }
    }

    public class Customer  // EntityType
    {
        private int _ID;
        private string _name;
        private Address _address;

        public Customer(int i, string name)
        {
            _ID = i;
            _name = name;
            _address.Line1 = "Line" + i;
            _address.Line2 = "Line" + i;
        }

        [DataWebKeyAttribute]
        public int ID  // Entity Type Key
        {
            get { return _ID; }
        }

        public string CustName   // Property
        {
            get { return _name; }
        }

        public Address Address   // Property
        {
            get { return _address; }
        }

    }


    public struct Address   // ComplexType
    {
        private string line1;
        private string line2;

        public string Line1   // Property
        {
            get { return this.line1; }
            set { this.line1 = value; }
        }

        public string Line2   // Property
        {
            get { return this.line2; }
            set { this.line2 = value; }
        }
    }
}

When mapping a CLR entity to an ADO.NET Data Services resource, the casing of the CLR name is copied by the associated ADO.NET Data Services resource. The CLR types that correspond to ADO.NET Data Services resources, as indicated by the comments in the previous code, are described in the following list.

Entity Containers and Entity Sets

  • A single public CLR class (C1) within an explicitly defined namespace is used to define all the top-level entity sets in the model. Top-level resources are accessible using the first path segment in a URI.

  • The namespace in which the class C1 exists is the namespace that identifies C1 as the entity container. This is constant even if C1 is a derived type.

  • The name of the class C1 represents the entity container. A single entity container can be defined for the namespace. This is constant even if C1 is a derived type.

  • Each entity set must be represented as a public property (P1) of the class C1 with a return type of IQueryable<T>. Zero or more such properties may exist on the class C1 or, if the class is a derived type, on one of its parent classes.

    • T represents the entity type within the entity set.

    • T must have a property suitable to be an entity key in order for property C1 to be considered an entity set. If no such suitable key property exists, property P1 is skipped and C1 is not considered to represent an entity set by the ADO.NET Data Services data service.

    • The same type T cannot be returned by more than one property on class C1 or a parent class of C1. This model definition based on the CLR does not support including the same entity type, in this case represented by class T, in multiple entity sets. Multiple entity sets per type can be implemented in the Entity Framework but not as classes deployed by ADO.NET Data Services.

Entity Types, Properties, and Navigation Links

  • A public CLR class (C1) represents an entity type.

  • To be recognized as an entity type, a class must have a property or properties that represent the key of the type. Such a property or group of properties becomes the key(s) for the entity type. The rules for properties that represent a key are:

    • Public property named ID.

    • Public property named <className>ID.

    • Public properties marked with the attribute DataWebKeyAttribute attribute.

    • If a class contains a property or group of properties that are marked with the DataWebKeyAttribute, those properties are used as the key, and the first two rules are ignored. If no properties contain the attribute then those properties matching the first two rules determine the key property of the entity type. If more than one property matches the rules, the enity type has, by definition, a composite key.

  • If the class C1 is part of a hierarchy, the class hierarchy is translated to an entity type hierarchy by applying the following rules:

    • The CLR class nearest to the root of the class hierarchy that includes a valid key property becomes the root of the entity type hierarchy. If class C1 is not the root of the CLR class hierarchy in which it participates, then the properties declared by classes above C1 in the hierarchy are assumed to be declared by C1.

  • Each property (P) declared by the class C1 is translated to a property on the entity type if the CLR property conforms to all of the following conventions:

    • The CLR property must have public scope.

    • The CLR property must implement a get method for the property.

    • The CLR property must not be an indexer.

    • If the return type of property P is a primitive type, and the type corresponds to an EDM type, the property must be represented as a property. See ADO.NET Data Services Content Types for a mapping of EDM types to CLR types.

    • If the return type of property P is a reference type, and the type or one of its parent objects, if it is a derived type, represents an entity type, then P represents a (1-1) navigation link.

    • If the return type of property P is IEnumerable<T> and T represents an entity type, then P represents a one-to-many navigation link.

    • If the return type of property P is a value type, then P represents a complex type.

Complex Types

  • A public CLR value type (V1) represents a complex type.

  • Each property of the value type V1 is translated to a property of a complex type. Complex types follow rules similar to entity types to determine if a CLR property will be mapped to a property.

    • The CLR property must have public scope.

    • The CLR property must implement a Get method for the property.

    • The CLR property must not be an indexer.

    • If the return type of property P is a reference type, and the type or one of its parent objects, if it is a derived type, represents an entity type, P represents a one-to-one navigation link.

Example 2: CLR Classes to ADO.NET Data Service Resources

The following example shows CLR classes that include inheritance used to implement ADO.NET Data Services resources types.

namespace Accounting
{
    public class DataModel
    {
        public IQueryable<Customer> Customers
        {
            get
            {
                return new Customer[] { new Customer(1, "name1"),
                    new Customer(2, "name2") }.AsQueryable<Customer>();
            }
        }

        public IQueryable<Order> Orders
        {
            get
            {
                return new Order[] { new Order(1, "order1"),
                   new Order(2, "order2") }.AsQueryable<Order>();
            }
        }
    }


    public class DerivedDataModel : DataModel
    {
        public IQueryable<HumanResources.Employee> Employees
        {
            get { return new HumanResources.Employee[] { new 
                        HumanResources.Employee(1, "EmpName1"), new 
                        HumanResources.Employee(2, "EmpName2") 
                         }.AsQueryable<HumanResources.Employee>(); 
               } 
        }
    }

    public class Person
    {
        protected int _ID;
        public Person() { }
        public Person(int i)
        {
            _ID = i;
        }

        [DataWebKeyAttribute]
        public int ID
        {
            get { return _ID; }
        }
    }

    public class Customer : Person
    {
        private string _name;
        private Address _address;
        List<Order> _orders;

        public Customer(int i, string name)
        {
            _ID = i;
            _name = name;
            _orders = new List<Order>();
            if (i == 1) { _orders.Add(new Order(1, "order1")); }
            if (i == 2) { _orders.Add(new Order(2, "order2")); }
            _address.Line1 = "Line" + i;
            _address.Line2 = "Line" + i;
        }

        public string CustName
        {
            get { return _name; }
        }

        public Address Address
        {
            get { return _address; }
        }

        public IList<Order> Orders
        {
            get { return _orders; }
        }
    }

    public class Order
    {
        private int _ID;
        private string _name;

        public Order(int i, string name)
        {
            _ID = i;
            _name = name;
        }

        [DataWebKeyAttribute]
        public int OrderKey
        {
            get { return _ID; }
        }

        public string OrderName
        {
            get { return _name; }
        }
    }

    public struct Address
    {
        private string line1;
        private string line2;

        public string Line1
        {
            get { return this.line1; }
            set { this.line1 = value; }
        }

        public string Line2
        {
            get { return this.line2; }
            set { this.line2 = value; }
        }
    }
}

namespace HumanResources
{
    public class Employee
    {
        private int _ID;
        private string _name;

        public Employee(int i, string name)
        {
            _ID = i;
            _name = name;
        }

        public int ID
        {
            get { return _ID; }
        }

        public string EmpName
        {
            get { return _name; }
        }
    }
}

Write/Update Support

To enable create, update, and delete support on a CLR data model, the class that models the top-level entity sets must implement the IUpdatable interface. In reference to Example 2, CLR Objects to ADO.NET Data Services Framework resources above, the Accounting.DataModel class would be required to implement the IUpdatable interface.

See Also

Community Content   What is Community Content?
Add new content RSS  Annotations
error in code example      Nostromo   |   Edit   |  
the member OrderKeyID of OrderKey class (and other class) are
decorate with the wrong DataWebKeyAttribute instead of

DataServiceKeyAttribute

Tags What's this?: Add a tag
Flag as ContentBug
Processing
© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker