Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Part 13 - ExtensionDataObject in WCF

Suggested Videos
Part 10 - Message Contract in WCF
Part 11 - Difference between datacontract and messagecontract in wcf
Part 12 - Backward compatible WCF contract changes



In this video we will discuss the use of ExtensionDataObject. We will be working with the same example that we discussed in Part 12. Please watch Part 12 before proceeding.



Let us understand the use of ExtensionDataObject with an example. For some reason we want to remove Gender property from Employee DataContract. Notice that we have commented wherever Gender property is referenced.
[DataContract(Namespace = "http://pragimtech.com/Employee")]
public class Employee
{
    [DataMember(Order = 1)]
    public int Id { get; set; }

    [DataMember(Order = 2)]
    public string Name { get; set; }

    //[DataMember(Order = 3)]
    //public string Gender { get; set; }

    [DataMember(Order = 4)]
    public DateTime DateOfBirth { get; set; }

    [DataMember(Order = 5)]
    public EmployeeType Type { get; set; }

    [DataMember(Order = 6)]
    public string City { get; set; }
}

Since we have removed Gender property property from Employee DataContract, we also need to change EmployeeService.cs file as shown below. Changes are in Red colour.
public class EmployeeService : IEmployeeService
{
public Employee GetEmployee(int Id)
{
    Employee employee = null;
    string cs = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
    using (SqlConnection con = new SqlConnection(cs))
    {
        SqlCommand cmd = new SqlCommand("spGetEmployee", con);
        cmd.CommandType = CommandType.StoredProcedure;
        SqlParameter parameterId = new SqlParameter();
        parameterId.ParameterName = "@Id";
        parameterId.Value = Id;
        cmd.Parameters.Add(parameterId);
        con.Open();
        SqlDataReader reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            if ((EmployeeType)reader["EmployeeType"] == EmployeeType.FullTimeEmployee)
            {
                employee = new FullTimeEmployee
                {
                    Id = Convert.ToInt32(reader["Id"]),
                    Name = reader["Name"].ToString(),
                    //Gender = reader["Gender"].ToString(),
                    DateOfBirth = Convert.ToDateTime(reader["DateOfBirth"]),
                    Type = EmployeeType.FullTimeEmployee,
                    AnnualSalary = Convert.ToInt32(reader["AnnualSalary"]),
                    City = reader["City"].ToString()
                };
            }
            else
            {
                employee = new PartTimeEmployee
                {
                    Id = Convert.ToInt32(reader["Id"]),
                    Name = reader["Name"].ToString(),
                    //Gender = reader["Gender"].ToString(),
                    DateOfBirth = Convert.ToDateTime(reader["DateOfBirth"]),
                    Type = EmployeeType.PartTimeEmployee,
                    HourlyPay = Convert.ToInt32(reader["HourlyPay"]),
                    HoursWorked = Convert.ToInt32(reader["HoursWorked"]),
                    City = reader["City"].ToString()
                };
            }
        }
    }
    return employee;
}

