Although the way to reproduce it is a bit convoluted, it used to work correctly with the older build, and it works properly with MSSQL and SQLite.
Exception:
Code: Select all
Unhandled exception. System.InvalidOperationException: The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.
at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction..ctor(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, IDiagnosticsLogger`1 logger, Boolean transactionOwned, ISqlGenerationHelper sqlGenerationHelper)
at Devart.Common.Entity.c0..ctor(IRelationalConnection A_0, DbTransaction A_1, Guid A_2, IDiagnosticsLogger`1 A_3, Boolean A_4, ISqlGenerationHelper A_5)
at Devart.Data.Oracle.Entity.at..ctor(IRelationalConnection A_0, DbTransaction A_1, Guid A_2, IDiagnosticsLogger`1 A_3, Boolean A_4, ISqlGenerationHelper A_5)
at Devart.Data.Oracle.Entity.au.Create(IRelationalConnection connection, DbTransaction transaction, Guid transactionId, IDiagnosticsLogger`1 logger, Boolean transactionOwned)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.CreateRelationalTransaction(DbTransaction transaction, Guid transactionId, Boolean transactionOwned)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.UseTransaction(DbTransaction transaction, Guid transactionId)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.UseTransaction(DatabaseFacade databaseFacade, DbTransaction transaction, Guid transactionId)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.UseTransaction(DatabaseFacade databaseFacade, DbTransaction transaction)
Code: Select all
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Data.Common;
namespace efcore_demo
{
class Program
{
static void Main(string[] args)
{
test_devart_fail_reuse_transaction_in_new_scope();
Console.WriteLine("Hello World!");
}
private static void test_devart_fail_reuse_transaction_in_new_scope()
{
var sp = new devart_fail_reuse_transaction_in_new_scope.ServiceProviderWrapper(devart_fail_reuse_transaction_in_new_scope.DbType.Oracle);
var seeder = new devart_fail_reuse_transaction_in_new_scope.Seeder(sp);
seeder.Seed();
var logic = new devart_fail_reuse_transaction_in_new_scope.Logic(sp);
logic.ExecuteQuery();
}
}
internal class devart_fail_reuse_transaction_in_new_scope
{
public enum DbType
{
Oracle,
SQLite,
MSSQL
}
public class ServiceProviderWrapper
{
private readonly IServiceProvider _serviceProvider;
public IServiceProvider ServiceProvider => _serviceProvider;
public ServiceProviderWrapper(DbType dbType)
{
_serviceProvider = new ServiceCollection()
.AddLogging(b => b.AddConsole())
.AddScoped<DbConnectionHolder>()
.AddDbContext<MyDbContext>((sp, options) =>
{
var conn = sp.GetService<DbConnectionHolder>();
options = options.EnableSensitiveDataLogging();
if (dbType == DbType.Oracle)
{
if (conn?.DbConnection != null)
{
options.UseOracle(conn.DbConnection);
System.Diagnostics.Debug.WriteLine("Using Oracle DbConnection: " + conn.DbConnection.ConnectionString);
}
else
{
options.UseOracle(OracleConstants.ORACLE_CONNECTION);
System.Diagnostics.Debug.WriteLine("Using Oracle hardcoded string");
}
}
else if (dbType == DbType.SQLite)
{
if (conn?.DbConnection != null)
{
options.UseSqlite(conn.DbConnection);
System.Diagnostics.Debug.WriteLine("Using SQLite DbConnection: " + conn.DbConnection.ConnectionString);
}
else
{
options.UseSqlite("Data Source=efcoredemo.db");
System.Diagnostics.Debug.WriteLine("Using SQLite hardcoded string");
}
}
else if (dbType == DbType.MSSQL)
{
if (conn?.DbConnection != null)
{
options.UseSqlServer(conn.DbConnection);
System.Diagnostics.Debug.WriteLine("Using MSSQL DbConnection: " + conn.DbConnection.ConnectionString);
}
else
{
options.UseSqlServer("Server=(localdb)\\ProjectsV13;Database=devart_fail_reuse_transaction_in_new_scope;Trusted_Connection=True;MultipleActiveResultSets=true");
System.Diagnostics.Debug.WriteLine("Using MSSQL hardcoded string");
}
}
}, ServiceLifetime.Scoped)
.BuildServiceProvider();
}
}
public class Logic
{
private readonly ServiceProviderWrapper _serviceProviderWrapper;
public Logic(ServiceProviderWrapper serviceProviderWrapper)
{
_serviceProviderWrapper = serviceProviderWrapper;
}
public void ExecuteQuery()
{
using (var parentScope = _serviceProviderWrapper.ServiceProvider.CreateScope())
{
var parentScopeDbContext = parentScope.ServiceProvider.GetRequiredService<MyDbContext>();
var transaction = parentScopeDbContext.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
using (var scope = parentScope.ServiceProvider.CreateScope())
{
var dbTransaction = parentScopeDbContext.Database.CurrentTransaction?.GetDbTransaction();
if (dbTransaction != null)
{
var conn = scope.ServiceProvider.GetRequiredService<DbConnectionHolder>();
conn.DbConnection = dbTransaction.Connection;
var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
dbContext.Database.UseTransaction(dbTransaction);
}
}
}
}
}
internal class DbConnectionHolder
{
public DbConnection DbConnection { get; set; }
}
public class Seeder
{
private readonly ServiceProviderWrapper _serviceProviderWrapper;
public Seeder(ServiceProviderWrapper serviceProviderWrapper)
{
_serviceProviderWrapper = serviceProviderWrapper;
}
public void Seed()
{
using (var scope = _serviceProviderWrapper.ServiceProvider.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<MyDbContext>();
var rdc = dbContext.Database.GetService<IRelationalDatabaseCreator>();
if (rdc.Exists())
rdc.EnsureDeleted();
rdc.EnsureCreated();
if (!dbContext.Items.Any())
{
for (int i = 0; i < 10; i++)
{
var rand = new Random();
int cat = rand.Next() % 3;
int colors = rand.Next() % 4;
var item = new Item { CategoryId = cat, Name = $"Item {i}" };
for (int c = 0; c <= colors; c++)
{
item.AddColor(c);
}
dbContext.Items.Add(item);
}
}
if (!dbContext.Categories.Any())
{
for (int i = 0; i < 3; i++)
{
var category = new Category { Name = $"Category {i}" };
dbContext.Categories.Add(category);
}
}
dbContext.SaveChanges();
}
}
}
public class Dto
{
public int Id { get; set; }
public string Name { get; set; }
public string Colors { get; set; }
}
public class Item
{
private readonly List<Color> _colors = new List<Color>();
public IReadOnlyCollection<Color> Colors => _colors;
public int Id { get; set; }
public int CategoryId { get; set; }
public string Name { get; set; }
internal void AddColor(int i)
{
var color = new Color { Name = $"Color {i}" };
_colors.Add(color);
}
}
public class Color
{
public int ItemId { get; private set; }
public virtual Item Item { get; private set; }
public int Id { get; set; }
public string Name { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDbContext : DbContext
{
public static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
public DbSet<Item> Items { get; set; }
public DbSet<Color> Colors { get; set; }
public DbSet<Category> Categories { get; set; }
public MyDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new ItemConfiguration());
modelBuilder.ApplyConfiguration(new CategoryConfiguration());
}
}
public class ItemConfiguration : IEntityTypeConfiguration<Item>
{
public void Configure(EntityTypeBuilder<Item> builder)
{
builder.ToTable("Item");
builder.HasKey(o => o.Id);
builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
builder.Property(t => t.CategoryId).HasColumnName("categoryId");
builder.Property(t => t.Name).HasColumnName("name");
}
}
public class ColorConfiguration : IEntityTypeConfiguration<Color>
{
public void Configure(EntityTypeBuilder<Color> builder)
{
builder.ToTable("Color");
builder.HasKey(o => o.Id);
builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
builder.Property(t => t.Name).HasColumnName("name");
builder.HasOne(s => s.Item)
.WithMany(s => s.Colors)
.HasForeignKey(s => s.ItemId);
}
}
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
{
public void Configure(EntityTypeBuilder<Category> builder)
{
builder.ToTable("Category");
builder.HasKey(o => o.Id);
builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
builder.Property(t => t.Name).HasColumnName("name");
}
}
}
}