Значения данных кэширования Linq - основная проблема concurrency?

Вот небольшой эксперимент, который я сделал:

MyClass obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single();
Console.WriteLine(obj.MyProperty); // output = "initial"
Console.WriteLine("Waiting..."); // put a breakpoint after this line
obj = null;
obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); // same as before, but reloaded
Console.WriteLine(obj.MyProperty); // output still = "initial"
obj.MyOtherProperty = "foo";
dataContext.SubmitChanges(); // throws concurrency exception

Когда я попал в точку останова после строки 3, я перехожу в окно запроса SQL и вручную меняю значение на "обновленный". Затем я продолжаю бегать. Linq не перезагружает мой объект, но повторно использует тот, который он ранее имел в памяти! Это проблема огромная для данных concurrency!

Как отключить скрытый кеш объектов, которые Linq явно хранит в памяти?

РЕДАКТИРОВАТЬ. При отражении просто немыслимо, что Microsoft могла оставить такую ​​зияющую пропасть в рамках Linq. Вышеприведенный код - это тупиковая версия того, что я на самом деле делаю, и могут быть небольшие тонкости, которые я пропустил. Короче говоря, я был бы признателен, если бы вы сделали свое собственное экспериментирование, чтобы убедиться, что мои выводы выше правильны. Кроме того, должен существовать какой-то "секретный переключатель", который делает Linq устойчивым к одновременным обновлениям данных. Но что?

Ответ 2

LinqToSql имеет широкий спектр инструментов для решения проблем concurrency.

Первый шаг, однако, состоит в том, чтобы признать, что существует проблема concurrency!

Во-первых, предполагается, что жизненный цикл объектов, предназначенных для DataContext, должен соответствовать UnitOfWork. Если вы продолжаете работать в течение длительного периода времени, вам придется работать намного сложнее, потому что класс не предназначен для использования таким образом.

Во-вторых, DataContext отслеживает две копии каждого объекта. Один из них - это исходное состояние, а одно - измененное/изменяемое состояние. Если вы попросите MyClass с Id = 1, он вернет вам тот же экземпляр, который он дал вам в последний раз, а это измененная/изменяемая версия... не оригинал. Он должен сделать это, чтобы предотвратить проблемы с concurrency в экземплярах памяти... LinqToSql не позволяет одному DataContext знать две изменяемые версии MyClass (Id = 1).

В-третьих, DataContext понятия не имеет, изменилось ли ваше изменение в памяти до или после изменения базы данных, и поэтому не может обсуждать конфликт concurrency без каких-либо указаний. Все, что он видит, это:

  • Я прочитал MyClass (Id = 1) из базы данных.
  • Программист модифицировал MyClass (Id = 1).
  • Я отправил MyClass (Id = 1) обратно в базу данных (посмотрите на этот sql, чтобы увидеть оптимистичный concurrency в предложении where)
    • Обновление будет успешным, если версия базы данных будет соответствовать оригиналу (оптимистичный concurrency).
    • Обновление будет завершено с ошибкой concurrency, если версия базы данных не соответствует оригиналу.

Хорошо, теперь, когда проблема заявлена, вот несколько способов справиться с ней.

Вы можете выбросить DataContext и начать все заново. Это немного тяжело для некоторых, но, по крайней мере, это легко реализовать.

Вы можете попросить обновить исходный экземпляр или измененный/изменяемый экземпляр значением базы данных, вызвав DataContext.Refresh(RefreshMode, target) (справочные документы со многими хорошими concurrency в разделе "Замечания" ). Это приведет к изменениям на клиентской стороне и позволит вашему коду выработать окончательный результат.

Вы можете отключить проверку concurrency в dbml (ColumnAttribute.UpdateCheck). Это отключает оптимистичный concurrency, и ваш код будет топать над любыми другими изменениями. Также тяжелая рука, также легко реализуемая.

Ответ 3

Установите для свойства ObjectTrackingEnabled объекта DataContext значение false.

Когда ObjectTrackingEnabled установлен в true, DataContext ведет себя как Unit of Work. Он будет хранить любой объект, загруженный в память, чтобы он мог отслеживать изменения. DataContext должен помнить объект, когда вы первоначально загружали его, чтобы узнать, были ли сделаны какие-либо изменения.

Если вы работаете в сценарии только для чтения, вы должны отключить отслеживание объектов. Это может быть достойное повышение производительности.

Если вы не работаете в сценарии только для чтения, я не уверен, почему вы хотите, чтобы он работал таким образом. Если вы внесли изменения, зачем вы хотите, чтобы он ввел измененное состояние из базы данных?

Ответ 4

LINQ to SQL использует шаблон проектирования идентификационной карты, что означает, что он всегда будет возвращать тот же экземпляр объекта, для которого данный первичный ключ (если вы не отключите отслеживание объектов).

Решение просто использует второй контекст данных, если вы не хотите, чтобы он вмешивался в первый экземпляр или обновлял первый экземпляр, если вы это делаете.