c# работа с Entity Framework на многопоточном сервере

какова наилучшая практика работы с entity framework на многопоточном сервере? Я использую entity framework ObjectContext чтобы управлять всеми моими действиями базы данных, теперь я знаю, что этот контекст не является потокобезопасным, поэтому сейчас, когда мне нужно использовать его для выполнения некоторых действий БД, я окружаю его lock заявление, чтобы быть безопасным. Вот как я должен это сделать??

6 ответов


некоторые быстрые советы для Entity Framework в многопоточной среде:


Я немного смущен, я думал, что использование одного контекста хорошо, потому что я верю, что это заразительно, поэтому, когда я имею дело с тем же самым ... entity в последовательных запросах будет намного быстрее использовать то же самое контекст затем создает новый контекст каждый раз. Так почему же это хорошо? используйте его так, если он медленнее и все еще не потокобезопасен?

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

Я вижу две основные проблемы, которые часто возникают при таком подходе:

  1. вы будете использовать много памяти, так как ваш контекст никогда не будет удален, и все управляемые объекты будут кэшироваться в памяти (каждый объект, который появляется в результат запроса кэшируется).

  2. вы столкнетесь с множеством проблем параллелизма, если вы измените данные из другой программы / контекста. Например, если вы изменяете что-то непосредственно в своей базе данных, а Связанный объект уже кэширован в уникальном объекте контекста, то ваш контекст никогда не будет знать о модификации что было сделано непосредственно в базе данных. Вы будете работать с кэшированной сущностью, которая не обновлена, и поверьте мне, это приведет к трудно найти и исправить проблемы.

также не беспокойтесь о производительности использования нескольких контекстов: накладные расходы на создание / удаление нового контекста на запрос почти незначительны в 90% случаев использования. Помните, что создание нового контекста не обязательно создает новое соединение с базой данных (поскольку база данных обычно использует пул соединений).


Так ли я должен это сделать??

нет. Как минимум, используйте контекст в потоке, но я настоятельно рекомендую вам думать о контексте как о единице работы и, таким образом, использовать контекст в единице работы в потоке.

это до вас, чтобы определить "единицу работы" для вашего приложения. Но не используйте lock для использования контекста в нескольких потоках. Она не масштабируется.


вы рассматриваете ObjectContext как чрезвычайно дорогую сущность, поэтому вы создаете экземпляр один раз, а затем рассматриваете его как "фасад". В этом нет необходимости. Если ни по какой другой причине, соединения объединяются под капотом и стоят очень мало (микросекунды? - наверное, меньше?) полностью настроить "цепочку объектов" для использования абстракции ObjectContext.

ObjectContext, как и прямое использование SqlConnection и т. д. предназначен для использования с "создать экземпляр как можно позже и сбросить как можно скорее" методология.

EF дает вам некоторую безопасность в том, что у вас есть возможность проверить, есть ли у вас последние объекты до фиксации (оптимистичный параллелизм). Это не означает "потокобезопасный", как таковой, но он выполняет то же самое, если вы уважаете правила.


обычно ObjectContext не должен использоваться глобально во всем приложении. Вы должны часто создавать новые ObjectContexts и утилизировать старые. Они, конечно, не являются threadsafe либо. Если вы продолжаете использовать один и тот же ObjectContext (в зависимости от времени жизни вашего приложения), легко получить исключение из памяти, если вы изменяете массивные объемы данных, так как ссылки на объекты, которые вы изменяете, хранятся в контексте объекта.


Я создаю новый контекст для каждой атомарной операции и распоряжаться контексте. Насколько я знаю из книг и статей, я предпочитаю держать время жизни контекста как можно короче. (но это зависит от вашего подхода и типа приложения, winform или web)

пожалуйста найдите больше информации на большой статье. http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management

хорошие книги: http://books.google.co.th/books?id=Io7hHlVN3qQC&pg=PA580&lpg=PA580&dq=DbContext+lifetime+for+desktop+application&source=bl&ots=ogCOomQwEE&sig=At3G1Y6AbbJH7OHxgm-ZvJo0Yt8&hl=th&ei=rSlzTrjAIovOrQeD2LCuCg&sa=X&oi=book_result&ct=result&resnum=2&ved=0CCgQ6AEwAQ#v=onepage&q&f=false

существующее обсуждение на время жизни Datacontext в сценарии привязки WinForm


Я использую entity framework в многопоточной среде, где любой поток, пользовательский интерфейс и фон (как STA, так и MTA) могут одновременно обновлять одну и ту же базу данных. Я решил эту проблему, воссоздав соединение сущности с нуля в начале использования в любом новом фоновом потоке. Изучение экземпляра соединения сущности ConnectionString показывает идентификатор guid читателя, который, как я предполагаю, используется для связи общих экземпляров соединения. При воссоздании соединения сущности с нуля значения guid являются разные для каждого потока, и конфликт не возникает. Обратите внимание на сборку только в той же сборке, что модель находится.

public static EntityConnection GetEntityConnection(
// Build the connection string.

  var sqlBuilder = new SqlConnectionStringBuilder();
  sqlBuilder.DataSource = serverName;
  sqlBuilder.InitialCatalog = databaseName;
  sqlBuilder.MultipleActiveResultSets = true;
  ...
  var providerString = sqlBuilder.ToString();
  var sqlConnection = new SqlConnection(providerString);

// Build the emtity connection.

  Assembly metadataAssembly = Assembly.GetExecutingAssembly();
  Assembly[] metadataAssemblies = { metadataAssembly };
  var metadataBase = @"res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl";
  var dbModelMetadata = String.Format(metadataBase, objectContextTypeModelName);
  // eg: "res://*/Models.MyDatabaseModel.csdl|res://*/Models.MyDatabaseModel.ssdl|res://*/Models.MyDatabaseModel.msl"
  var modelMetadataPaths = modelMetadata.Split('|');
  var metadataWorkspace = new MetadataWorkspace(modelMetadataPaths, metadataAssemblies);
  var entityDbConnection = new EntityConnection(metadataWorkspace, sqlConnection);
  return entityDbConnection;