Sunday, March 17, 2013

WCF - Collection Types in Data Contracts


In .NET, a collection is any type that supports the IEnumerable or IEnumerable<T> interface.

All of the built-in collections in .NET, such as the array, the list, and the stack,support these interfaces.

 A data contract can include a collection as a data member,and a service contract can define operations that interact with a collection directly.

Whenever you define a service operation that uses the collection interfaces IEnumerable<T>, IList<T>, or ICollection<T>, the resulting metadata always uses an array.

Collection type in .net

http://msdn.microsoft.com/en-us/library/system.collections(v=vs.80).aspx

 

Collection Types in WCF Example :

Service Interface  :


using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace WCF_DataContract_CollectionType
{
  [ServiceContract]
    public interface IService1
    {

        [OperationContract] //
        IEnumerable<Item>  Getorder(); 

        [OperationContract]
        List<Item> GetorderList();


        [OperationContract]
        ICollection<Item> GetorderICollection();

       [OperationContract]
        void add(Item i);
     
    }


  public class Item
  {
      public int itemid { get; set; }
      public string ItemName { get; set; }
  }
    
}

 

Service

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Xml;
using System.IO;

namespace WCF_DataContract_CollectionType
{
    public class Service1 : IService1
    {

        List<Item> objlst = new List<Item>();
        Item _obj2;
     // This method add data to list
         public void AddData()
        {
          
           _obj2 = new Item();
             _obj2.itemid = 3001;
            _obj2.ItemName = "Mobiles";
            objlst.Add(_obj2);

            _obj2 = new Item();
            _obj2.itemid = 3002;
            _obj2.ItemName = "LCD";
            objlst.Add(_obj2);
         
        }


        public void add(Item i)
        {
           
            //we can use this method to add data into database table
            //we can access Properties of Item class like this
            // i.itemid.ToString()   ----//90001
            //i.ItemName.ToString() -----/ Client side
      
        }

        public IEnumerable<Item>  Getorder()
        {
            AddData();  // this function add data to list and returnlist 
            return objlst;          
        }

        public ICollection<Item> GetorderICollection()
        {
            AddData();  
            return objlst;          
        }

        public List<Item> GetorderList()
        {
            AddData();
            return objlst;

        }
   
    }
}



Client side

using System;
using System.Collections;
using System.Linq;
using System.Text;

using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
           SC1.Service1Client obj = new SC1.Service1Client();
           SC1.Item _obj2;
           _obj2 = new SC1.Item();
           _obj2.itemid = 90001;
           _obj2.ItemName = "ClientSide";
           obj.add(_obj2);

                
           Console.WriteLine(" ");
           Console.WriteLine("******  IEnumerable Method called ********** ");
           Console.WriteLine(" ");
           SC1.Item[] itemd = obj.Getorder();
           for (Int32 l = 0; l < itemd.Length; l++)
           {
               Console.WriteLine("Item {0}  ItemName {1}", itemd[l].itemid, itemd[l].ItemName);
           }
                   
         Console.WriteLine(" ");
         Console.WriteLine("******  List Method called ********** ");
         Console.WriteLine(" ");
         List<SC1.Item> itemlist = obj.GetorderList().ToList();
         for (Int32 l = 0; l < itemlist.Count; l++)
           {
               Console.WriteLine("Item {0}  ItemName {1}", itemd[l].itemid, itemd[l].ItemName);
           }

         Console.WriteLine(" ");
         Console.WriteLine("******  ICollection Method called ********** ");
         Console.WriteLine(" ");
         SC1.Item[]  itemIcoll = obj.GetorderICollection();
         for (Int32 l = 0; l < itemlist.Count; l++)
         {
             Console.WriteLine("Item {0}  ItemName {1}", itemd[l].itemid, itemd[l].ItemName);
         }

           Console.ReadLine();
         
        }
  
    }
}




 

OutPut

******  IEnumerable Method called **********

Item 3001  ItemName Mobiles

Item 3002  ItemName LCD

******  List Method called **********

Item 3001  ItemName Mobiles

Item 3002  ItemName LCD

******  ICollection Method called **********

Item 3001  ItemName Mobiles

Item 3002  ItemName LCD

Note:

