C# работа с Entity Framework на многопоточном сервере
Какова лучшая практика для работы с структурой сущностей на многопоточном сервере? Я использую структуру лица ObjectContext
чтобы управлять всеми своими действиями с базой данных, теперь я знаю, что этот контекст не является потокобезопасным, поэтому сейчас, когда мне нужно использовать его для выполнения некоторых действий с БД, я окружаю его lock
заявление, чтобы быть в безопасности. Это как я должен это сделать??
6 ответов
Несколько полезных советов по Entity Framework в многопоточной среде:
- Не используйте уникальный контекст с
locks
(без единого шаблона) - Предоставлять сервисы без сохранения состояния (вам нужно создать экземпляр одного контекста для каждого запроса)
- Сократите время жизни контекста как можно больше.
- Реализуйте систему управления параллелизмом. Оптимистичный параллелизм может быть легко реализован с помощью Entity Framework (с практическими рекомендациями). Это гарантирует, что вы не перезапишете изменения в БД, когда используете устаревшую сущность
Я немного сбит с толку, я подумал, что использование одного контекста хорошо, потому что, по моему мнению, он делает некоторые выводы, поэтому, когда я работаю с одной и той же сущностью в последовательных запросах, гораздо быстрее будет использовать тот же контекст, чем создавать новый каждый раз. Так почему же полезно использовать его таким образом, если он медленнее и еще не безопасен для потоков?
Вы можете использовать только один контекст, но это настоятельно не рекомендуется, если вы действительно не знаете, что делаете.
Я вижу две основные проблемы, которые часто случаются с таким подходом:
вы будете использовать много памяти, так как ваш контекст никогда не будет удален, а все манипулируемые сущности будут кэшироваться в памяти (каждая сущность, которая появляется в результате запроса, кэшируется).
Вы столкнетесь с множеством проблем параллелизма, если измените данные из другой программы / контекста. Например, если вы изменяете что-то непосредственно в вашей базе данных, и связанная сущность уже была кэширована в вашем уникальном объекте контекста, ваш контекст никогда не узнает об изменениях, которые были сделаны непосредственно в базе данных. Вы будете работать с кешированной сущностью, которая не обновлена, и, поверьте мне, это приведет к трудным для поиска и исправления проблемам.
Также не беспокойтесь о производительности использования нескольких контекстов: издержки на создание / удаление нового контекста для каждого запроса практически невелики в 90% случаев использования. Помните, что создание нового контекста не обязательно создает новое соединение с базой данных (поскольку база данных обычно использует пул соединений).
Это как я должен это сделать??
Нет. Как минимум, используйте контекст для потока, но я настоятельно рекомендую вам рассматривать контекст как единицу работы и, таким образом, использовать контекст для единицы работы для потока.
Это зависит от вас, чтобы определить "единицу работы" для вашего приложения. Но не используйте lock
использовать контекст в нескольких потоках. Это не масштабируется.
Вы рассматриваете ObjectContext, как будто это чрезвычайно дорогая сущность, поэтому вы создаете экземпляр один раз, а затем рассматриваете его как "фасад". Там нет необходимости делать это. Если по какой-либо другой причине соединения объединяются под капотом и стоят очень мало (микросекунда? - вероятно, меньше?) Полной настройки "цепочки объектов" для использования абстракции ObjectContext.
ObjectContext, очень похожий на прямое использование SqlConnection и т. Д., Предназначен для использования с методологией "как можно более поздней реализации и сброса как можно скорее".
EF дает вам некоторую безопасность в том, что у вас есть возможность проверить, есть ли у вас новейшие объекты до совершения (Optimistic Concurrency). Это само по себе не означает "потокобезопасность", но выполняет то же самое, если вы уважаете правила.
Обычно ObjectContext не должен использоваться глобально во всем приложении. Вы должны часто создавать новые ObjectContext s и избавляться от старых. Они, конечно, также не безопасны. Если вы продолжаете использовать один и тот же ObjectContext (в зависимости от времени жизни вашего приложения), легко получить исключение нехватки памяти, если вы изменяете огромные объемы данных, так как ссылки на изменяемые объекты хранятся в контексте объекта.
Я создаю новый Контекст для каждой атомарной операции и располагаю контекст. Насколько я знаю по книгам и статьям, я предпочитаю максимально сократить время жизни в Context. (но это зависит от вашего подхода и типа приложения, winform или web)
Пожалуйста, найдите больше информации в большой статье. http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management
Существующее обсуждение в Datacontext Lifetime в сценарии привязки WinForm
Я использую Entity Framework в многопоточной среде, где любой поток, пользовательский интерфейс и фон (как STA, так и MTA) могут одновременно обновлять одну и ту же базу данных. Я решил эту проблему, заново создав соединение сущностей с нуля в начале использования в любом новом фоновом потоке. Изучение экземпляра соединения сущности ConnectionString показывает руководство читателя, которое, как я полагаю, используется для связи общих экземпляров соединения. При воссоздании соединения сущностей с нуля значения 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;