public void SaveEmployee(Employee employee)
{
    string cs = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
    using (SqlConnection con = new SqlConnection(cs))
    {
        SqlCommand cmd = new SqlCommand("spSaveEmployee", con);
        cmd.CommandType = CommandType.StoredProcedure;
        SqlParameter parameterId = new SqlParameter
        {
            ParameterName = "@Id",
            Value = employee.Id
        };
        cmd.Parameters.Add(parameterId);

        SqlParameter parameterName = new SqlParameter
        {
            ParameterName = "@Name",
            Value = employee.Name
        };
        cmd.Parameters.Add(parameterName);

        //SqlParameter parameterGender = new SqlParameter
        //{
        //    ParameterName = "@Gender",
        //    Value = employee.Gender
        //};
        //cmd.Parameters.Add(parameterGender);

        SqlParameter parameterCity = new SqlParameter
        {
            ParameterName = "@City",
            Value = employee.City
        };
        cmd.Parameters.Add(parameterCity);
                
        SqlParameter parameterDateOfBirth = new SqlParameter
        {
            ParameterName = "@DateOfBirth",
            Value = employee.DateOfBirth
        };
        cmd.Parameters.Add(parameterDateOfBirth);

        SqlParameter parameterEmployeeType = new SqlParameter
        {
            ParameterName = "@EmployeeType",
            Value = employee.Type
        };
        cmd.Parameters.Add(parameterEmployeeType);

        if (employee.GetType() == typeof(FullTimeEmployee))
        {
            SqlParameter parameterAnnualSalary = new SqlParameter
            {
                ParameterName = "@AnnualSalary",
                Value = ((FullTimeEmployee)employee).AnnualSalary
            };
            cmd.Parameters.Add(parameterAnnualSalary);
        }
        else
        {
            SqlParameter parameterHourlyPay = new SqlParameter
            {
                ParameterName = "@HourlyPay",
                Value = ((PartTimeEmployee)employee).HourlyPay,
            };
            cmd.Parameters.Add(parameterHourlyPay);

            SqlParameter parameterHoursWorked = new SqlParameter
            {
                ParameterName = "@HoursWorked",
                Value = ((PartTimeEmployee)employee).HoursWorked
            };
            cmd.Parameters.Add(parameterHoursWorked);
        }

        con.Open();
        cmd.ExecuteNonQuery();
    }
}
}

We also need to change spGetEmployee and spSaveEmployee procedures to remove Gender.
Alter procedure spGetEmployee        
@Id int        
as        
Begin        
 Select Id, Name, DateOfBirth, EmployeeType, 
        AnnualSalary, HourlyPay, HoursWorked, City    
 from   tblEmployee where Id = @Id        
End  

Alter procedure spSaveEmployee        
@Id int,        
@Name nvarchar(50),        
@Gender nvarchar(50) = null,        
@DateOfBirth DateTime,      
@EmployeeType int,      
@AnnualSalary int = null,      
@HourlyPay int = null,      
@HoursWorked int = null,  
@City nvarchar(50) = null   
as        
Begin        
 Insert into tblEmployee        
 values (@Id, @Name, @Gender, @DateOfBirth,       
         @EmployeeType, @AnnualSalary, @HourlyPay, 
         @HoursWorked, @City)        
End

At this point run the service. Run the client application. The client application still references Gender property. This is because the client proxy classes were generated before we have modified the service. So, when we Save an employee, we also also passing value for Gender property. This property value is unknown to the service and by default the value will be lost at the service.

Try to get the same employee and notice that, Gender property value is lost. Let's say we want to preserve unknown elements that are sent by client and then return them back to the client. To achieve this the Employee DataContract has to implement IExtensibleDataObject interface. This interface has one property called ExtensionData that we need to implement.

Modify Employee DataContract in Employee.cs file as shown below. Changes in Red colour.
[DataContract(Namespace = "http://pragimtech.com/Employee")]
//Implement IExtensibleDataObject interface
public class Employee : IExtensibleDataObject
{
    [DataMember(Order = 1)]
    public int Id { get; set; }

    [DataMember(Order = 2)]
    public string Name { get; set; }

    //[DataMember(Order = 3)]
    //public string Gender { get; set; }

    [DataMember(Order = 4)]
    public DateTime DateOfBirth { get; set; }

    [DataMember(Order = 5)]
    public EmployeeType Type { get; set; }

    [DataMember(Order = 6)]
    public string City { get; set; }

    public ExtensionDataObject ExtensionData { get; set; }
}

