Consider the following code
[TestClass]
public sealed class Rc2Tests : MyDbTests
{
public int ID { get; set; } = 1;
[TestMethod]
public async Task ForEachAsyncTest()
{
MyDbContext db = this.GetRequiredService<MyDbContext>();
CancellationToken ct = CancellationToken.None;
db.Projects.Add(new DbProject("Test", "Test"));
await db.SaveChangesAsync(ct);
int id = 1;
using (IDbContextTransaction t = await db.Database.BeginTransactionAsync(ct))
{
await db.Projects
.Where(p => p.Id == 1)
.ForEachAsync(p =>
{
p.DisplayName = "NewName1";
}, ct);
await db.SaveChangesAsync(ct);
t.Commit();
}
int[] ids = new[] { id };
using (IDbContextTransaction t = await db.Database.BeginTransactionAsync(ct))
{
await db.Projects
.Where(p => ids.Contains(p.Id))
.ForEachAsync(p =>
{
p.DisplayName = "NewName2";
}, ct);
await db.SaveChangesAsync(ct);
t.Commit();
}
using (IDbContextTransaction t = await db.Database.BeginTransactionAsync(ct))
{
await db.Projects
.Where(p => p.Id == id) // Throws System.Data.SqlClient.SqlException: The transaction operation cannot be performed because there are pending requests working on this transaction.
.ForEachAsync(p =>
{
p.DisplayName = "NewName3";
}, ct);
await db.SaveChangesAsync(ct);
t.Commit();
}
using (IDbContextTransaction t = await db.Database.BeginTransactionAsync(ct))
{
await db.Projects
.Where(p => p.Id == this.ID) // Also throws System.Data.SqlClient.SqlException: The transaction operation cannot be performed because there are pending requests working on this transaction.
.ForEachAsync(p =>
{
p.DisplayName = "NewName3";
}, ct);
await db.SaveChangesAsync(ct);
t.Commit();
}
}
}
The line with // Throws System.Data.SqlClient.SqlException causes the following at t.Commit();
System.Data.SqlClient.SqlException: The transaction operation cannot be performed because there are pending requests working on this transaction.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
at System.Data.SqlClient.SqlInternalTransaction.Commit()
at System.Data.SqlClient.SqlTransaction.Commit()
at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Commit()
The DB contains "NewName2"
This was working in RC1.
Please help, I am desperate. Any idea for a workaround would be great.
I found a possible fix
int id = 1;
using (IDbContextTransaction t = await db.Database.BeginTransactionAsync(ct))
{
await db.Projects
.Where(p => p.Id == EfIssue.Fix(id))
.ForEachAsync(p => { p.DisplayName = "Fixed"; }, ct);
await db.SaveChangesAsync(ct);
t.Commit();
}
With
public static class EfIssue
{
/// <summary>
/// Fixes https://github.com/aspnet/EntityFramework/issues/5579
/// by returning the value you give to it.
/// </summary>
public static T Fix<T>(T value)
{
return value;
}
}
Possible dupe of #5440
@Mertsch Can you explain the fix? Also, are you able to re-test this against a nightly build? You can use this feed: https://www.myget.org/F/aspnetvnext/api/v3/index.json
@anpete The fix EfIssue.Fix(id) just changes the signature of the anonymous method created.
Instead of a property / variable accessor a method is invoked, this apparently changes (and corrects) the way the linq query is analyzed.
I will try to check against the nightly, but it will take a day or two.
@Mertsch We think this is likely fixed post RC2. Please feel free to re-open if this is still an issue.
@anpete Confirmed fixed in 1.0 RTM