Здравствуйте, samius, Вы писали:
>Здесь меня немного пугает обилие кода, связанным с поддержкой сообщений об изменении DTO объектов и довольно плотной подпиской на все сообщения ClientContext-ом.
Есть предположение, что можно написать вот такой базовый класс для DTO
public class DataTransferObjectBase<TLinqToSqlObject>
{
protected T GetValue<T>(Expression<Func<TLinqToSqlObject, T>> expression)
{
//TODO: implement this
throw new NotImplementedException();
}
protected void SetValue<T>(Expression<Func<TLinqToSqlObject, T>> expression, T value)
{
//TODO: implement this
throw new NotImplementedException();
}
protected IEnumerable<T> GetValue<T>(
Expression<Func<TLinqToSqlObject, EntitySet<T>>> expression) where T : class
{
//TODO: implement this
throw new NotImplementedException();
}
}
пример конкретного DTO
public class OrderDTO : DataTransferObjectBase<Order>
{
public DateTime? OrderDate
{
get
{
return GetValue(x => x.OrderDate);
}
set
{
SetValue(x => x.OrderDate, value);
}
}
public Customer Customer
{
get
{
return GetValue(x => x.Customer);
}
set
{
SetValue(x => x.Customer, value);
}
}
public IEnumerable<OrderDetail> OrderDetails
{
get
{
return GetValue(x => x.OrderDetails);
}
}
}
Сообщения об изменениях, на которые подписывается ClientContext, будут реализованы в базовом классе DataTransferObjectBase. Помимо этого в базовом классе реализуется логика управления связями между DTO объектами, например, OrderDTO.Customer, OrderDTO.OrderDetails.
Второй вариант -- сделать DTO автогенеренными.
>Накопление истории изменений и применение ее на сервере тоже может оказаться громоздким.
Если говорить схематично, история изменений будет накапливаться в виде простой структуры ("Тип LINQtoSQL объекта", "Значение Primary Key-я", "Имя свойства", "Значение свойства"). Возможно, потребуется передача значений всех свойств. Кроме того, будут аналогичные схемы для insert и delete. Еще timpstamp-ы. Но все это будет выполнятся в инфраструктурных классах, и вроде не так уж это сложно получается.
>Если нет — могу предложить вариант, где ClaimDTO и граф объектов являются надстройкой над типизированным DataSet-ом.
Мы рассматривали возможность использования DataSet-ов в качестве DTO. DataSet все-таки отличается от графа связанных объектов. В случае графа -- если объекты графа теряют ссылку на какой-либо объект, то он уже не пренадлежит этому графу, и его уберет сборщик мусора. В случае DataSet-а такого нет -- DataSet жестко хранит все DataRow в себе. Пример, в котором это проявляется, это сценарий с Lookup-ом. Lookup -- это поле на форме, значение поля можно выбирать из списка, список может быть большим, возможно, с пейджингом (например, список продуктов). Если пользователь меняет значение Lookup-а, то в случае графа объекта мы просто выставляем новое значение
orderItem.Product = newProduct
Новый продукт присоединился к графу, старый ушел к сборщику мусора. newProduct берется из списка, который показывается пользователю, либо по Id подгружается с сервера.
В случае с DataSet-ом, у нас проблемы. Мерджить DataSet-ы? или не иметь свойство Product у orderItem, и работать с двумя датасетами и выдергивать Product из второго DataSet-а по Id?
ClientContext (лучше его назвать просто ChangesTracker) не накладывает такой жесткой связи на множество объектов как DataSet. Между DataSet-ом и DataRow двухстороння связь -- DataSet знает о DataRow, и DataRow знает о DataSet. В случает ClientContext-а -- DtoObject хранит ссылку на ClientContext (точнее на делегат из ClientContext), а ClientContext не хранит ссылку на DtoObject.
>При использовании LINQ to SQL, такой DataSet вообще не обязан повторять схему БД, может содержать любую избыточную информацию и служить буфером для изменений в ClaimDTO или в схеме БД.
Схему типизированного DataSet-а каждый раз рисовать вручную?