c# 在多线程服务器中使用实体框架

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9415955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 07:18:16  来源:igfitidea点击:

c# working with Entity Framework in a multi threaded server

c#databasemultithreadingentity-frameworkobjectcontext

提问by Eyal

What is the best practice for working with entity framework in a multi threaded server? I'm using entity framework ObjectContextto manage all my database actions, now I know this context isn't thread safe, so for now when I need to use it to perform some db actions I surround it with lockstatement to be safe. Is this how I should do it??

在多线程服务器中使用实体框架的最佳实践是什么?我正在使用实体框架ObjectContext来管理我所有的数据库操作,现在我知道这个上下文不是线程安全的,所以现在当我需要使用它来执行一些数据库操作时,我用lock语句包围它以确保安全。这是我应该怎么做??

采纳答案by ken2k

Some quick advices for Entity Framework in a multi-threaded environment:

多线程环境中实体框架的一些快速建议:

  • Don't use a unique context with locks(no singleton pattern)
  • Provide stateless services(you need to instantiate and dispose one context per request)
  • Shorten the context lifetime as much as possible
  • Do implement a concurrency-control system. Optimistic concurrency can be easily implemented with Entity Framework (how-to). This will ensure you don't overwrite changes in the DB when you use an entity that is not up-to-date
  • 不要使用具有locks(无单例模式)的唯一上下文
  • 提供无状态服务(您需要为每个请求实例化和处理一个上下文
  • 尽可能缩短上下文生命周期
  • 执行并发控制系统。使用 Entity Framework ( how-to)可以轻松实现乐观并发。这将确保您在使用不是最新的实体时不会覆盖数据库中的更改


I'm a bit confused, I thought that using one context is good because it does some catching I believe, so when I'm dealing with the same entity in consecutive requests it be much faster to use the same context then creating a new context every time. So why does it good to use it like this if It slower and still not thread safe?

我有点困惑,我认为使用一个上下文很好,因为我相信它可以捕获一些信息,所以当我在连续请求中处理同一个实体时,使用相同的上下文然后创建一个新的上下文要快得多每次。那么,如果它更慢并且仍然不是线程安全的,那么为什么像这样使用它是好的呢?

You coulduse only one context, but it's strongly discouraged unless you really know what you are doing.

可以只使用一种上下文,但强烈建议不要这样做,除非您真的知道自己在做什么

I see two main problems that often happen with such an approach:

我看到这种方法经常发生的两个主要问题:

  1. you'll use a lot of memory as your context will never be disposed and all manipulated entities will be cached in memory (each entity that appears in the result of a query is cached).

  2. you'll face lots of concurrency issues if you modify you data from another program/context. For example, if you modify something directly in your database and the associated entity was already cached in your unique context object, then your context won't ever know about the modificationthat was made directly in the database. You'll work with a cached entity that is not up-to-date, and trust me, it'll lead to hard-to-find-and-fix issues.

  1. 您将使用大量内存,因为您的上下文永远不会被处理,并且所有被操纵的实体都将缓存在内存中(出现在查询结果中的每个实体都被缓存)。

  2. 如果您从另一个程序/上下文修改数据,您将面临许多并发问题。例如,如果您直接在数据库中修改某些内容并且关联实体已经缓存在您唯一的上下文对象中,那么您的上下文将永远不会知道直接在数据库中进行的修改。您将使用不是最新的缓存实体,相信我,它会导致难以查找和修复的问题。

Also don't worry about performances of using multiple contexts: the overhead of creating/disposing a new context per request is almost insignificant in 90% of use cases. Remember that creating a new context does not necessarily create a new connection to the database (as the database generally uses a connection pool).

也不要担心使用多个上下文的性能:在 90% 的用例中,每个请求创建/处理新上下文的开销几乎微不足道。请记住,创建新上下文并不一定会创建到数据库的新连接(因为数据库通常使用连接池)。

回答by jason

Is this how I should do it??

这是我应该怎么做??

No. At a minimum, use a context per thread but I strongly encourage you to think of a context as unit of work, and thus use a context per unit of work per thread.

不。至少,每个线程使用一个上下文,但我强烈建议您将上下文视为工作单元,因此每个线程的每个工作单元使用一个上下文。

It's up to you to define "unit of work" for your application. But don't use lockto use a context across multiple threads. It doesn't scale.

您可以为您的应用程序定义“工作单元”。但是不要用于lock跨多个线程使用上下文。它没有规模。

回答by Pongsathon.keng

I create a new Context for each atomic operation and dispose the context. As far as i know from books and articles, i prefer to keep life time of Context as short as possible. (but it depend on you approach and you application type, winform or web)

我为每个原子操作创建一个新的上下文并处理上下文。据我从书籍和文章中了解到,我更喜欢让 Context 的生命周期尽可能短。(但这取决于您的方法和应用程序类型,winform 或 web)

Please find more information at great article. http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management

请在伟大的文章中找到更多信息。 http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management

Good books: 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

好书:http: //books.google.co.th/books?id=Io7hHlVN3qQC&pg=PA580&lpg=PA580&dq=DbContext+lifetime+for+desktop+application&source=bl&ots=ogCOomQwEE&sig=At3G1Y6AbbJH7OHlqcyd&htei&lpg=PA580&dq=DbContext+lifetime+for+desktop+application&source=bl&ots=ogCOomQwEE&sig=At3G1Y6AbbJH7OHLycti&lz&eqdl&lpg=PA580&dq=DbContext+lifetime+for+desktop+application&source=bl&ots= =result&resnum=2&ved=0CCgQ6AEwAQ#v=onepage&q&f=false

Existing discussion at Datacontext Lifetime in WinForm Binding Scenario

WinForm 绑定方案Datacontext Lifetime 的现有讨论

回答by Devin

Typically the ObjectContext shouldn't be used globally throughout the application. You should create new ObjectContexts frequently and dispose old ones. They certainly aren't threadsafe either. If you continue to use the same ObjectContext (depending on the lifetime of your application) it is easy to get an out of memory exception if you modify massive amounts of data since references to entities that you change are held by the object context.

通常,不应在整个应用程序中全局使用 ObjectContext。您应该经常创建新的 ObjectContext 并处理旧的。它们当然也不是线程安全的。如果您继续使用相同的 ObjectContext(取决于应用程序的生命周期),如果您修改大量数据,则很容易出现内存不足异常,因为对您更改的实体的引用由对象上下文保存。

回答by Gregory A Beamer

You are treating ObjectContext like it is an extremely expensive entity, so you are instantiating once and then treating it as a "facade". There is no need to do this. If, for no other reason, the connections are pooled underneath the hood and cost very little (microsecond? - probably less?) to completely set up the "chain of objects" to use the ObjectContext abstraction.

您将 ObjectContext 视为一个非常昂贵的实体,因此您将实例化一次,然后将其视为“外观”。没有必要这样做。如果没有其他原因,连接在引擎盖下汇集并且花费很少(微秒? - 可能更少?)来完全设置“对象链”以使用 ObjectContext 抽象。

The ObjectContext, much like direct use of SqlConnection, etc., is designed to be used with an "instantiate as late a possible and dump as soon as possible" methodology.

ObjectContext 与直接使用 SqlConnection 等非常相似,旨在与“尽可能晚地实例化并尽快转储”方法一起使用。

EF gives you some safety in that you have the ability to test whether you have the latest objects prior to committing (Optimistic Concurrency). This does not mean "thread safe", per se, but it accomplishes the same thing if you respect the rules.

EF 为您提供了一些安全性,因为您可以在提交之前测试您是否拥有最新的对象(乐观并发)。这本身并不意味着“线程安全”,但如果您遵守规则,它会完成同样的事情。

回答by David Coleman

I use entity framework in a multi-threaded environment, where any thread, ui and background (both STA and MTA), can concurrently update the same database. I resolved this problem by re-creating the entity connection from scratch at the start of usage on any new background thread. Examining the entity connection instance ConnectionString shows a reader guid which I assume is used to link common connection instances. By recreating the entity connection from scratch the guid values are different for each thread and no conflict appears to occur. Note the assembly is only required to be the same assembly as the model resides.

我在多线程环境中使用实体框架,其中任何线程、ui 和后台(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;