Blog

LINQ to SQL and WCF

This is the scenario… We already have our LINQ DataContext and we need to receive/send info through a WCF service, what can we do to accomplish this?
Option A. - Convert our LINQ entities to data contracts, to do this we just need to modify the Serialization Mode property from None to Unidirectional, right click in any area in the dbml and select properties.
Doing this our LINQ objects are going to have the attributes [DataContract()] and [DataMember] as show in the figure 2. A few questions come to my mind (for sure there are a couple of more and for sure not all the options showed here are going to cover all the cases, hopefully a good part of)… What if we don’t need/want to send all the information in our LINQ objects through the WCF service? Or What if the Service was developed in other department/company and we already have defined the DataContracts?
 
 
 
[Table(Name="dbo.Employees")]
 [DataContract()]
 public partial class Employee : INotifyPropertyChanging, INotifyPropertyChanged
 {
[Column(Storage="_EmployeeID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
  [DataMember(Order=1)]
  public int EmployeeID
  {
   get
   {
    return this._EmployeeID;
   }
   set
   {
    if ((this._EmployeeID != value))
    {
     this.OnEmployeeIDChanging(value);
     this.SendPropertyChanging();
     this._EmployeeID = value;
     this.SendPropertyChanged("EmployeeID");
     this.OnEmployeeIDChanged();
    }
   }
  }
Figure 2
Our Contracts….
   [DataContract]
    public class EmployeeContract : BaseContract
    {
        [DataMember]
        public int EmployeeID { get; set; }
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
    }
    [DataContract]
    public class OrderContract : BaseContract
    {
        [DataMember]
        public int OrderID { get; set; }
        [DataMember]
        public string CustomerID { get; set; }
        [DataMember]
        public EmployeeContract Employee { get; set; }
    }
 
 
Option B. - Create a method and manually map DataContract and LINQ objects. In this case an extension method was created.
public static Order ToLinqEntity(this OrderContract contract)
        {
            Order order = new Order()
            {
                OrderID = contract.OrderID,
                CustomerID = contract.CustomerID
            };
            if (null != contract.Employee)
            {
                order.Employee = new Employee()
                {
                    EmployeeID = contract.Employee.EmployeeID,
                    FirstName = contract.Employee.FirstName,
                    LastName = contract.Employee.LastName
                };
            }
            return order;
        }
Option C. - Use Reflection to map DataContract and LINQ objects.
        public static void ToLinqEntity<T>(this BaseContract contract, T entity) where T : class
        {
            if (null == entity)
                throw new ArgumentNullException();
            PropertyInfo[] contractProperties = contract.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            PropertyInfo[] entityProperties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
            for (var i = 0; i <= contractProperties.Length - 1; i++)
            {
                //search for the contract property in the property collection of the entity
                PropertyInfo entityPI = (from e in entityProperties
                                         where e.Name == contractProperties[i].Name
                                         select e).FirstOrDefault();
                if (null != entityPI)
                {                   
                    //if the property is a linq association we need to search for the corresponding values
                    if (null != (from ca in entityPI.GetCustomAttributes(true)
                                 where ca.GetType() == typeof(System.Data.Linq.Mapping.AssociationAttribute)
                                 select ca).FirstOrDefault())
                    {
                        //get the value from the contract
                        var contractClass = (BaseContract)contractProperties[i].GetValue(contract, null);
                        //if value isn't null procede to asign to the linq entity
                        if (null != contractClass)
                        {
                            var entityClass = entityPI.GetValue(entity, null);
                            //if class in linq entity==null we need to call the constructor to create a new one
                            if (null == entityClass)
                            {
                                ConstructorInfo ci = entityPI.PropertyType.GetConstructor(new Type[0]);
                                entityClass = ci.Invoke(null);
                            }
                            //recursive call to fill out the new class
                            contractClass.ToLinqEntity(entityClass);
                            //asign value of the new class to the linq entity
                            entityPI.SetValue(entity, entityClass, null);
                        }
                    }
                    else // not a class, we just set the value
                    {
                        entityPI.SetValue(entity, contractProperties[i].GetValue(contract, null), null);
                    }
                } // end null != entityPI
            }//end for contractProperties
        }
 
 

Comments

On 27 Oct 2009 09:57, Justin Broyles said:

Great post!!

Leave a comment

 
 
 
 
CAPTCHA Image Validation