вторник, 15 января 2013 г.

WCF DataServices - расширение клиентской модели


   При разработке многоуровневых  приложений с использование WCF + OData сервисов зачастую возникает необходимость расширить сущностные классы на клиенте, т.е. добавить свойство, которого нет в серверной EDMX модели.
   Например вот так:
public partial class Enterprise : global::System.ComponentModel.INotifyPropertyChanged
{
   public string NewProperty
   {
      get
      {
            …
      }
   }
}
   Однако, добавив новое свойство, мы получаем ошибку времени выполнения связанную с невозможностью сериализации вновь добавленного члена класса. Дело в том, что при сериализации и отправке запроса на сервер выполняется сверка XML описания объектов, соответственно, т.к. свойство добавлено только на клиенте генерируется исключение. Решение проблемы - пометить свойство как не подлежащее сериализации. Для этого потребуется создать атрибут, которым будем помечать свойства, расширяющие клиентскую модель и необходимо реализовать метод который должен быть подписан на событие сериализации сущности (DataServiceContext.WritingEntity). Задачей метода является удаление свойств, помеченных атрибутом запрета сериализации,  из XML схемы описания объекта. Синтаксис метода ниже:
void DataServiceContextEx_WritingEntity
      (object sender, ReadingWritingEntityEventArgs e)
 {
   // e.Data - сериализуемый элемент
xnEntityProperties = XName.Get("properties"
                    ,e.Data.GetNamespaceOfPrefix("m").NamespaceName);
XElement xePayload = null;
foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
   {
       object[] doNotSerializeAttributes =    
                property.GetCustomAttributes(typeof(DoNotSerializeAttribute), false); 
       if (doNotSerializeAttributes.Length > 0)
       {
         if (xePayload == null)
         {
               xePayload = e.Data
                            .Descendants()
                            .Where<XElement>(xe => xe.Name == xnEntityProperties)
                            .First<XElement>();
         } 
         XName xnProperty = XName.Get(property.Name,                     
                                      e.Data.GetNamespaceOfPrefix("d").NamespaceName);
         foreach (XElement xeRemoveThisProperty in 
                        xePayload.Descendants(xnProperty).ToList())
         {
            xeRemoveThisProperty.Remove();
         }
       }
    }
  }

Ну а синтаксис атрибута совсем простой:
[
AttributeUsage(AttributeTargets.Property)]
public class DoNotSerializeAttribute : Attribute
{
}
Вот и всё!