Modify EmployeeService in EmployeeService.cs file as shown below.
// First change
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class EmployeeService : IEmployeeService
{
    // Second change
    private Employee _lastSavedEmployee;
        
    public Employee GetEmployee(int Id)
    {
        Employee employee = null;
        string cs = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
        using (SqlConnection con = new SqlConnection(cs))
        {
            SqlCommand cmd = new SqlCommand("spGetEmployee", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter parameterId = new SqlParameter();
            parameterId.ParameterName = "@Id";
            parameterId.Value = Id;
            cmd.Parameters.Add(parameterId);
            con.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                if ((EmployeeType)reader["EmployeeType"] == EmployeeType.FullTimeEmployee)
                {
                    employee = new FullTimeEmployee
                    {
                        Id = Convert.ToInt32(reader["Id"]),
                        Name = reader["Name"].ToString(),
                        //Gender = reader["Gender"].ToString(),
                        DateOfBirth = Convert.ToDateTime(reader["DateOfBirth"]),
                        Type = EmployeeType.FullTimeEmployee,
                        AnnualSalary = Convert.ToInt32(reader["AnnualSalary"]),
                        City = reader["City"].ToString()
                    };
                }
                else
                {
                    employee = new PartTimeEmployee
                    {
                        Id = Convert.ToInt32(reader["Id"]),
                        Name = reader["Name"].ToString(),
                        //Gender = reader["Gender"].ToString(),
                        DateOfBirth = Convert.ToDateTime(reader["DateOfBirth"]),
                        Type = EmployeeType.PartTimeEmployee,
                        HourlyPay = Convert.ToInt32(reader["HourlyPay"]),
                        HoursWorked = Convert.ToInt32(reader["HoursWorked"]),
                        City = reader["City"].ToString()
                    };
                }
            }
        }
        // Third change
        if (_lastSavedEmployee != null && Id == _lastSavedEmployee.Id)
        {
            employee.ExtensionData = _lastSavedEmployee.ExtensionData;
        }
        return employee;
    }

    public void SaveEmployee(Employee employee)
    {
        // Fourth change
        _lastSavedEmployee = employee;
        string cs = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
        using (SqlConnection con = new SqlConnection(cs))
        {
            SqlCommand cmd = new SqlCommand("spSaveEmployee", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter parameterId = new SqlParameter
            {
                ParameterName = "@Id",
                Value = employee.Id
            };
            cmd.Parameters.Add(parameterId);

            SqlParameter parameterName = new SqlParameter
            {
                ParameterName = "@Name",
                Value = employee.Name
            };
            cmd.Parameters.Add(parameterName);

            //SqlParameter parameterGender = new SqlParameter
            //{
            //    ParameterName = "@Gender",
            //    Value = employee.Gender
            //};
            //cmd.Parameters.Add(parameterGender);

            SqlParameter parameterCity = new SqlParameter
            {
                ParameterName = "@City",
                Value = employee.City
            };
            cmd.Parameters.Add(parameterCity);
                
            SqlParameter parameterDateOfBirth = new SqlParameter
            {
                ParameterName = "@DateOfBirth",
                Value = employee.DateOfBirth
            };
            cmd.Parameters.Add(parameterDateOfBirth);

            SqlParameter parameterEmployeeType = new SqlParameter
            {
                ParameterName = "@EmployeeType",
                Value = employee.Type
            };
            cmd.Parameters.Add(parameterEmployeeType);

            if (employee.GetType() == typeof(FullTimeEmployee))
            {
                SqlParameter parameterAnnualSalary = new SqlParameter
                {
                    ParameterName = "@AnnualSalary",
                    Value = ((FullTimeEmployee)employee).AnnualSalary
                };
                cmd.Parameters.Add(parameterAnnualSalary);
            }
            else
            {
                SqlParameter parameterHourlyPay = new SqlParameter
                {
                    ParameterName = "@HourlyPay",
                    Value = ((PartTimeEmployee)employee).HourlyPay,
                };
                cmd.Parameters.Add(parameterHourlyPay);

                SqlParameter parameterHoursWorked = new SqlParameter
                {
                    ParameterName = "@HoursWorked",
                    Value = ((PartTimeEmployee)employee).HoursWorked
                };
                cmd.Parameters.Add(parameterHoursWorked);
            }

            con.Open();
            cmd.ExecuteNonQuery();
        }
    }
}

In short, use IExtensibleDataObject to preserve unkown elements during serialization and deserialization of DataContracts. On the service side, at the time of deserialization the unknown elements from the client are stored in ExtensionDataObject. To send data to the client, the service has to serialize data into XML. During this serialization process the data from ExtensionDataObject is serialized into XML as it was provided at the time of service call.

wcf tutorial

1 comment:

  1. Hi Sir,

    The code is working good as expected.I have a question for you.
    Why do we need ServiceBehavior attribute? In which way this attribute is linked with the code?

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

    ReplyDelete

It would be great if you can help share these free resources