Tuesday, September 10, 2013

WCF Data Services with Reflection Provider

Today let’s see how to implement a WCF Data Service with Reflection Provider. The reflection provider exposes data in classes which returns an IQueryable<T>

I am going to use the same scenario which I used to demonstrate WCF Data Services with Entity Framework Provider.

Scenario

I have three entities which are “Employee”, “Department” and “JobRole”. The relationships among these three entities are as follows.
  • Employee can only belong to one Department and one Department can have many Employees.
  • Employee can have many JobRoles and one JobRole can have many Employees.
Let me start off by creating a WCF Service application.

image
Creating a WCF Service Application

I am deleting the service and it’s interface which was created initially when creating the project. I am going to add latest WCF Data Services references through Nuget. I am searching online for “WCF Data Services Server” and installing it.

Untitled
Adding references through Nuget

Once all the needed references are installed, I am going to create a class which I am going to name as “MyDataContext”.

I am going to create three classes for each of my entity types.
public class Department
{
    public int DepartmentId { get; set; }
    public string DepartmentName { get; set; }
}

public class JobRole
{
    public int JobRoleId { get; set; }
    public string JobRoleName { get; set; }
}

public class Employee
{
    public int EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Department Departments { get; set; }
    public List<JobRole> JobRoles { get; set; }
}
OK, now you can see I have three classes. In the “Employee” class, I have two properties along with basic properties which are “Departments” and “JobRoles”. By having “Departments” property which is of type “Department”, I am mentioning the department a particular employee belong to. By having list of “JobRoles” which is of type “List<JobRole>”, I am mentioning the job roles a particular employee can have.

Now I need to have primary key fielda for each of these entity types. Of course, I have three properties which are “DepartmentId”,”JobRoleId” and “EmployeeId” which I can use as primary key fields. So for that I am going to decorate the class with the attribute DataServiceKeyAttribute.

So now I am going to create a method in each of these classes, which will return some data I want to expose to outside.
[DataServiceKeyAttribute("DepartmentId")]
public class Department
{
    public int DepartmentId { get; set; }
    public string DepartmentName { get; set; }

    public static List<Department> GetDepartments()
    {
        List<Department> deptList = new List<Department>();
        deptList.Add(new Department() { DepartmentId = 1, DepartmentName = "Information Technology" });
        deptList.Add(new Department() { DepartmentId = 2, DepartmentName = "Finance" });
        deptList.Add(new Department() { DepartmentId = 2, DepartmentName = "Human Resources" });
        return deptList;
    }
}

[DataServiceKeyAttribute("JobRoleId")]
public class JobRole
{
    public int JobRoleId { get; set; }
    public string JobRoleName { get; set; }

    public static List<JobRole> GetJobRoles()
    {
        List<JobRole> jobRoleList = new List<JobRole>();
        jobRoleList.Add(new JobRole() { JobRoleId = 1, JobRoleName = "Software Engineer" });
        jobRoleList.Add(new JobRole() { JobRoleId = 2, JobRoleName = "Project Manager" });
        jobRoleList.Add(new JobRole() { JobRoleId = 3, JobRoleName = "Accountant" });
        jobRoleList.Add(new JobRole() { JobRoleId = 4, JobRoleName = "HR Executive" });
        return jobRoleList;
    }
}
 
[DataServiceKeyAttribute("EmployeeId")]
public class Employee
{
    public int EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Department Departments { get; set; }
    public List<JobRole> JobRoles { get; set; }

    public static List<Employee> GetEmployees()
    {
        List<Employee> empList = new List<Employee>();
        empList.Add(new Employee()
        {
            EmployeeId = 1,
            FirstName = "Jaliya",
            LastName = "Udagedara",
            Departments = Department.GetDepartments().First(dept => dept.DepartmentName == "Information Technology"),
            JobRoles = new List<JobRole>() 
            { 
                JobRole.GetJobRoles().First(jobRole => jobRole.JobRoleName == "Software Engineer"),
                JobRole.GetJobRoles().First(jobRole => jobRole.JobRoleName == "Project Manager") 
            }
        });
        empList.Add(new Employee()
        {
            EmployeeId = 2,
            FirstName = "John",
            LastName = "Doe",
            Departments = Department.GetDepartments().First(dept => dept.DepartmentName == "Finance"),
            JobRoles = new List<JobRole>() 
            { 
                JobRole.GetJobRoles().First(jobRole => jobRole.JobRoleName == "Accountant") 
            }
        });
        empList.Add(new Employee()
        {
            EmployeeId = 3,
            FirstName = "Jane",
            LastName = "Doe",
            Departments = Department.GetDepartments().First(dept => dept.DepartmentName == "Human Resources"),
            JobRoles = new List<JobRole>() 
            { 
                JobRole.GetJobRoles().First(jobRole => jobRole.JobRoleName == "HR Executive") 
            }
        });
        return empList;
    }
}