When we add service reference than click on advance it will open Service reference settings where collection type by  default System.Array.

 

 

 

Click object browser and you can see that array has been returned in metadata instead of collection as shown in fig below. In this case we need to cast  to Tolist() List<SC1.Item> itemlist = obj.GetorderList().ToList();
Otherwise complietime error will generated
Error     1Cannot implicitly convert type 'Client.SC1.Item[]' to 'System.Collections.Generic.List<Client.SC1.Item>'            C:\Users\vikas.kumar\Desktop\WCF Experiments\WCF-DataContract-CollectionType\Client\Program.cs        36         36            Client

 

Solution is we can call getorderlist() method as below because we are returning collection from client side but we are getting array on client side

SC1.Item[]  itemlist = obj.GetorderList();
  

 

 

Return List instead of array

Right click on Configure service reference it will open Service reference settings window wwhere u can use generic list in array collection.

 

 

With this change method calling  

List<SC1.Item> itemlist = obj.GetorderList(); will work fine.
But other methods SC1.Item[] itemd = obj.Getorder();  will give error message like

Error   1          Cannot implicitly convert type 'System.Collections.Generic.List<Client.SC1.Item>' to 'Client.SC1.Item[]'     C:\Users\vikas.kumar\Desktop\WCF Experiments\WCF-DataContract-CollectionType\Client\Program.cs  32        31        Client

Solution is either change return type 
List<SC1.Item> itemd = obj.Getorder();
Or
use casting
SC1.Item[] itemd = obj.Getorder().ToArray() ;

 

Custom collection in WCF

 

Custom collection must have 

  1. Add method 
  2. Implement IEnumerable and IEnumerable<T> 
  3. Custom collection is serlializable 

 

Custom collection class

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace WCF_CustomCollection
{
    [Serializable]
    public class CustomClass<T> : IEnumerable<T>
    {
        private List<T> myList = new List<T>();

        public void AddRecord(T c)
        {
            myList.Add(c);
        }

  
        // IEnumerable<T> extends IEnumerable interface, therefore
        //the compiler expects you to implement two versions of the GetEnumerator() method
        // we need to implement both versions of GetEnumerator().
       
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return myList.GetEnumerator();
        }

        System.Collections.IEnumerator IEnumerable.GetEnumerator()
        {
            return myList.GetEnumerator();
        }

    }

}

 

Interface :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCF_CustomCollection
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        CustomClass<Item> Getorder();      

        [OperationContract]
        void AddRecord(Item i);
    }
   
    //item Class
    public class Item
    {
        public int itemid { get; set; }
        public string ItemName { get; set; }
    }    
     
}

Service  :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCF_CustomCollection
{
    public class Service1 : IService1
    {

        CustomClass<Item> objlst = new CustomClass<Item>();       
        Item _obj2;
        public void AddData()
        {
            _obj2 = new Item();
            _obj2.itemid = 3001;
            _obj2.ItemName = "Mobiles";
            objlst.AddRecord(_obj2);
            _obj2 = new Item();
            _obj2.itemid = 3002;
            _obj2.ItemName = "LCD";
            objlst.AddRecord(_obj2);
        }


        public void AddRecord(Item i)
        {
           
        }

     
        public CustomClass<Item> Getorder()
        {   
             AddData();
            return objlst;
        }
           
    }
}

Client side

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClientConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            SC1.Service1Client obj = new SC1.Service1Client();               
            Console.WriteLine(" ");
            Console.WriteLine("******  IEnumerable Method called ********** ");
            Console.WriteLine(" ");
            SC1.Item[] itemd = obj.Getorder().myList.ToArray() ;
            for (Int32 l = 0; l < itemd.Length; l++)
            {
                Console.WriteLine("Item {0}  ItemName {1}", itemd[l].itemid, itemd[l].ItemName);
            }

             Console.WriteLine(" ");
            Console.WriteLine("******  List Method called ********** ");
            Console.WriteLine(" ");
            List<SC1.Item> itemlist = obj.Getorder().myList;
            for (Int32 l = 0; l < itemlist.Count; l++)
            {
                Console.WriteLine("Item {0}  ItemName {1}", itemd[l].itemid, itemd[l].ItemName);
            }

            Console.ReadLine();
        }
    }
}

 

output :

same as above