2009-09-30 3 views
6

Вот небольшой эксперимент, который я сделал:Значения данных кэширования Linq - основная проблема параллелизма?

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 не перезагружает мой объект, но повторно использует тот, который он ранее имел в памяти! Это проблема огромная проблема для параллелизма данных!

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

EDIT - В отражении просто немыслимо, что Microsoft могла оставить такую ​​зияющую пропасть в рамках Linq. Вышеприведенный код - это тупиковая версия того, что я на самом деле делаю, и могут быть небольшие тонкости, которые я пропустил. Короче говоря, я был бы признателен, если бы вы сделали свое собственное экспериментирование, чтобы убедиться, что мои выводы выше правильны. Кроме того, должен существовать какой-то «секретный переключатель», который делает Linq устойчивым к одновременным обновлениям данных. Но что?

ответ

5

Это не проблема, я сталкивался раньше (так как я не склонен держать DataContexts открытыми в течение длительного периода времени), но, похоже, кто-то еще есть:

http://www.rocksthoughts.com/blog/archive/2008/01/14/linq-to-sql-caching-gotcha.aspx

+1

+1 OMG, это действительно Gotcha! Должен добавить это в C# Gotcha KB ... –

+1

Хуже, если вы следуете его совету и установите ObjectTrackingEnabled = false, вы можете читать только из БД; вы не можете делать SubmitChanges! Это ДЕЙСТВИТЕЛЬНО делает жизнь трудной! –

+0

Вот ссылка на C# gotcha KB: http://stackoverflow.com/questions/241134/what-is-the-worst-c-net-gotcha –

2

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

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

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

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

+0

Я, но я не работаю в среде только для чтения. Любые другие идеи? –

+0

Это по дизайну. Он предназначен для того, чтобы вы могли входить в состояние, изменять и записывать с использованием оптического параллелизма. Вы можете вызвать Refresh в DataContext, но это может убить любые сделанные вами изменения. Он не предназначен для синхронизации в реальном времени. Синхронизация и редактирование в реальном времени не идут хорошо. –

1

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

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

5

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

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

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

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

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

  • Я прочитал MyClass (Id = 1) из базы данных.
  • Программист изменен MyClass (Id = 1).
  • Я послал MyClass (Id = 1) в базу данных (смотрите на этом SQL, чтобы увидеть оптимистический параллелизм в ИНЕКЕ)
    • Обновление будет успешным, если версия этой базы данных соответствует исходной (оптимистический параллелизм).
    • Обновление не будет выполнено с использованием исключения параллелизма, если версия базы данных не соответствует оригиналу.

Хорошо, теперь, что задача сформулирована, вот несколько способов борьбы с ним.

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

Вы можете попросить экземпляр оригинала или измененный/изменяемый экземпляр обновить значение базы данных, позвонив по телефону DataContext.Refresh(RefreshMode, target) (reference docs with many good concurrency links in the "Remarks" section). Это приведет к изменениям на стороне клиента и позволит вашему коду выработать окончательный результат.

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

Смежные вопросы