Once it is done, I have some entities and some methods which return some data. Now I am going to create my model or the context that I am going to expose through a WCF Data Service.

So I am modifying my “MyDataContext” class as follows.
public class MyDataContext
{
    public IQueryable<Employee> Employees
    {
        get
        {
            return Employee.GetEmployees().AsQueryable();
        }
    }

    public IQueryable<Department> Departments
    {
        get
        {
            return Department.GetDepartments().AsQueryable();
        }
    }

    public IQueryable<JobRole> JobRoles
    {
        get
        {
            return JobRole.GetJobRoles().AsQueryable();
        }
    }
}

I have added three Properties which is of IQueryable<T>. You might be wondering why specially IQueryable<T>, don’t worry about it now, we will discuss about it later.

Now I am almost done. Let’s add a WCF Data Service to the project and put “EntitySetRights.AllRead” as the entity access rule for all the entities, so consumers can read all entities.
using System.Data.Services;
using System.Data.Services.Common; 

namespace WCFDataServicesWithReflectionProvider
{
    public class WcfDataService1 : DataService<MyDataContext>
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(DataServiceConfiguration config)
        {
            // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
            // Examples:
            config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
            // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
        }
    }
}

Now when I hit F5, I can see the WCF Data Service up and running.

image
WCF Data Service
When you view the metadata of this service by typing,
http://domain/service.svc/$metadata
you can see the following.

image
metadata

Now I am going to write the theoretical things related to Reflection Provider. The reason for me to didn’t mention these in the beginning and to mention now is, the best way to understand theory is, comparing it with the practical output.

Of course I will be copy and pasting the things from the MSDN, so you might will bored. But when you contrast them with WCF Data Service we just created, you will find them all interesting.

When you create the data service, the provider infers the data model by using reflection. The following list shows how the reflection provider infers the data model.
  • Entity container - the class that exposes the data as properties that return an IQueryable<T> instance.
    • Here it’s our class “MyDataContext”
  • Entity sets - properties that return IQueryable<T> instances are treated as entity sets.
    • Here it’s our properties in the “MyDataContext” class which are “Employees”, “Departments” and “JobRoles”. The reason to create properties that return IQueryable<T> instances are, basically it is a must. If you return IEnumerable<T>, you will not be able to see those entity sets in the WCF Data Service. Here is a nice post which describes IQueryable<T> vs. IEnumerable<T>
  • Entity types - the type T of the IQueryable<T> that the entity set returns.
    • Here it’s our entity classes which are “Employee”, “Department” and “JobRole”
  • Entity keys - each data class that is an entity type must have a key property. This property is attributed with the DataServiceKeyAttribute attribute ([DataServiceKeyAttribute]).
    • Here it’s “EmployeeId”, “DepartmentId” and “JobRoleId”.
  • Entity type properties - other than the entity key, the reflection provider treats the accessible, non-indexer properties of a class that is an entity type as follows:
    • Here it’s the properties of our entity classes.
    • If the property returns a primitive type, then the property is assumed to be a property of an entity type.
    • If the property returns a type that is also an entity type, then the property is assumed to be a navigation property that represents the "one" end of a many-to-one or one-to-one relationship.
      • Departments property of Employee class
    • If the property returns an IEnumerable<T> of an entity type, then the property is assumed to be a navigation property that represents the "many" end of a one-to-many or many-to-many relationship.
      • JobRoles property of Employee
    • If the return type of the property is a value type, then the property represents a complex type.
So that’s it. I am uploading the sample to my SkyDrive, Enjoy WCF Data Services with Reflection Provider.



Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment