Использование сущностей LINQ в службах WCF

Технология LINQ для SQL сама по себе представляет большой интерес, поскольку позволяет проверять схему базы данных и запросы к базе данных на этапе компиляции, избавиться от поддержки хранимых процедур в базе данных и соответствия кода слоя логики хранимым процедурам в базе данных, в результате – сохранить время уменьшив объем ручной работы. В свою очередь технология WCF позволяет создавать службы, описывая лишь данные и контракты самих служб, не вдаваясь в детали реализации того или иного протокола передачи данных и сериализации объектов, позволяя декларативно в конфигурационном файле менять адреса и протоколы, используемые службой. Таким образом, WCF позволяет сократить время разработки и уменьшить объем кода, который должен написать разработчик.

При попытке использовать сущности LINQ в качестве контрактов передаваемых данных, приходится писать дополнительный код. В этой статье представлен один из способов связать сущности LINQ и службы WCF.

Для начала представим себе стандартную архитектуру WCF службы, состоящую из слоя данных (сборка, содержащая сущности LINQ), собственно службы (сборка, содержащая код службы) и хоста службы (пусть это будет сборка, содержащая консольное приложение). Для того, чтобы сгенерировать набор сущностей LINQ, достаточно добавить .dbml файл в проект слоя данных, открыть его в дизайнере и перетащить на него таблицы из Server Explorer. В этой статье в качестве примера будет использована база данных Northwind.

После этого сущности LINQ уже можно использовать, например, так:

NorthwindDataContext dc = new ColiseumDataContext();
var product = dc.Products.Single(p => p.ProductID == productId);

 

Однако, для того, чтобы использовать эти сущности в WCF, необходимо пометить соответствующие классы и свойства атрибутами DataContract и DataMember, соответственно. Для этого существует специальный параметр в свойствах дизайнера сущностей LINQ: Serialization. Чтобы установить этот атрибут, можно щелкнуть по дизайнеру, открыть свойства (обычно F4, либо щелчок правой кнопкой мыши и пункт Properties) и установить свойство Serialization = Unidirectional. В результате, будут генерироваться классы, уже размеченные необходимыми атрибутами, например:

    [Table(Name="dbo.[Order Details]")]

    [DataContract()]

    public partial class Order_Detail : INotifyPropertyChanging, INotifyPropertyChanged
    {
    
        [Column(Storage="_OrderID", DbType="Int NOT NULL", IsPrimaryKey=true)]
        [DataMember(Order=1)]
        public int OrderID
        {
            get
            {
                return this._OrderID;
            }
            set
            {
                if ((this._OrderID != value))
                {
                    if (this._Order.HasLoadedOrAssignedValue)
                    {
                        throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
                    }
                    this.OnOrderIDChanging(value);
                    this.SendPropertyChanging();
                    this._OrderID = value;
                    this.SendPropertyChanged("OrderID");
                    this.OnOrderIDChanged();
                }
            }
        }

и т.д. ……………………………

 

Теперь эти объекты можно использовать в WCF службах. Например, создадим простую службу, которая работает с объектами Orders. Для этого определим интерфейс (контракт) службы и, собственно службу:

[ServiceContract]
public interface IMobileService
{
[OperationContract]
Order GetOrder(int orderId);
[OperationContract]
void UpdateOrder(Order obj);
}

public class MobileService : IMobileService
{
public Order GetOrder(int orderId)
{
return OrdersMapper.GetOrder(orderId);
}
public void UpdateOrder(Order obj)
{
OrdersMapper.UpdateOrder(obj);
}
 

Однако, если с получением данных на стороне клиента проблем нет, то с обновлением на стороне сервиса возникает сложность, поскольку десериализованный на сервере объект не связан с контекстом данных LINQ. В результате, все-таки придется написать немного кода для метода UpdateOrder:

public static class OrdersMapper

{
public static Order GetOrder(int orderId)
{
return DataUtility.Context.Orders.Single(o => o.OrderID == orderId);
}

public static void UpdateOrder(Order obj)
{
var dc = DataUtility.Context;
Order oldOrder = dc.Orders.Single(o => o.OrderID == obj.OrderID);
oldOrder.CustomerID = obj.CustomerID;
oldOrder.EmployeeID = obj.EmployeeID;
oldOrder.Freight = obj.Freight;

foreach (Order_Detail od in oldOrder.Order_Details)
{
dc.Order_Details.Remove(od);
}

oldOrder.Order_Details.Clear();

foreach (Order_Detail od in obj.Order_Details)
{
Order_Detail nod = new Order_Detail();
nod.Discount = od.Discount;
nod.OrderID = od.OrderID;
nod.ProductID = od.ProductID;
nod.Quantity = od.Quantity;
nod.UnitPrice = od.UnitPrice;
oldOrder.Order_Details.Add(nod);
}

oldOrder.OrderDate = obj.OrderDate;
oldOrder.RequiredDate = obj.RequiredDate;
oldOrder.ShipAddress = obj.ShipAddress;
oldOrder.ShipCity = obj.ShipCity;
oldOrder.ShipCountry = obj.ShipCountry;
oldOrder.ShipName = obj.ShipName;
oldOrder.ShippedDate = obj.ShippedDate;
oldOrder.ShipPostalCode = obj.ShipPostalCode;
oldOrder.ShipRegion = obj.ShipRegion;
oldOrder.ShipVia = obj.ShipVia;
dc.SubmitChanges();
}
}

 

Как видно из приведенного примера, необходимо заново связать данные с сущностью LINQ, более того, при наличии связей (как в связке Order – Order_Details), необходимо также обработать и их. Например, в примере выше список Order_Details полностью пересоздается, чтобы не отслеживать изменения в каждом объекте.

Собственно все. Очевидно, что пока использование сущностей LINQ и WCF сопряжено с некоторыми сложностями, однако, не так часто необходимо передавать сущности из базы данных удаленным клиентам, поэтому может быть выбрано решение с использованием дополнительных классов-контрактов данных для WCF.

Источник: Gaidar Magdanurov



Опубликовал admin
12 Ноя, Понедельник 2007г.



Программирование для чайников.