C# Entity Framework 新事务是不允许的,因为会话中有其他线程在运行,多线程保存

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15363242/
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-10 16:31:16  来源:igfitidea点击:

Entity Framework new transaction is not allowed because there are other threads running in the session, multi thread save

c#multithreadingentity-framework-4savechanges

提问by andrea

I'm tryng to save on a DB the log of a multi thread processo but I'm getting the following error: new transaction is not allowed because there are other threads running in the session.

我正在尝试将多线程进程的日志保存在数据库中,但出现以下错误:不允许新事务,因为会话中有其他线程正在运行。

in each tread I have this function:

在每个踏板中,我都有这个功能:

 internal bool WriteTrace(IResult result, string message, byte type)
    {
        SPC_SENDING_TRACE trace = new SPC_SENDING_TRACE(
                        message,
                        Parent.currentLine.CD_LINE,
                        type,
                        Parent.currentUser.FULLNAME,
                        Parent.guid);
        Context.SPC_SENDING_TRACE.AddObject(trace);
        if (Context.SaveChanges(result) == false)
            return false;
        return true;

    }

the Context is different for each thread, but the connection with the DB is always the same.

每个线程的 Context 不同,但与 DB 的连接始终相同。

is there a way to solve this problem?

有没有办法解决这个问题?

thank you Andrea

谢谢安德里亚

采纳答案by ecampver

You should create a context for each transaction and then dispose it, you can do that like this:

您应该为每个事务创建一个上下文,然后处理它,您可以这样做:

using(var ctx = new MyContext()) {
    //do transaction here
}

After the closed bracket the context is disposed.

在封闭的括号之后,上下文被处理。

For better understanding refer to this postwhere you can find a great answer by ken2k. Hope you can fix you issue :)

为了更好地理解,请参阅这篇文章,您可以在其中找到ken2k 的一个很好的答案。希望你能解决你的问题:)

UPDATE:

更新:

You should also try adding .ToList()to every LINQ query you have. When you iterate over a LINQ result, you can't make any changes until the iteration has finished. Check if you have something like that or share more code i.e. the piece of code where you call WriteTrace. Hope that this time this actually helps you.

您还应该尝试添加.ToList()到您拥有的每个 LINQ 查询中。迭代 LINQ 结果时,在迭代完成之前不能进行任何更改。检查您是否有类似的东西或共享更多代码,即您调用的代码段WriteTrace。希望这次真的能帮到你。

回答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.

我在多线程环境中使用实体框架,其中任何线程、ui 和后台(STA 和 MTA)都可以同时更新同一个数据库。我通过在任何新后台线程开始使用时从头开始重新创建实体连接来解决此问题。检查实体连接实例 ConnectionString 显示了一个阅读器 guid,我认为它用于链接公共连接实例。通过从头开始重新创建实体连接,每个线程的 guid 值都不同,并且似乎没有发生冲突。

// 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;