Какова наилучшая практика для работы с инфраструктурой сущностей на многопоточном сервере? Я использую структуру сущности ObjectContext
для управления всеми действиями моей базы данных, теперь я знаю, что этот контекст не является потокобезопасным, поэтому пока мне нужно использовать его для выполнения некоторых действий db, я окружаю его с помощью инструкции lock
, чтобы быть в безопасности. Это как я должен это делать?C#, работающий с Entity Framework на многопоточном сервере
ответ
Некоторые быстрые советы для Entity Framework в многопоточной среде:
- Не используйте уникальный контекст с
locks
(без одноплодной шаблон) - Обеспечить stateless services (вам нужно создать экземпляр и распоряжаться один контекст по запросу)
- укоротить контекст жизни как можно больше
- ли реализовать concurrency-control system. Оптимистичный параллелизм может быть легко реализован с помощью Entity Framework (how-to). Это позволит вам не перезаписать изменения в БД, когда вы используете объект, который не уточненный
Я немного запутался, я думал, что с помощью одного контекста хорошо, потому что это немного ловушка, я верю, поэтому, когда я имею дело с тем же сущностью в последовательных запросах, гораздо быстрее использовать тот же контекст , а затем создавать новый контекст каждый раз. Так почему же это хорошо для использовать его, как это, если он медленнее и все еще не потокобезопасен?
Вы могли использовать только один контекст, но это настоятельно не рекомендуется , если вы действительно не знаете, что вы делаете.
Я вижу две основные проблемы, которые часто случаются при таком подходе:
вы будете использовать много памяти, как ваш контекст никогда не будут реализованы и все манипулируют объекты будут сохраняться в памяти (каждый объект, который появляется в результате запроса, кэшируется).
вы столкнетесь с множеством проблем параллелизма, если вы измените данные из другой программы/контекста. Например, если вы изменили что-то непосредственно в своей базе данных, и связанный объект уже был кэширован в вашем уникальном объекте контекста, то ваш контекст никогда не узнает об изменении, которое было сделано непосредственно в базе данных.Вы будете работать с кэшированным сущностью, которая не обновляется, и, поверьте мне, это приведет к проблемам, которые трудно найти и исправить.
Также не беспокоиться о спектаклях, используя несколько контекстов: накладные создания/утилизации нового контекста для каждого запроса практически незначительны в 90% случаях использования. Помните, что создание нового контекста не обязательно создает новое соединение с базой данных (поскольку база данных обычно использует пул соединений).
Это как я должен это сделать ??
№ Как минимум, используйте контекст для потока, но я настоятельно рекомендую вам подумать о контексте как единице работы и, таким образом, использовать контекст на единицу работы в потоке.
Это зависит от вас, чтобы определить «единицу работы» для вашего приложения. Но не используйте lock
для использования контекста для нескольких потоков. Он не масштабируется.
Это. Еще один момент - ресурсы, которые вы пытаетесь сохранить, теоретически, уже сохраняются пулом соединений. –
У вас означает, что мне нужно создать контекст перед каждой работой db и утилизировать его после завершения работы? Например, когда клиент отправляет запрос на вход, сервер создаст контекст-> проверить имя пользователя и пароль в БД с этим контекстом-> удалите контекст – Eyal
Да, это будет единица работы. – jason
Я создаю новый контекст для каждой атомной операции и удаляю контекст. Насколько я знаю из книг и статей, я предпочитаю максимально сократить время жизни Контекста. (но это зависит от вашего подхода и вашего типа приложения, winform или сети)
Дополнительную информацию можно найти в большой статье. http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management
Существующее обсуждение на Datacontext Lifetime in WinForm Binding Scenario
Обычно ObjectContext не следует использовать глобально по всему приложению. Вы должны создавать новые объекты ObjectContext и использовать старые. Они, конечно же, не являются потокобезопасными. Если вы продолжаете использовать один и тот же объект ObjectContext (в зависимости от срока службы вашего приложения), вы можете получить исключение из памяти, если вы изменяете массивные объемы данных, поскольку ссылки на сущности, которые вы меняете, удерживаются контекстом объекта.
Вы обрабатываете ObjectContext так, как будто это чрезвычайно дорогая сущность, поэтому вы создаете экземпляр один раз, а затем рассматриваете его как «фасад». Нет необходимости делать это. Если по какой-либо другой причине соединения объединены под капотом и стоят очень мало (микросекунда? - возможно, меньше?), Чтобы полностью настроить «цепочку объектов» для использования абстракции ObjectContext.
ObjectContext, так же как прямое использование SqlConnection и т. Д., Предназначен для использования с методологией «экземпляр как можно скорее и сбрасывать как можно скорее».
EF дает вам некоторую безопасность в том смысле, что у вас есть возможность проверить, есть ли у вас последние объекты до совершения (Оптимистический параллелизм). Это не означает «потокобезопасность», как таковая, но она выполняет то же самое, если вы уважаете правила.
Я использую структуру сущности в многопоточной среде, где любой поток, ui и фон (как 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;
Если я создаю контекст для каждого запроса (или для работы с db), и у меня есть более одного контекста, изменяющего конкретный объект из db в одно и то же время, это проблема? – Eyal
@Eyal У вас обычно возникают проблемы с параллелизмом. Попытка одновременного обновления одного и того же объекта несколькими потоками будет трудно реализовать без уступок. – ken2k
Я немного смущен, я подумал, что использование одного контекста является хорошим, потому что это действительно ловушка, я полагаю, поэтому, когда я имею дело с одним и тем же объектом в последовательных запросах, гораздо быстрее использовать один и тот же контекст, а затем создать новый контекст каждый раз. Так почему же полезно использовать его так, если он медленнее и все еще не потокобезопасен? – Eyal