1464 lines
61 KiB
C#
1464 lines
61 KiB
C#
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
|||
|
|
|||
|
namespace System.Data.Entity.Objects
|
|||
|
{
|
|||
|
using System.Data.Common;
|
|||
|
using System.Data.Entity.Core;
|
|||
|
using System.Data.Entity.Core.EntityClient;
|
|||
|
using System.Data.Entity.Core.Metadata.Edm;
|
|||
|
using System.Data.Entity.Core.Objects;
|
|||
|
using System.Data.Entity.Infrastructure;
|
|||
|
using System.Data.Entity.Resources;
|
|||
|
using System.Data.SqlClient;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
using System.Transactions;
|
|||
|
using Xunit;
|
|||
|
|
|||
|
public class TransactionLogEntry
|
|||
|
{
|
|||
|
public int ID { get; set; }
|
|||
|
public int TransactionCount { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class TransactionContext : ObjectContext
|
|||
|
{
|
|||
|
public TransactionContext(string connectionString)
|
|||
|
: base(connectionString)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public TransactionContext(EntityConnection connection)
|
|||
|
: base(connection)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public IQueryable<TransactionLogEntry> LogEntries
|
|||
|
{
|
|||
|
get { return CreateObjectSet<TransactionLogEntry>("Entities.TransactionLog"); }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class TransactionDbContext : DbContext
|
|||
|
{
|
|||
|
public TransactionDbContext(string connectionString)
|
|||
|
: base(connectionString)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public TransactionDbContext(EntityConnection connection, bool contextOwnsConnection)
|
|||
|
: base(connection, contextOwnsConnection)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public TransactionDbContext(SqlConnection connection, DbCompiledModel model, bool contextOwnsConnection)
|
|||
|
: base(connection, model, contextOwnsConnection)
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public DbSet<TransactionLogEntry> LogEntries { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public class TransactionsTests : FunctionalTestBase, IUseFixture<TransactionFixture>
|
|||
|
{
|
|||
|
private string connectionString;
|
|||
|
private string modelDirectory;
|
|||
|
|
|||
|
private MetadataWorkspace workspace;
|
|||
|
private SqlConnection globalConnection;
|
|||
|
private DbCompiledModel compiledModel;
|
|||
|
|
|||
|
public void SetFixture(TransactionFixture data)
|
|||
|
{
|
|||
|
globalConnection = data.GlobalConnection;
|
|||
|
compiledModel = data.CompiledModel;
|
|||
|
|
|||
|
connectionString = string.Format(
|
|||
|
@"metadata=res://EntityFramework.FunctionalTests.Transitional/System.Data.Entity.Objects.TransactionsModel.csdl|res://EntityFramework.FunctionalTests.Transitional/System.Data.Entity.Objects.TransactionsModel.ssdl|res://EntityFramework.FunctionalTests.Transitional/System.Data.Entity.Objects.TransactionsModel.msl;provider=System.Data.SqlClient;provider connection string=""{0}""",
|
|||
|
ModelHelpers.SimpleConnectionString("tempdb"));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_using_DbContext_and_no_transaction_created_by_user_and_connection_is_closed()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionDbContext())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Database.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_using_DbContext_and_no_transaction_created_by_user_and_EntityConnection_is_opened_and_context_owns_connection()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var entityConnection = new EntityConnection(connectionString);
|
|||
|
entityConnection.Open();
|
|||
|
|
|||
|
using (var ctx = CreateTransactionDbContext(entityConnection, contextOwnsConnection: true))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Database.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
Assert.Equal(ConnectionState.Closed, entityConnection.State);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_using_DbContext_and_no_transaction_created_by_user_and_EntityConnection_is_opened_and_context_does_not_own_connection()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var entityConnection = new EntityConnection(connectionString);
|
|||
|
entityConnection.Open();
|
|||
|
using (var ctx = CreateTransactionDbContext(entityConnection, contextOwnsConnection: false))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
Assert.Equal(ConnectionState.Open, entityConnection.StoreConnection.State);
|
|||
|
Assert.Equal(ConnectionState.Open, entityConnection.State);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_using_DbContext_and_no_transaction_created_by_user_and_SqlConnection_is_opened_and_context_owns_connection()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var sqlConnection = new SqlConnection(globalConnection.ConnectionString);
|
|||
|
sqlConnection.Open();
|
|||
|
|
|||
|
using (var ctx = CreateTransactionDbContext(sqlConnection, compiledModel, contextOwnsConnection: true))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Database.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
Assert.Equal(ConnectionState.Closed, sqlConnection.State);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_using_DbContext_and_no_transaction_created_by_user_and_SqlConnection_is_opened_and_context_does_not_own_connection()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var sqlConnection = new SqlConnection(globalConnection.ConnectionString);
|
|||
|
sqlConnection.Open();
|
|||
|
|
|||
|
using (var ctx = CreateTransactionDbContext(sqlConnection, compiledModel, contextOwnsConnection: false))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
Assert.Equal(ConnectionState.Open, sqlConnection.State);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_using_DbContext_and_user_creates_transaction_using_TransactionScope_and_EntityConnection_is_closed()
|
|||
|
{
|
|||
|
var entityConnection = new EntityConnection(connectionString);
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionDbContext(entityConnection, true))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_using_DbContext_and_user_creates_transaction_using_TransactionScope_and_SqlConnection_is_closed()
|
|||
|
{
|
|||
|
var connection = new SqlConnection(globalConnection.ConnectionString);
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionDbContext(connection, compiledModel, contextOwnsConnection: true))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_using_DbContext_and_user_creates_transaction_using_TransactionScope_and_EntityConnection_is_opened_inside_transaction_scope()
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
connection.Open();
|
|||
|
using (var ctx = CreateTransactionDbContext(connection, contextOwnsConnection: true))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
// Fails for now - MSDTC on server 'MAUMARDEV\SQLEXPRESS' is unavailable. See Issue #771 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_implicit_transaction_is_not_created_when_using_DbContext_and_user_creates_transaction_using_TransactionScope_and_connection_is_opened_inside_transaction_scope()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new SqlConnection(globalConnection.ConnectionString);
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// connection.Open();
|
|||
|
//// using (var ctx = CreateTransactionDbContext(connection, compiledModel, contextOwnsConnection: true))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // "Transaction not commited. No entities should be saved to the database."
|
|||
|
//// Assert.Equal(0, LogEntriesCount());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
// What should be the correct behavior for this? See Issue #777 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_implicit_transaction_is_not_created_when_using_DbContext_and_user_creates_transaction_using_TransactionScope_and_EntityConnection_is_opened_outside_transaction_scope()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var entityConnection = new EntityConnection(connectionString);
|
|||
|
//// entityConnection.Open();
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// using (var ctx = CreateTransactionDbContext(entityConnection, contextOwnsConnection: true))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // "Transaction not commited. No entities should be saved to the database."
|
|||
|
//// Assert.Equal(0, LogEntriesCount());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
// Fails for now - MSDTC on server 'MAUMARDEV\SQLEXPRESS' is unavailable. See Issue #771 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_implicit_transaction_is_not_created_when_using_DbContext_and_user_creates_transaction_using_TransactionScope_and_connection_is_opened_outside_transaction_scope()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new SqlConnection(globalConnection.ConnectionString);
|
|||
|
|
|||
|
//// connection.Open();
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// using (var ctx = CreateTransactionDbContext(connection, compiledModel, contextOwnsConnection: true))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // "Transaction not commited. No entities should be saved to the database."
|
|||
|
//// Assert.Equal(0, LogEntriesCount());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_no_transaction_created_by_user_and_connection_is_closed()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_no_transaction_created_by_user_and_connection_is_created_outside_context_and_is_closed()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount(connection));
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_no_transaction_created_by_user_and_connection_is_open()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_created_when_no_transaction_created_by_user_and_connection_is_created_outside_context_and_is_open()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
connection.Open();
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
|
|||
|
// Implicit transaction committed. 1 entity expected.
|
|||
|
Assert.Equal(1, LogEntriesCount(connection));
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_is_closed()
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_context_and_is_closed()
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_context_and_is_closed_plus_AdoNet_calls()
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
AddLogEntryUsingAdoNet((SqlConnection)connection.StoreConnection);
|
|||
|
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_transaction_scope_and_is_closed()
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_transaction_scope_and_is_closed_plus_AdoNet_calls()
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
AddLogEntryUsingAdoNet((SqlConnection)connection.StoreConnection);
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_only_one_transaction_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_transaction_scope_and_is_closed_plus_AdoNet_calls_and_transaction_is_completed()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
AddLogEntryUsingAdoNet((SqlConnection)connection.StoreConnection);
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
transactionScope.Complete();
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// verify that there are two entries (one for AdoNet and one for EF, but only one transaction
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
var transactionCountEntries = ctx.LogEntries.Select(e => e.TransactionCount).OrderBy(c => c).ToList();
|
|||
|
Assert.Equal(2, transactionCountEntries.Count());
|
|||
|
Assert.Equal(-1, transactionCountEntries[0]);
|
|||
|
Assert.Equal(1, transactionCountEntries[1]);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_is_open()
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_context_and_is_open()
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
var connection = new EntityConnection(connectionString);
|
|||
|
connection.Open();
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
// What should be the correct behavior for this? See Issue #777 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_and_opened_outside_transaction_scope()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new EntityConnection(connectionString);
|
|||
|
//// connection.Open();
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// using (var ctx = CreateTransactionContext(connection))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // Transaction not commited. No entities should be saved to the database.
|
|||
|
//// Assert.Equal(0, CreateTransactionContext().LogEntries.Count());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
// What should be the correct behavior for this? See Issue #777 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_and_opened_outside_transaction_scope_plus_AdoNet_calls()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new EntityConnection(connectionString);
|
|||
|
//// connection.Open();
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// // need to enlist store connection so that AdoNet calls execute inside a transaction
|
|||
|
//// connection.StoreConnection.EnlistTransaction(Transaction.Current);
|
|||
|
//// AddLogEntryUsingAdoNet((SqlConnection)connection.StoreConnection);
|
|||
|
//// using (var ctx = CreateTransactionContext(connection))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // Transaction not commited. No entities should be saved to the database.
|
|||
|
//// Assert.Equal(0, LogEntriesCount());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
// What should be the correct behavior for this? See Issue #777 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_only_one_transaction_created_when_user_creates_transaction_using_TransactionScope_and_connection_created_outside_transaction_scope_and_is_open_plus_AdoNet_calls_and_transaction_is_completed()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new EntityConnection(connectionString);
|
|||
|
//// connection.Open();
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// // need to enlist store connection so that AdoNet calls execute inside a transaction
|
|||
|
//// connection.StoreConnection.EnlistTransaction(Transaction.Current);
|
|||
|
//// AddLogEntryUsingAdoNet((SqlConnection)connection.StoreConnection);
|
|||
|
//// using (var ctx = CreateTransactionContext(connection))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// transactionScope.Complete();
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // verify that there are two entries (one for AdoNet and one for EF, but only one transaction
|
|||
|
//// using (var ctx = CreateTransactionContext())
|
|||
|
//// {
|
|||
|
//// var transactionCountEntries = ctx.LogEntries.Select(e => e.TransactionCount).OrderBy(c => c).ToList();
|
|||
|
//// Assert.Equal(2, transactionCountEntries.Count());
|
|||
|
//// Assert.Equal(-1, transactionCountEntries[0]);
|
|||
|
//// Assert.Equal(1, transactionCountEntries[1]);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_executing_multiple_operations_in_the_same_TransactionScope()
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
|
|||
|
transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
// What should be the correct behavior for this? See Issue #777 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_implicit_transaction_is_not_created_when_executing_multiple_operations_in_the_same_TransactionScope_and_connection_is_opened_outside_transaction_scope()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new EntityConnection(connectionString);
|
|||
|
//// connection.Open();
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// using (var ctx = CreateTransactionContext(connection))
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
|
|||
|
//// transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
//// Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // "Transaction not commited. No entities should be saved to the database."
|
|||
|
//// Assert.Equal(0, LogEntriesCount());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_transaction_explicitly_using_CommittableTransaction()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_enlists_TransactionScope()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(Transaction.Current);
|
|||
|
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_creates_ambient_CommittableTransaction()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction(new TimeSpan(1, 0, 0)))
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
Transaction.Current = committableTransaction;
|
|||
|
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
|
|||
|
Transaction.Current = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Transaction not commited. No entities should be saved to the database.
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_is_not_created_when_user_starts_DbTransaction_explicitly()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var dbTransaction = ctx.Connection.BeginTransaction())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_DbTransaction_cannot_be_mixed_with_TransactionScope()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var dbTransaction = ctx.Connection.BeginTransaction())
|
|||
|
{
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = new TransactionLogEntry();
|
|||
|
ctx.AddObject("Entities.TransactionLog", transactionLogEntry);
|
|||
|
|
|||
|
Assert.Equal(
|
|||
|
Strings.EntityClient_ProviderSpecificError("EnlistTransaction"),
|
|||
|
Assert.Throws<EntityException>(() => ctx.SaveChanges()).Message);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_DbTransaction_cannot_be_mixed_with_CommittableTransaction()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var dbTransaction = ctx.Connection.BeginTransaction())
|
|||
|
{
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
Assert.Equal(
|
|||
|
Strings.EntityClient_ProviderSpecificError("EnlistTransaction"),
|
|||
|
Assert.Throws<EntityException>(() => ctx.Connection.EnlistTransaction(committableTransaction)).Message);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_using_TransactionScope_with_DbTransaction_results_in_nested_transaction_and_implicit_transaction_not_created()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (ctx.Connection.BeginTransaction())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(2, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
// What should be the correct behavior for this? See Issue #777 for details
|
|||
|
////[Fact]
|
|||
|
////public void Verify_using_TransactionScope_with_DbTransaction_results_in_nested_transaction_and_implicit_transaction_not_created_when_using_context_with_already_opened_connection()
|
|||
|
////{
|
|||
|
//// try
|
|||
|
//// {
|
|||
|
//// var connection = new EntityConnection(connectionString);
|
|||
|
//// connection.Open();
|
|||
|
//// using (var ctx = CreateTransactionContext(connection))
|
|||
|
//// {
|
|||
|
//// using (var transactionScope = new TransactionScope())
|
|||
|
//// {
|
|||
|
//// // need to enlist connection here, otherwise test will fail
|
|||
|
//// ctx.Connection.EnlistTransaction(Transaction.Current);
|
|||
|
//// using (var dbTransaction = ctx.Connection.BeginTransaction())
|
|||
|
//// {
|
|||
|
//// var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
//// Assert.Equal(2, transactionLogEntry.TransactionCount);
|
|||
|
//// Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
//// }
|
|||
|
|
|||
|
//// // "Transaction not commited. No entities should be saved to the database."
|
|||
|
//// Assert.Equal(0, LogEntriesCount());
|
|||
|
//// }
|
|||
|
//// finally
|
|||
|
//// {
|
|||
|
//// ResetTables();
|
|||
|
//// }
|
|||
|
////}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_using_CommittableTransaction_with_DbTransaction_results_in_nested_transaction_and_implicit_transaction_not_created()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
|
|||
|
using (ctx.Connection.BeginTransaction())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(2, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_TransactionScope_cannot_be_mixed_with_CommitableTransaction()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
Assert.Equal(
|
|||
|
Strings.EntityClient_ProviderSpecificError("EnlistTransaction"),
|
|||
|
Assert.Throws<EntityException>(() => ctx.Connection.EnlistTransaction(committableTransaction)).Message);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_CommitableTransaction_cannot_be_mixed_with_TransactionScope()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = new TransactionLogEntry();
|
|||
|
ctx.AddObject("Entities.TransactionLog", transactionLogEntry);
|
|||
|
|
|||
|
Assert.Equal(
|
|||
|
Strings.EntityClient_ProviderSpecificError("EnlistTransaction"),
|
|||
|
Assert.Throws<EntityException>(() => ctx.SaveChanges()).Message);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_created_when_transaction_from_previous_operation_disposed_and_connection_opened()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
|
|||
|
AddLogEntryToDatabase(ctx);
|
|||
|
|
|||
|
// "Implicit transaction committed. 1 entity expected."
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_no_implicit_transaction_created_when_if_enlisted_in_explicit_transaction_after_transaction_from_previous_operation_disposed()
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_implicit_transaction_created_when_transaction_from_previous_operation_disposed_and_connection_closed()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
using (var transactionScope = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
|
|||
|
var newTransactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
|
|||
|
// "Implicit transaction committed. 1 entity expected."
|
|||
|
Assert.Equal(1, LogEntriesCount());
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_explicit_transaction_cleared_after_disposing()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, CreateTransactionContext().LogEntries.Count());
|
|||
|
|
|||
|
var newTransactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
|
|||
|
// "Implicit transaction committed. 1 entity expected."
|
|||
|
Assert.Equal(1, CreateTransactionContext().LogEntries.Count());
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_no_implicit_transaction_created_when_transactions_change_between_requests()
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
|
|||
|
using (var transaction1 = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
using (var transaction2 = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_no_implicit_transaction_created_when_enlisting_in_transaction_between_requests()
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
|
|||
|
using (var transaction1 = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Open, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_no_implicit_transaction_created_when_transaction_change_and_connection_is_closed_between_requests()
|
|||
|
{
|
|||
|
using (var ctx = CreateObjectContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
|
|||
|
using (var trx = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
ctx.Connection.Close();
|
|||
|
}
|
|||
|
|
|||
|
using (var trx = new TransactionScope())
|
|||
|
{
|
|||
|
var transactionLogEntry = AddLogEntryToDatabase(ctx);
|
|||
|
Assert.Equal(1, transactionLogEntry.TransactionCount);
|
|||
|
Assert.Equal(ConnectionState.Closed, ctx.Connection.State);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// "Transaction not commited. No entities should be saved to the database."
|
|||
|
Assert.Equal(0, LogEntriesCount());
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_cannot_enlist_in_more_than_one_active_transaction_on_the_same_opened_connection()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
using (var newCommittableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
|
|||
|
Assert.Equal(
|
|||
|
Strings.EntityClient_ProviderSpecificError("EnlistTransaction"),
|
|||
|
Assert.Throws<EntityException>(() => ctx.Connection.EnlistTransaction(newCommittableTransaction)).Message);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Verify_closing_connection_invalidates_explicit_transaction()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var committableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(committableTransaction);
|
|||
|
ctx.Connection.Close();
|
|||
|
|
|||
|
ctx.Connection.Open();
|
|||
|
using (var newCommittableTransaction = new CommittableTransaction())
|
|||
|
{
|
|||
|
ctx.Connection.EnlistTransaction(newCommittableTransaction);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
ResetTables();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private TransactionContext CreateTransactionContext()
|
|||
|
{
|
|||
|
var ctx = new TransactionContext(connectionString);
|
|||
|
ctx.MetadataWorkspace.LoadFromAssembly(Assembly.GetExecutingAssembly());
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private TransactionContext CreateTransactionContext(EntityConnection connection)
|
|||
|
{
|
|||
|
var ctx = new TransactionContext(connection);
|
|||
|
ctx.MetadataWorkspace.LoadFromAssembly(Assembly.GetExecutingAssembly());
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private ObjectContext CreateObjectContext()
|
|||
|
{
|
|||
|
var ctx = new ObjectContext(connectionString);
|
|||
|
ctx.MetadataWorkspace.LoadFromAssembly(Assembly.GetExecutingAssembly());
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private ObjectContext CreateObjectContext(EntityConnection connection)
|
|||
|
{
|
|||
|
var ctx = new ObjectContext(connection);
|
|||
|
ctx.MetadataWorkspace.LoadFromAssembly(Assembly.GetExecutingAssembly());
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private TransactionDbContext CreateTransactionDbContext()
|
|||
|
{
|
|||
|
var ctx = new TransactionDbContext(connectionString);
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private TransactionDbContext CreateTransactionDbContext(EntityConnection connection, bool contextOwnsConnection)
|
|||
|
{
|
|||
|
var ctx = new TransactionDbContext(connection, contextOwnsConnection);
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private TransactionDbContext CreateTransactionDbContext(SqlConnection connection, DbCompiledModel compiledModel, bool contextOwnsConnection)
|
|||
|
{
|
|||
|
var ctx = new TransactionDbContext(connection, compiledModel, contextOwnsConnection);
|
|||
|
|
|||
|
return ctx;
|
|||
|
}
|
|||
|
|
|||
|
private int LogEntriesCount()
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext())
|
|||
|
{
|
|||
|
return ctx.LogEntries.Count();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private int LogEntriesCount(EntityConnection connection)
|
|||
|
{
|
|||
|
using (var ctx = CreateTransactionContext(connection))
|
|||
|
{
|
|||
|
return ctx.LogEntries.Count();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private TransactionLogEntry AddLogEntryToDatabase(ObjectContext ctx)
|
|||
|
{
|
|||
|
var transactionLogEntry = new TransactionLogEntry();
|
|||
|
ctx.AddObject("Entities.TransactionLog", transactionLogEntry);
|
|||
|
ctx.SaveChanges();
|
|||
|
|
|||
|
return transactionLogEntry;
|
|||
|
}
|
|||
|
|
|||
|
private TransactionLogEntry AddLogEntryToDatabase(TransactionDbContext ctx)
|
|||
|
{
|
|||
|
var transactionLogEntry = new TransactionLogEntry();
|
|||
|
ctx.LogEntries.Add(transactionLogEntry);
|
|||
|
ctx.SaveChanges();
|
|||
|
|
|||
|
return transactionLogEntry;
|
|||
|
}
|
|||
|
|
|||
|
private void AddLogEntryUsingAdoNet(SqlConnection connection)
|
|||
|
{
|
|||
|
bool shouldCloseConnection = false;
|
|||
|
if (connection.State == ConnectionState.Closed)
|
|||
|
{
|
|||
|
connection.Open();
|
|||
|
shouldCloseConnection = true;
|
|||
|
}
|
|||
|
|
|||
|
var command = new SqlCommand();
|
|||
|
command.Connection = connection;
|
|||
|
command.CommandText = @"INSERT INTO ##TransactionLog Values(-1)";
|
|||
|
command.ExecuteNonQuery();
|
|||
|
|
|||
|
if (shouldCloseConnection)
|
|||
|
{
|
|||
|
connection.Close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes all entries from the tables used for tests. Must be called from tests that are committing transactions
|
|||
|
/// as each test expects that the db will be initially clean.
|
|||
|
/// </summary>
|
|||
|
private void ResetTables()
|
|||
|
{
|
|||
|
new SqlCommand("DELETE FROM ##TransactionLog", globalConnection).ExecuteNonQuery();
|
|||
|
Assert.Equal(0, CreateTransactionContext().LogEntries.Count());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public class TransactionFixture : FunctionalTestBase, IDisposable
|
|||
|
{
|
|||
|
public SqlConnection GlobalConnection { get; private set; }
|
|||
|
public DbCompiledModel CompiledModel { get; private set; }
|
|||
|
|
|||
|
public TransactionFixture()
|
|||
|
{
|
|||
|
if (GlobalConnection != null)
|
|||
|
{
|
|||
|
throw new InvalidOperationException("Database is still in use and cannot be initialized.");
|
|||
|
}
|
|||
|
|
|||
|
// we are using tempdb and SQLExpress instance so we don't want this to be configurable
|
|||
|
GlobalConnection = new SqlConnection("Data Source=.\\SQLEXPRESS;Initial Catalog=tempdb;Integrated Security=SSPI;");
|
|||
|
GlobalConnection.Open();
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
new SqlCommand(
|
|||
|
@"
|
|||
|
CREATE TABLE [dbo].[##TransactionLog](
|
|||
|
[ID] [int] IDENTITY(1,1) NOT NULL,
|
|||
|
[TransactionCount] [int] NOT NULL,
|
|||
|
CONSTRAINT [PK_TransactionLog] PRIMARY KEY CLUSTERED
|
|||
|
(
|
|||
|
[ID] ASC
|
|||
|
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
|
|||
|
) ON [PRIMARY]", GlobalConnection).ExecuteNonQuery();
|
|||
|
|
|||
|
new SqlCommand(
|
|||
|
@"
|
|||
|
CREATE PROCEDURE [dbo].[##CreateTransactionLogEntry]
|
|||
|
AS
|
|||
|
BEGIN
|
|||
|
DECLARE @tranCount AS int = @@TRANCOUNT -- assigning to a variable prevents from counting an implicit transaction created for insert
|
|||
|
INSERT INTO ##TransactionLog Values(@tranCount)
|
|||
|
SELECT ID, TransactionCount
|
|||
|
FROM ##TransactionLog
|
|||
|
WHERE ID = SCOPE_IDENTITY()
|
|||
|
END", GlobalConnection).ExecuteNonQuery();
|
|||
|
|
|||
|
CompiledModel = BuildCompiledModel();
|
|||
|
}
|
|||
|
catch
|
|||
|
{
|
|||
|
CleanupDatabase();
|
|||
|
|
|||
|
throw;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private DbCompiledModel BuildCompiledModel()
|
|||
|
{
|
|||
|
// hack - we have to create context that uses StoredProcedure to for inserts. DbModelBuilder does not support that.
|
|||
|
// Instead, we use reflection to inject MetadataWorkspace created using csdl/ssdl/msl into a DbCompiledModel
|
|||
|
var connectionString = string.Format(
|
|||
|
@"metadata=res://EntityFramework.FunctionalTests.Transitional/System.Data.Entity.Objects.TransactionsModel.csdl|res://EntityFramework.FunctionalTests.Transitional/System.Data.Entity.Objects.TransactionsModel.ssdl|res://EntityFramework.FunctionalTests.Transitional/System.Data.Entity.Objects.TransactionsModel.msl;provider=System.Data.SqlClient;provider connection string=""{0}""",
|
|||
|
ModelHelpers.SimpleConnectionString("tempdb"));
|
|||
|
|
|||
|
var ctx = new TransactionDbContext(connectionString);
|
|||
|
ctx.LogEntries.Count();
|
|||
|
var objectContext = ((IObjectContextAdapter)ctx).ObjectContext;
|
|||
|
|
|||
|
DbModelBuilder builder = new DbModelBuilder();
|
|||
|
builder.Entity(typeof(TransactionLogEntry)).ToTable("##TransactionLog");
|
|||
|
builder.HasDefaultSchema("tempdbModel");
|
|||
|
builder.Conventions.Add(new ModelContainerConvention("Entities"));
|
|||
|
|
|||
|
|
|||
|
var model = builder.Build(ctx.Database.Connection);
|
|||
|
var compiledModel = model.Compile();
|
|||
|
var cachedMetadataWorkspaceFieldInfo = compiledModel.GetType().GetField("_workspace", BindingFlags.Instance | BindingFlags.NonPublic);
|
|||
|
var cachedMetadataWorkspace = cachedMetadataWorkspaceFieldInfo.GetValue(compiledModel);
|
|||
|
var metadataWorkspaceFieldInfo = cachedMetadataWorkspace.GetType().GetField("_metadataWorkspace", BindingFlags.Instance | BindingFlags.NonPublic);
|
|||
|
metadataWorkspaceFieldInfo.SetValue(cachedMetadataWorkspace, objectContext.MetadataWorkspace);
|
|||
|
|
|||
|
return compiledModel;
|
|||
|
}
|
|||
|
|
|||
|
private void CleanupDatabase()
|
|||
|
{
|
|||
|
Debug.Assert(GlobalConnection != null);
|
|||
|
|
|||
|
GlobalConnection.Close();
|
|||
|
GlobalConnection.Dispose();
|
|||
|
GlobalConnection = null;
|
|||
|
}
|
|||
|
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
CleanupDatabase();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|