Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, MxKazan, Вы писали:
L>>>Предположим, что у нас есть класс "Группа", у которой есть свойство-коллекция "Студенты". Что должно произойти с содержимым этой поллекции при удалении студента не напрямую, а опосредованно, через delete-запрос?
MK>>Это вполне можно оставить на разработчика. Если его будет интересовать состояние коллекции "Студенты", то он ее перечитает.
L>Как? И как он узнает, что именно надо перечитать?
Эээ... а что, человек работает с БД не зная о ёё структуре?
Здравствуйте, MxKazan, Вы писали:
L>>>>Предположим, что у нас есть класс "Группа", у которой есть свойство-коллекция "Студенты". Что должно произойти с содержимым этой поллекции при удалении студента не напрямую, а опосредованно, через delete-запрос?
MK>>>Это вполне можно оставить на разработчика. Если его будет интересовать состояние коллекции "Студенты", то он ее перечитает.
L>>Как? И как он узнает, что именно надо перечитать?
MK>Эээ... а что, человек работает с БД не зная о ёё структуре?
Не о стурктуре речь. Даже если предположить, что он ее знает, не факт, что он знает о всех загруженных в данный момент в память "Группах".
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, MxKazan, Вы писали:
L>>>>>Предположим, что у нас есть класс "Группа", у которой есть свойство-коллекция "Студенты". Что должно произойти с содержимым этой поллекции при удалении студента не напрямую, а опосредованно, через delete-запрос?
MK>>>>Это вполне можно оставить на разработчика. Если его будет интересовать состояние коллекции "Студенты", то он ее перечитает.
L>>>Как? И как он узнает, что именно надо перечитать?
MK>>Эээ... а что, человек работает с БД не зная о ёё структуре?
L>Не о стурктуре речь. Даже если предположить, что он ее знает, не факт, что он знает о всех загруженных в данный момент в память "Группах".
Ну это уже скорее вопросы проектирования. Удалять что-то, не понимая к чему это может привести — неправильный метод. Такой случай я и не рассматриваю.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Sinclair, Вы писали:
S>>Здравствуйте, Lloyd, Вы писали:
L>>>А как определить, что конкретный петя подпадает под это условие? Что если список оценок на клиента еще не загружен? S>>Не надо ничего загружать. Нужно просто сконвертировать linq запрос в sql запрос и передать на сервер для выполнения.
L>Предположим, что у нас есть класс "Группа", у которой есть свойство-коллекция "Студенты". Что должно произойти с содержимым этой поллекции при удалении студента не напрямую, а опосредованно, через delete-запрос?
гм. то же, что в случае когда эта коллекция есть в кеше другого клиента. ты никогда не узнаешь, актуальные ли данные у тебя в данный момент. они теряют актуальность на момент, когда выплевываются из базы (читай заканчивается транзакция)
Здравствуйте, MxKazan, Вы писали:
L>>Не о стурктуре речь. Даже если предположить, что он ее знает, не факт, что он знает о всех загруженных в данный момент в память "Группах".
MK>Ну это уже скорее вопросы проектирования. Удалять что-то, не понимая к чему это может привести — неправильный метод.
А если удаляя что-то мы всегда должны помнить, на что это может повлиять, то получаем высокую связность. Что тоже не хорошо.
Так что, как ни крути, а ничего хорошего от добавления удаления без предварительно загрузки в память — не получается.
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, MxKazan, Вы писали:
L>>>Не о стурктуре речь. Даже если предположить, что он ее знает, не факт, что он знает о всех загруженных в данный момент в память "Группах".
MK>>Ну это уже скорее вопросы проектирования. Удалять что-то, не понимая к чему это может привести — неправильный метод.
L>А если удаляя что-то мы всегда должны помнить, на что это может повлиять, то получаем высокую связность. Что тоже не хорошо. L>Так что, как ни крути, а ничего хорошего от добавления удаления без предварительно загрузки в память — не получается.
Ну собственно, вынуждая нас запрашивать из БД, то что мы и без того хотим удалить, Linq данную проблему никак не решает, потому что сюда
Здравствуйте, MxKazan, Вы писали:
MK>Ну это уже скорее вопросы проектирования. Удалять что-то, не понимая к чему это может привести — неправильный метод. Такой случай я и не рассматриваю.
Здря. Linq предоставляет возможности по декомпозиции. А это значит что часть запросов или даже часть запроса может быть перенесена в другой кусок кода. Не нужно в голове держать полную последовательность действий.
Здравствуйте, MxKazan, Вы писали:
L>>А если удаляя что-то мы всегда должны помнить, на что это может повлиять, то получаем высокую связность. Что тоже не хорошо. L>>Так что, как ни крути, а ничего хорошего от добавления удаления без предварительно загрузки в память — не получается.
MK>Ну собственно, вынуждая нас запрашивать из БД, то что мы и без того хотим удалить, Linq данную проблему никак не решает, потому что сюда
Есть такое. Но если с рассогласованностью кэшей у разных клиентов еще как-то можно смириться, то с рассогласованностью с самим собой — это уже по-моему чрезчур.
Здравствуйте, MxKazan, Вы писали:
MK>Ну собственно, вынуждая нас запрашивать из БД, то что мы и без того хотим удалить, Linq данную проблему никак не решает, потому что сюда
.
Почему не решает? Вполне решает. Просто тут важны приоритеты. Транзакция которая закончилась неуспешно, и не смогла перевести базу данных в несогласованное состояние — это почти не зло. Зло — это когда губят базу данных. Если мы выходим за пределы транзакции, то это действительно нужно делать осторожно. Глобальный кэш — это хорошее средство для того чтобы ее загубить. Но если ты не выходишь за пределы транзакции, используешь только те данные которые в данной транзакции получены и не изменены другой транзакцией до фиксации (что опасно в кэше), то проблемы как таковой — нет. Средств для обеспечения согласованности, тот же DbContext — вполне достаточны.
Здравствуйте, MxKazan, Вы писали:
IT>>В некотором смысле первый. Дело в том, что преобразованием имени класса в имя поля теперь занимается сам компилятор. Ранее это был ручной или генерируемый код. MK>А какая в сущности разница?
Комрилятор знает больше. Сгенерированная строковая константа для компилятора всё так же остаётся константой. А поле/свойство класса — это ещё и принадлежность к определённому классу. В принципе, precompile-time генератция может многое, но у неё свои козявки. Идеал же вообще ещё не достигнут, но уже возможен. Так, например, уже сегодня вполне возможно на Немерле генерировать необходимую метаинформацию непосредственно из БД в компайл-тайм
MK>Намёк ясен. Но он не совсем применим к Linq-To-Sql, уж слишком специфично, я написал чего не хватает. Видно и Microsoft это понимает, потому и есть Entity Framework. А проблема с DELETE похоже не имеет простого решения.
По мне, так EF — это чистый маркетинг. Наш ответ Хайберлену. Есть же в конце концов и другие невостребованные технологии у MS вроде того же WWF. Будет ещё одна.
MK>Ну вот, например, мне хотелось обновлять объекты в БД так, чтобы генерируемый Sql запрос не проверял изменение остальных полей (имеется ввиду concurrency). Приходилось ручками проставлять почти всем полям класса UpdateCheck в Never. Поменял поле, перегенерил класс и опять давай проставлять. Справедливости ради скажу, это скорее следует отнести к недостаткам Студии.
То о чём ты говоришь если это так, то это ошибки в дизайне. Я не хочу, чтобы компьютер за меня думал. Машина не должна думать, машина должна ездить. Куда скажу, туда и поехала. Тут в соседней ветке об этом уже на 400 сообщений нафлеймили, доказывая, что в хайбернейте LL это очень полезно. Вот такими спецэфектами потом приходится расплачиваться.
IT>>Linq 2 Objects внёс декларативность. Т.е. теперь я пишу что я хочу получить, а не как. В результате вместо кучи циклов с перебором списков и словарей у меня декларативная конструкция. MK>Это понятно, про ФП я читал. Но не очень понятно в чем конкретная новизна по памяти? Речь о локальности данных?
О декларативности работы с данными. Давай рассмотрим пример, может быть так понятней будет. Пример возьмём не очень сложный, но и не самый простой, т.к. на простых примерах вся мощь линка не так проявляется.
Возьмём финансовую задачку. Допустим у нас есть провайдеры, которые тратят бабло и есть получатели, на которых это бабло списывается. С провайдеров деньги списываются через аккаунты в соответствии с определёнными процентами. Например, провайдер '1111' списывает 20% на аккаунт 'AAAA' и 80% на аккаунт 'BBBB'. Получатели получают свою долю тоже через аккаунты, по такому же принципу, т.е., например, получатель '8888' получает 40% с аккаунта 'AAAA' и 60% с аккаунта 'BBBB'. Наша задача получить массив процентов, в котором будет указано, что провайдер '1111' списывает через аккаунт 'AAAA' столько-то процентов на '8888'.
Ниже приведён код. Метод CalcPercents делает эту работу с помощью linq, метод CalcPercents2 в исполнении C# 2.0.
using System;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApplication
{
class Program
{
class ProvidingVolume
{
public string Provider;
public string Account;
public double Volume;
}
class ReceivingVolume
{
public string Receiver;
public string Account;
public double Volume;
}
class Percent
{
public string Provider;
public string Receiver;
public string Account;
public double Value;
}
static Dictionary<string, List<Percent>> CalcPercents(
ProvidingVolume[] providingVolumes, ReceivingVolume[] receivingVolumes)
{
var percents =
from percent in
from prov in
from p in providingVolumes
group p by p.Provider into pg
let total = pg.Sum(v => v.Volume)
from pp in pg
select new { pp, total }
join rec in
from r in receivingVolumes
group r by r.Account into rg
let total = rg.Sum(v => v.Volume)
from rr in rg
select new { rr, total }
on prov.pp.Account equals rec.rr.Account
select new Percent
{
Provider = prov.pp.Provider,
Receiver = rec.rr.Receiver,
Account = prov.pp.Account,
Value = prov.pp.Volume / prov.total * rec.rr.Volume / rec.total
}
group percent by percent.Provider;
return percents.ToDictionary(p => p.Key, p => p.ToList());
}
static Dictionary<string, List<Percent>> CalcPercents2(
ProvidingVolume[] providingVolumes, ReceivingVolume[] receivingVolumes)
{
Dictionary<string, List<ProvidingVolume>> providingGroup = new Dictionary<string,List<ProvidingVolume>>();
foreach (ProvidingVolume pv in providingVolumes)
{
List<ProvidingVolume> p;
if (!providingGroup.TryGetValue(pv.Provider, out p))
{
p = new List<ProvidingVolume>();
providingGroup[pv.Provider] = p;
}
p.Add(pv);
}
Dictionary<string, List<ReceivingVolume>> receivingGroup = new Dictionary<string,List<ReceivingVolume>>();
foreach (ReceivingVolume rv in receivingVolumes)
{
List<ReceivingVolume> r;
if (!receivingGroup.TryGetValue(rv.Account, out r))
{
r = new List<ReceivingVolume>();
receivingGroup[rv.Account] = r;
}
r.Add(rv);
}
Dictionary<string, double> receivingSum = new Dictionary<string,double>();
foreach (List<ReceivingVolume> list in receivingGroup.Values)
{
double sum = 0;
foreach (ReceivingVolume rv in list)
sum += rv.Volume;
receivingSum[list[0].Account] = sum;
}
Dictionary<string, List<Percent>> percents = new Dictionary<string,List<Percent>>();
foreach (List<ProvidingVolume> list in providingGroup.Values)
{
double sum = 0;
foreach (ProvidingVolume pv in list)
sum += pv.Volume;
foreach (ProvidingVolume pv in list)
{
List<ReceivingVolume> rv;
if (receivingGroup.TryGetValue(pv.Account, out rv))
{
foreach (ReceivingVolume r in rv)
{
Percent percent = new Percent();
percent.Provider = pv.Provider;
percent.Receiver = r.Receiver;
percent.Account = pv.Account;
percent.Value = pv.Volume / sum * r.Volume / receivingSum[r.Account];
List<Percent> ps;
if (!percents.TryGetValue(percent.Provider, out ps))
{
ps = new List<Percent>();
percents.Add(percent.Provider, ps);
}
ps.Add(percent);
}
}
}
}
return percents;
}
static void Main(string[] args)
{
var providingVolums = new []
{
new ProvidingVolume { Provider = "1111", Account = "AAAA", Volume = 20 },
new ProvidingVolume { Provider = "1111", Account = "BBBB", Volume = 80 },
new ProvidingVolume { Provider = "2222", Account = "AAAA", Volume = 30 },
new ProvidingVolume { Provider = "2222", Account = "BBBB", Volume = 70 },
};
var receivingVolumes = new []
{
new ReceivingVolume { Receiver = "8888", Account = "AAAA", Volume = 40 },
new ReceivingVolume { Receiver = "8888", Account = "BBBB", Volume = 60 },
new ReceivingVolume { Receiver = "9999", Account = "AAAA", Volume = 50 },
new ReceivingVolume { Receiver = "9999", Account = "BBBB", Volume = 50 },
};
var percents = CalcPercents(providingVolums, receivingVolumes);
foreach (var ps in percents.Values)
foreach (var p in ps)
Console.WriteLine("{0} {1} {2} {3}", p.Provider, p.Receiver, p.Account, p.Value);
percents = CalcPercents2(providingVolums, receivingVolumes);
foreach (var ps in percents.Values)
foreach (var p in ps)
Console.WriteLine("{0} {1} {2} {3}", p.Provider, p.Receiver, p.Account, p.Value);
}
}
}
Обрати внимание, кода на линке в 3 раза меньше и он декларативен, в отличии от императивщины на C# 2.0. А если учесть, что таких вычислений у меня в коде пачками, то выгода linq более чем очевидна.
MK>Но вообще, IT, я бы хотел еще раз обозначить, что я принципе не против Linq. Хотелось бы просто понимать реальную необходимость этого, реальные плюсы, а не просто как "игрушка для этюдов". Разве это есть гуд, требовать "что", не понимая "как"?
Но ты же не говоришь SQL серверу как именно выполнять запросы? Ты говоришь, хочу то-то и то-то, а он уже сам читает с диска или из кеша, оптимизирует запросы и занимается прочими 'как'.
Неясность изложения обычно происходит от путаницы в мыслях.
Если нам не помогут, то мы тоже никого не пощадим.
[]
L>Есть такое. Но если с рассогласованностью кэшей у разных клиентов еще как-то можно смириться, то с рассогласованностью с самим собой — это уже по-моему чрезчур.
Здравствуйте, Константин Л., Вы писали:
L>>Есть такое. Но если с рассогласованностью кэшей у разных клиентов еще как-то можно смириться, то с рассогласованностью с самим собой — это уже по-моему чрезчур.
КЛ>но тогда, по твоим словам, проблема не решаема
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Константин Л., Вы писали:
L>>>Есть такое. Но если с рассогласованностью кэшей у разных клиентов еще как-то можно смириться, то с рассогласованностью с самим собой — это уже по-моему чрезчур.
КЛ>>но тогда, по твоим словам, проблема не решаема
L>которая из?
1. использовать линк
2. грохать каким-то образом объекты
3. "не поломать" загруженные объекты
Здравствуйте, Константин Л., Вы писали:
L>>>>Есть такое. Но если с рассогласованностью кэшей у разных клиентов еще как-то можно смириться, то с рассогласованностью с самим собой — это уже по-моему чрезчур.
КЛ>>>но тогда, по твоим словам, проблема не решаема
L>>которая из?
КЛ>1. использовать линк КЛ>2. грохать каким-то образом объекты КЛ>3. "не поломать" загруженные объекты
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Константин Л., Вы писали:
L>>>>>Есть такое. Но если с рассогласованностью кэшей у разных клиентов еще как-то можно смириться, то с рассогласованностью с самим собой — это уже по-моему чрезчур.
КЛ>>>>но тогда, по твоим словам, проблема не решаема
L>>>которая из?
КЛ>>1. использовать линк КЛ>>2. грохать каким-то образом объекты КЛ>>3. "не поломать" загруженные объекты
L>Выходит что так.
не может быть
а что там у нас с identity? есть что почитать по Linq internals?
Здравствуйте, Lloyd, Вы писали:
L>Здравствуйте, Константин Л., Вы писали:
L>>>Выходит что так.
КЛ>>не может быть
КЛ>>а что там у нас с identity? есть что почитать по Linq internals?
L>да нет там никаких особо internals. linq прост как пробка.
Здравствуйте, Константин Л., Вы писали:
КЛ>>>не может быть
КЛ>>>а что там у нас с identity? есть что почитать по Linq internals?
L>>да нет там никаких особо internals. linq прост как пробка.
КЛ>ну так с identity то что?
Здравствуйте, nikov, Вы писали:
RO>>Какая именно? RO>>Он написал не одну, и российские издательства использовали разные написания.
N>На какую книгу ссылаешься, из той книги вариант написания фамилии автора и надо брать для ссылки.
А если я хочу сказать: «Некто Тр___сен заявляет, будто неплохо разбирается в .NET»?