我目前正在此错误:
System.Data.SqlClient.SqlException:新的事务是不允许的,因为在会话中运行其他线程。
在运行此代码:
public class ProductManager : IProductManager
{
#region Declare Models
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
#endregion
public IProduct GetProductById(Guid productId)
{
// Do a quick sync of the feeds...
SyncFeeds();
...
// get a product...
...
return product;
}
private void SyncFeeds()
{
bool found = false;
string feedSource = "AUTO";
switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
{
if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
{
var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
{
foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
{
if (targetProduct.alternateProductID == sourceProduct.AutoID)
{
found = true;
break;
}
}
if (!found)
{
var newProduct = new RivWorks.Model.Negotiation.Product();
newProduct.alternateProductID = sourceProduct.AutoID;
newProduct.isFromFeed = true;
newProduct.isDeleted = false;
newProduct.SKU = sourceProduct.StockNumber;
company.Product.Add(newProduct);
}
}
_dbRiv.SaveChanges(); // ### THIS BREAKS ### //
}
}
}
break;
}
}
}
型号#1 - 这个模型坐落在我们的开发服务器上的数据库。 型号#1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
型号#2 - 这个模型坐落在我们的督促服务器上的数据库,并通过自动饲料,每天更新。 替代文字http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
注 - 红色型号盘旋项目1是我使用的“地图”到模型#2的字段。 请忽略型号#2的红色圆圈:这是另外一个问题我不得不也就是现在回答。
注:我还需要放在一个检查请将isDeleted,所以我可以从软删除DB1,如果它已经走了我们客户的库存。
所有我想做的事,与这个特殊的代码,是一个客户端连接一个公司DB1 DB2中,从DB2得到他们的产品清单,并把它插入DB1,如果它已不存在。 第一时间通过应该是库存的全面拉动。 每次它没有经过那里运行时间应该发生,除非新的库存排在进料过夜。
因此,最大的问题-怎样才能解决我的交易错误,我的故事吗? 我是否需要删除,并通过循环每次重新创建我的上下文(没有道理给我)?
Answer 1:
多拉出的头发后,我发现foreach
循环是罪犯。 需要采取什么是调用EF但它返回到IList<T>
这一目标类型然后循环对IList<T>
例:
IList<Client> clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
// ...
}
Answer 2:
正如你已经确定,你不能从内保存foreach
这仍然是从数据库中通过积极的读者绘制。
调用ToList()
或ToArray()
是适合于小数据集,但是当你有几千行,你会消耗大量的内存。
这是更好地加载块的行。
public static class EntityFrameworkUtil
{
public static IEnumerable<T> QueryInChunksOf<T>(this IQueryable<T> queryable, int chunkSize)
{
return queryable.QueryChunksOfSize(chunkSize).SelectMany(chunk => chunk);
}
public static IEnumerable<T[]> QueryChunksOfSize<T>(this IQueryable<T> queryable, int chunkSize)
{
int chunkNumber = 0;
while (true)
{
var query = (chunkNumber == 0)
? queryable
: queryable.Skip(chunkNumber * chunkSize);
var chunk = query.Take(chunkSize).ToArray();
if (chunk.Length == 0)
yield break;
yield return chunk;
chunkNumber++;
}
}
}
鉴于上述扩展方法,你可以写你的查询是这样的:
foreach (var client in clientList.OrderBy(c => c.Id).QueryInChunksOf(100))
{
// do stuff
context.SaveChanges();
}
调用此方法上可查询的对象必须是有序的。 这是因为实体框架只支持IQueryable<T>.Skip(int)
上订购查询,当你考虑到了不同范围的多个查询要求的排序是稳定这是有道理的。 如果排序是不是对你很重要,只是主键顺序,这可能有一个聚集索引。
这个版本将查询数据库中的100注批次是SaveChanges()
被调用为每个实体。
如果你想显着提高你的吞吐量,你应该调用SaveChanges()
不那么频繁。 使用这样的代码来代替:
foreach (var chunk in clientList.OrderBy(c => c.Id).QueryChunksOfSize(100))
{
foreach (var client in chunk)
{
// do stuff
}
context.SaveChanges();
}
这导致100倍更少的数据库更新调用。 当然,每个来电者的需要更长的时间来完成,但你还是出来到底遥遥领先。 您的里程可能会有所不同,但是这是世界上对我更快。
而且它可以让你在约会的例外左右。
编辑我运行SQL事件探查器和更新的一些事情,以改善性能后,重新审视这个问题。 对于任何人谁是有兴趣的,下面是一些示例的SQL显示了由DB创建。
第一循环不会需要跳过任何东西,所以比较简单。
SELECT TOP (100) -- the chunk size
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
FROM [dbo].[Clients] AS [Extent1]
ORDER BY [Extent1].[Id] ASC
后续调用需要跳过结果的前一大块,所以引入了使用row_number
:
SELECT TOP (100) -- the chunk size
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
FROM (
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], row_number()
OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[Clients] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 100 -- the number of rows to skip
ORDER BY [Extent1].[Id] ASC
Answer 3:
现在,我们已经发布到了官方回应打开了连接错误 。 我们建议您采用的解决方法如下:
这个错误是由于实体框架调用SaveChanges()调用过程中创建一个隐式事务。 解决此错误的最好方法是使用不同的模式(即,不是在阅读之中节省时间)或通过显式声明的事务。 这里有三种可能的解决方案:
// 1: Save after iteration (recommended approach in most cases)
using (var context = new MyContext())
{
foreach (var person in context.People)
{
// Change to person
}
context.SaveChanges();
}
// 2: Declare an explicit transaction
using (var transaction = new TransactionScope())
{
using (var context = new MyContext())
{
foreach (var person in context.People)
{
// Change to person
context.SaveChanges();
}
}
transaction.Complete();
}
// 3: Read rows ahead (Dangerous!)
using (var context = new MyContext())
{
var people = context.People.ToList(); // Note that this forces the database
// to evaluate the query immediately
// and could be very bad for large tables.
foreach (var person in people)
{
// Change to person
context.SaveChanges();
}
}
Answer 4:
只要把context.SaveChanges()
的结束后foreach
(循环)。
Answer 5:
事实上,你无法保存内部变化foreach
使用实体框架在C#中循环。
context.SaveChanges()
方法的作用就像一个提交常规数据库系统(RDMS)上。
只是让所有的变化(这实体框架将缓存),然后保存所有这些一次调用SaveChanges()
循环(它之外)之后,如数据库提交命令。
这工作,如果你可以一次保存所有更改。
Answer 6:
FYI:从一本书和一些线条调整,因为它的STIL有效:
调用的SaveChanges()方法开始,其自动回滚所有变更保存到如果迭代完成之前发生异常的数据库的交易; 否则事务提交。 你也许会每个实体更新或删除之后,而不是迭代完成,特别是当你更新或删除实体的庞大的数字后应用方法。
如果你尝试调用的SaveChanges()中的所有数据均已被处理之前,您所发生的一个“新的交易是不允许的,因为在会话中运行其他线程”的异常。 出现异常是因为SQL Server不容许有开放的,即使在连接字符串启用多个活动的记录集(MARS)一个SqlDataReader一个连接上开始一个新的事务(EF的默认连接字符串使MARS)
有时,它能够更好地理解为什么事情正在发生;-)
Answer 7:
我得到同样的问题,但在不同的情况。 我曾在一个列表框中的项目列表。 用户可以点击一个项目,然后选择删除,但我使用一个存储过程来删除的项目因为涉及删除项目大量的逻辑。 当我调用存储的过程删除工作正常,但任何未来的SaveChanges调用将导致错误。 我的解决办法是调用存储过程EF之外,这工作得很好。 出于某种原因,当我打电话使用的做事方式EF离开的东西开放的存储过程。
Answer 8:
这里有另一个2个选项,让您的SaveChanges调用()在每个循环。
第一个选项是使用一个的DbContext生成列表对象来遍历,然后创建一个第二的DbContext调用的SaveChanges()。 下面是一个例子:
//Get your IQueryable list of objects from your main DBContext(db)
IQueryable<Object> objects = db.Object.Where(whatever where clause you desire);
//Create a new DBContext outside of the foreach loop
using (DBContext dbMod = new DBContext())
{
//Loop through the IQueryable
foreach (Object object in objects)
{
//Get the same object you are operating on in the foreach loop from the new DBContext(dbMod) using the objects id
Object objectMod = dbMod.Object.Find(object.id);
//Make whatever changes you need on objectMod
objectMod.RightNow = DateTime.Now;
//Invoke SaveChanges() on the dbMod context
dbMod.SaveChanges()
}
}
第二个选项是让从数据库的DbContext对象的列表,但只有选择的ID。 然后通过ID的(大概是一个int)的列表中迭代,并获得对应于每个INT对象,并调用调用SaveChanges()的方式。 这种方法背后的想法是抓住整数大名单,是很多更有效然后让数据库对象的大名单,整个调用对象的.ToList()。 下面是该方法的一个例子:
//Get the list of objects you want from your DBContext, and select just the Id's and create a list
List<int> Ids = db.Object.Where(enter where clause here)Select(m => m.Id).ToList();
var objects = Ids.Select(id => db.Objects.Find(id));
foreach (var object in objects)
{
object.RightNow = DateTime.Now;
db.SaveChanges()
}
Answer 9:
始终使用选择的列表
例如:
var tempGroupOfFiles = Entities.Submited_Files.Where(r => r.FileStatusID == 10 && r.EventID == EventId).ToList();
然后遍历集合,同时保存更改
foreach (var item in tempGroupOfFiles)
{
var itemToUpdate = item;
if (itemToUpdate != null)
{
itemToUpdate.FileStatusID = 8;
itemToUpdate.LastModifiedDate = DateTime.Now;
}
Entities.SaveChanges();
}
Answer 10:
因此,在该项目是我有此相同的问题,问题并不在foreach
或.toList()
这实际上是在我们使用的AutoFac配置。 这创造了一些奇怪的情况是上面的错误被抛出,但也有一堆其他同等的错误被抛出。
这是我们的解决办法:改变了这个:
container.RegisterType<DataContext>().As<DbContext>().InstancePerLifetimeScope();
container.RegisterType<DbFactory>().As<IDbFactory>().SingleInstance();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
至:
container.RegisterType<DataContext>().As<DbContext>().As<DbContext>();
container.RegisterType<DbFactory>().As<IDbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().As<IUnitOfWork>();//.InstancePerRequest();
Answer 11:
我需要在表中读取一个巨大的ResultSet和更新一些记录。 我试图用大块作为建议德鲁诺克斯的答案 。
不幸的是后50000个记录我有OutOfMemoryException异常。 答案实体框架大的数据集,内存溢出异常解释说,这
EF创建其中用于改变检测使用(以便它可以持续改变数据库)的数据的第二个副本。 EF持有的背景和它的这一套,多数民众赞成的寿命这第二组运行你的内存不足。
该建议是恢复你的背景下每批。
主要的所以我检索最小和最大值键盘 - 阅读的表有作为汽车增量integers.Then我通过为每个块打开右键从记录的数据库检索数据块的主键。 处理该块上下文后关闭并释放内存。 它保证了内存使用量并没有增长。
下面是我的代码片段:
public void ProcessContextByChunks ()
{
var tableName = "MyTable";
var startTime = DateTime.Now;
int i = 0;
var minMaxIds = GetMinMaxIds();
for (int fromKeyID= minMaxIds.From; fromKeyID <= minMaxIds.To; fromKeyID = fromKeyID+_chunkSize)
{
try
{
using (var context = InitContext())
{
var chunk = GetMyTableQuery(context).Where(r => (r.KeyID >= fromKeyID) && (r.KeyID < fromKeyID+ _chunkSize));
try
{
foreach (var row in chunk)
{
foundCount = UpdateRowIfNeeded(++i, row);
}
context.SaveChanges();
}
catch (Exception exc)
{
LogChunkException(i, exc);
}
}
}
catch (Exception exc)
{
LogChunkException(i, exc);
}
}
LogSummaryLine(tableName, i, foundCount, startTime);
}
private FromToRange<int> GetminMaxIds()
{
var minMaxIds = new FromToRange<int>();
using (var context = InitContext())
{
var allRows = GetMyTableQuery(context);
minMaxIds.From = allRows.Min(n => (int?)n.KeyID ?? 0);
minMaxIds.To = allRows.Max(n => (int?)n.KeyID ?? 0);
}
return minMaxIds;
}
private IQueryable<MyTable> GetMyTableQuery(MyEFContext context)
{
return context.MyTable;
}
private MyEFContext InitContext()
{
var context = new MyEFContext();
context.Database.Connection.ConnectionString = _connectionString;
//context.Database.Log = SqlLog;
return context;
}
FromToRange是一个简单的结构,从和到属性。
Answer 12:
我也面临着同样的问题。
这里是原因和解决方案。
http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx
发射数据操作之前请命令,如插入,更新,你已经关闭了所有以前的活动SQL读者。
最常见的错误是读取数据库和返回值的数据功能。 对于像isRecordExist如功能。
在这种情况下,我们立即从函数返回,如果我们找到了记录,并忘记关闭的读者。
Answer 13:
就我而言,我打电话给通过EF存储过程,然后后面的SaveChanges抛出此异常的问题出现了。 在调用程序,枚举未安置问题。 我固定的方式如下代码:
public bool IsUserInRole(string username, string roleName, DataContext context)
{
var result = context.aspnet_UsersInRoles_IsUserInRoleEF("/", username, roleName);
//using here solved the issue
using (var en = result.GetEnumerator())
{
if (!en.MoveNext())
throw new Exception("emty result of aspnet_UsersInRoles_IsUserInRoleEF");
int? resultData = en.Current;
return resultData == 1;//1 = success, see T-SQL for return codes
}
}
Answer 14:
下面的代码为我工作:
private pricecheckEntities _context = new pricecheckEntities();
...
private void resetpcheckedtoFalse()
{
try
{
foreach (var product in _context.products)
{
product.pchecked = false;
_context.products.Attach(product);
_context.Entry(product).State = EntityState.Modified;
}
_context.SaveChanges();
}
catch (Exception extofException)
{
MessageBox.Show(extofException.ToString());
}
productsDataGrid.Items.Refresh();
}
Answer 15:
我感到非常迟到了,但今天我遇到了同样的错误,以及如何我决心很简单。 我的情况是类似这样定的代码我正在嵌套的for-each循环的内部数据库交易。
问题是,作为一个单一数据库事务需要一点点的时间比for-each循环不再那么一旦较早的事务不会再完成新的牵引抛出一个异常,那么解决的办法是在for-each循环来创建新对象在那里你是一个数据库事务。
对于上述情形的解决方案将是这样的:
foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
{
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
{
var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
{
foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
{
if (targetProduct.alternateProductID == sourceProduct.AutoID)
{
found = true;
break;
}
}
if (!found)
{
var newProduct = new RivWorks.Model.Negotiation.Product();
newProduct.alternateProductID = sourceProduct.AutoID;
newProduct.isFromFeed = true;
newProduct.isDeleted = false;
newProduct.SKU = sourceProduct.StockNumber;
company.Product.Add(newProduct);
}
}
_dbRiv.SaveChanges(); // ### THIS BREAKS ### //
}
}
Answer 16:
我有点晚了,但我也有这种错误。 我通过检查什么地方更新其中的值是解决了这个问题。
我发现我的查询是错误的,存在其中超过250+的编辑悬而未决。 所以我纠正了我的查询,而现在它的工作原理是正确的。
所以在我的情况:在结果查询返回检查错误的查询,通过调试。 之后纠正查询。
希望这有助于解决未来的问题。
Answer 17:
我知道这是一个老问题,但我今天遇到这个错误。
我发现,当数据库表触发器得到一个错误这个错误被抛出。
你的信息,你可以检查你的表触发过,当你得到这个错误。
Answer 18:
如果你得到这个错误是由于的foreach,你真的需要保存一个实体第一内环路和环路还使用生成的标识,如在我的情况下,最简单的解决方法是使用另一种的DbContext插入实体将返回标识和使用这个标识外背景
例如
using (var context = new DatabaseContext())
{
...
using (var context1 = new DatabaseContext())
{
...
context1.SaveChanges();
}
//get id of inserted object from context1 and use is.
context.SaveChanges();
}
Answer 19:
制作()您可查询名单.ToList,它应该工作的罚款。
文章来源: SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session