diff --git a/Volo.Abp.sln b/Volo.Abp.sln index a7eb296773..ac4f6f1738 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -138,8 +138,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Identity.Domain", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Identity.Domain.Shared", "src\Volo.Abp.Identity.Domain.Shared\Volo.Abp.Identity.Domain.Shared.csproj", "{DF676F73-3FC9-46CE-909A-2D75E19982AD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Tests", "test\Volo.Abp.EntityFrameworkCore.Tests\Volo.Abp.EntityFrameworkCore.Tests.csproj", "{3AF7C7F5-6513-47D4-8DD0-6E1AF14568D8}" -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleConsoleDemo", "test\SimpleConsoleDemo\SimpleConsoleDemo.csproj", "{2B48CF90-DBDB-469F-941C-5B5AECEEACE0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Tests", "test\Volo.Abp.EntityFrameworkCore.Tests\Volo.Abp.EntityFrameworkCore.Tests.csproj", "{3AF7C7F5-6513-47D4-8DD0-6E1AF14568D8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleConsoleDemo", "test\SimpleConsoleDemo\SimpleConsoleDemo.csproj", "{2B48CF90-DBDB-469F-941C-5B5AECEEACE0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Tests.SecondContext", "test\Volo.Abp.EntityFrameworkCore.Tests.SecondContext\Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj", "{127FC2BF-DC40-4370-B845-16088328264C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -359,6 +362,10 @@ Global {2B48CF90-DBDB-469F-941C-5B5AECEEACE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B48CF90-DBDB-469F-941C-5B5AECEEACE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B48CF90-DBDB-469F-941C-5B5AECEEACE0}.Release|Any CPU.Build.0 = Release|Any CPU + {127FC2BF-DC40-4370-B845-16088328264C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {127FC2BF-DC40-4370-B845-16088328264C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {127FC2BF-DC40-4370-B845-16088328264C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {127FC2BF-DC40-4370-B845-16088328264C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -426,6 +433,7 @@ Global {DF676F73-3FC9-46CE-909A-2D75E19982AD} = {1895A5C9-50D4-4568-9A3A-14657E615A5E} {3AF7C7F5-6513-47D4-8DD0-6E1AF14568D8} = {37087D1B-3693-4E96-983D-A69F210BDE53} {2B48CF90-DBDB-469F-941C-5B5AECEEACE0} = {37087D1B-3693-4E96-983D-A69F210BDE53} + {127FC2BF-DC40-4370-B845-16088328264C} = {37087D1B-3693-4E96-983D-A69F210BDE53} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/src/Volo.Abp.TestBase/AbpTestBaseWithServiceProvider.cs b/src/Volo.Abp.TestBase/AbpTestBaseWithServiceProvider.cs index 4f8eb7641d..6c0ecc066d 100644 --- a/src/Volo.Abp.TestBase/AbpTestBaseWithServiceProvider.cs +++ b/src/Volo.Abp.TestBase/AbpTestBaseWithServiceProvider.cs @@ -10,12 +10,17 @@ namespace Volo.Abp.TestBase protected abstract IServiceProvider ServiceProvider { get; } protected virtual void WithUnitOfWork(Action action) + { + WithUnitOfWork(new UnitOfWorkOptions(), action); + } + + protected virtual void WithUnitOfWork(UnitOfWorkOptions options, Action action) { using (var scope = ServiceProvider.CreateScope()) { var uowManager = scope.ServiceProvider.GetRequiredService(); - using (var uow = uowManager.Begin()) + using (var uow = uowManager.Begin(options)) { action(); @@ -24,13 +29,18 @@ namespace Volo.Abp.TestBase } } - protected virtual async Task WithUnitOfWorkAsync(Func action) + protected virtual Task WithUnitOfWorkAsync(Func func) + { + return WithUnitOfWorkAsync(new UnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(UnitOfWorkOptions options, Func action) { using (var scope = ServiceProvider.CreateScope()) { var uowManager = scope.ServiceProvider.GetRequiredService(); - using (var uow = uowManager.Begin()) + using (var uow = uowManager.Begin(options)) { await action(); @@ -40,12 +50,17 @@ namespace Volo.Abp.TestBase } protected virtual TResult WithUnitOfWork(Func func) + { + return WithUnitOfWork(new UnitOfWorkOptions(), func); + } + + protected virtual TResult WithUnitOfWork(UnitOfWorkOptions options, Func func) { using (var scope = ServiceProvider.CreateScope()) { var uowManager = scope.ServiceProvider.GetRequiredService(); - using (var uow = uowManager.Begin()) + using (var uow = uowManager.Begin(options)) { var result = func(); uow.Complete(); @@ -54,13 +69,18 @@ namespace Volo.Abp.TestBase } } - protected virtual async Task WithUnitOfWorkAsync(Func> func) + protected virtual Task WithUnitOfWorkAsync(Func> func) + { + return WithUnitOfWorkAsync(new UnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(UnitOfWorkOptions options, Func> func) { using (var scope = ServiceProvider.CreateScope()) { var uowManager = scope.ServiceProvider.GetRequiredService(); - using (var uow = uowManager.Begin()) + using (var uow = uowManager.Begin(options)) { var result = await func(); await uow.CompleteAsync(); diff --git a/src/Volo.Abp/Volo/Abp/Uow/ITransactionApi.cs b/src/Volo.Abp/Volo/Abp/Uow/ITransactionApi.cs index 1f6f9aa226..6bd78cc824 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/ITransactionApi.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/ITransactionApi.cs @@ -1,13 +1,12 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Volo.Abp.Uow { - public interface ITransactionApi + public interface ITransactionApi : IDisposable { void Commit(); Task CommitAsync(); - - void Dispose(); } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs index 4a329d7d3a..353ec77911 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs @@ -141,15 +141,7 @@ namespace Volo.Abp.Uow _isRolledback = true; - foreach (var databaseApi in _databaseApis.Values) - { - (databaseApi as ISupportsRollback)?.Rollback(); - } - - foreach (var transactionApi in _transactionApis.Values) - { - (transactionApi as ISupportsRollback)?.Rollback(); - } + RollbackAll(); } public virtual async Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken)) @@ -161,21 +153,7 @@ namespace Volo.Abp.Uow _isRolledback = true; - foreach (var databaseApi in _databaseApis.Values) - { - if (databaseApi is ISupportsRollback) - { - await (databaseApi as ISupportsRollback).RollbackAsync(cancellationToken); - } - } - - foreach (var transactionApi in _transactionApis.Values) - { - if (transactionApi is ISupportsRollback) - { - await (transactionApi as ISupportsRollback).RollbackAsync(cancellationToken); - } - } + await RollbackAllAsync(cancellationToken); } public IDatabaseApi FindDatabaseApi(string key) @@ -256,6 +234,8 @@ namespace Volo.Abp.Uow _isDisposed = true; + DisposeTransactions(); + if (!_isCompleted || _exception != null) { OnFailed(); @@ -264,6 +244,20 @@ namespace Volo.Abp.Uow OnDisposed(); } + private void DisposeTransactions() + { + foreach (var transactionApi in _transactionApis.Values) + { + try + { + transactionApi.Dispose(); + } + catch + { + } + } + } + private void PreventMultipleComplete() { if (_isCompleted) @@ -274,9 +268,53 @@ namespace Volo.Abp.Uow _isCompleted = true; } - public override string ToString() + + protected virtual void RollbackAll() { - return $"[UnitOfWork {Id}]"; + foreach (var databaseApi in _databaseApis.Values) + { + try + { + (databaseApi as ISupportsRollback)?.Rollback(); + } + catch { } + } + + foreach (var transactionApi in _transactionApis.Values) + { + try + { + (transactionApi as ISupportsRollback)?.Rollback(); + } + catch { } + } + } + + protected virtual async Task RollbackAllAsync(CancellationToken cancellationToken) + { + foreach (var databaseApi in _databaseApis.Values) + { + if (databaseApi is ISupportsRollback) + { + try + { + await (databaseApi as ISupportsRollback).RollbackAsync(cancellationToken); + } + catch { } + } + } + + foreach (var transactionApi in _transactionApis.Values) + { + if (transactionApi is ISupportsRollback) + { + try + { + await (transactionApi as ISupportsRollback).RollbackAsync(cancellationToken); + } + catch { } + } + } } protected virtual void CommitTransactions() @@ -294,5 +332,10 @@ namespace Volo.Abp.Uow await transaction.CommitAsync(); } } + + public override string ToString() + { + return $"[UnitOfWork {Id}]"; + } } } \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Class1.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Class1.cs new file mode 100644 index 0000000000..c11584ab78 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace Volo.Abp.EntityFrameworkCore.Tests.SecondContext +{ + public class Class1 + { + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/20170927075606_Initial_Migration.Designer.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/20170927075606_Initial_Migration.Designer.cs new file mode 100644 index 0000000000..6c14d84718 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/20170927075606_Initial_Migration.Designer.cs @@ -0,0 +1,37 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using System; +using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; + +namespace Volo.Abp.EntityFrameworkCore.Tests.SecondContext.Migrations +{ + [DbContext(typeof(SecondDbContext))] + [Migration("20170927075606_Initial_Migration")] + partial class Initial_Migration + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.TestApp.SecondContext.BookInSecondDbContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Books"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/20170927075606_Initial_Migration.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/20170927075606_Initial_Migration.cs new file mode 100644 index 0000000000..a6eeaebd9c --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/20170927075606_Initial_Migration.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Volo.Abp.EntityFrameworkCore.Tests.SecondContext.Migrations +{ + public partial class Initial_Migration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Books", + columns: table => new + { + Id = table.Column(type: "BLOB", nullable: false), + Name = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Books", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Books"); + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/SecondDbContextModelSnapshot.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/SecondDbContextModelSnapshot.cs new file mode 100644 index 0000000000..7db0171e28 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Migrations/SecondDbContextModelSnapshot.cs @@ -0,0 +1,36 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using System; +using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; + +namespace Volo.Abp.EntityFrameworkCore.Tests.SecondContext.Migrations +{ + [DbContext(typeof(SecondDbContext))] + partial class SecondDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Volo.Abp.EntityFrameworkCore.TestApp.SecondContext.BookInSecondDbContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("Books"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj new file mode 100644 index 0000000000..685bded7b0 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj @@ -0,0 +1,32 @@ + + + + netcoreapp2.0 + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + Volo.Abp.EntityFrameworkCore.Tests.SecondContext + Volo.Abp.EntityFrameworkCore.Tests.SecondContext + true + true + false + false + false + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/AbpEfCoreTestSecondContextModule.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/AbpEfCoreTestSecondContextModule.cs new file mode 100644 index 0000000000..6e7f24139a --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/AbpEfCoreTestSecondContextModule.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext +{ + [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + public class AbpEfCoreTestSecondContextModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + scope.ServiceProvider + .GetRequiredService() + .Build(); + } + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/BookInSecondDbContext.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/BookInSecondDbContext.cs new file mode 100644 index 0000000000..61bc73b5ec --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/BookInSecondDbContext.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext +{ + public class BookInSecondDbContext : AggregateRoot + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondContextTestDataBuilder.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondContextTestDataBuilder.cs new file mode 100644 index 0000000000..1beb893d17 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondContextTestDataBuilder.cs @@ -0,0 +1,27 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; + +namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext +{ + public class SecondContextTestDataBuilder : ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; + + public SecondContextTestDataBuilder(IRepository bookRepository, IGuidGenerator guidGenerator) + { + _bookRepository = bookRepository; + _guidGenerator = guidGenerator; + } + + public void Build() + { + _bookRepository.Insert(new BookInSecondDbContext + { + Id = _guidGenerator.Create(), + Name = "TestBook1" + }); + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondDbContext.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondDbContext.cs new file mode 100644 index 0000000000..40c05832a4 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext +{ + public class SecondDbContext : AbpDbContext + { + public DbSet Books { get; set; } + + public SecondDbContext(DbContextOptions options) + : base(options) + { + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondDbContextFactory.cs b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondDbContextFactory.cs new file mode 100644 index 0000000000..e7ee6375e6 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests.SecondContext/Volo/Abp/EntityFrameworkCore/TestApp/SecondContext/SecondDbContextFactory.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext +{ + public class SecondDbContextFactory : IDesignTimeDbContextFactory + { + public SecondDbContext CreateDbContext(string[] args) + { + var builder = new DbContextOptionsBuilder(); + builder.UseSqlite(@"Data Source=d:\temp\VoloAbpEfCoreTestModule.db;"); + return new SecondDbContext(builder.Options); + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20170927080244_Initial_Migration.Designer.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20170927080244_Initial_Migration.Designer.cs new file mode 100644 index 0000000000..594586a2fc --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20170927080244_Initial_Migration.Designer.cs @@ -0,0 +1,66 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using System; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.TestApp.EntityFrameworkCore; + +namespace Volo.Abp.EntityFrameworkCore.Tests.Migrations +{ + [DbContext(typeof(TestAppDbContext))] + [Migration("20170927080244_Initial_Migration")] + partial class Initial_Migration + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Volo.Abp.TestApp.Domain.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Age"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("People"); + }); + + modelBuilder.Entity("Volo.Abp.TestApp.Domain.Phone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Number"); + + b.Property("PersonId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("PersonId"); + + b.ToTable("AppPhones"); + }); + + modelBuilder.Entity("Volo.Abp.TestApp.Domain.Phone", b => + { + b.HasOne("Volo.Abp.TestApp.Domain.Person") + .WithMany("Phones") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20170927080244_Initial_Migration.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20170927080244_Initial_Migration.cs new file mode 100644 index 0000000000..8b2e4f5aa1 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20170927080244_Initial_Migration.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Volo.Abp.EntityFrameworkCore.Tests.Migrations +{ + public partial class Initial_Migration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "People", + columns: table => new + { + Id = table.Column(type: "BLOB", nullable: false), + Age = table.Column(type: "INTEGER", nullable: false), + Name = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_People", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AppPhones", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Number = table.Column(type: "TEXT", nullable: true), + PersonId = table.Column(type: "BLOB", nullable: false), + Type = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AppPhones", x => x.Id); + table.ForeignKey( + name: "FK_AppPhones_People_PersonId", + column: x => x.PersonId, + principalTable: "People", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AppPhones_PersonId", + table: "AppPhones", + column: "PersonId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AppPhones"); + + migrationBuilder.DropTable( + name: "People"); + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/TestAppDbContextModelSnapshot.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/TestAppDbContextModelSnapshot.cs new file mode 100644 index 0000000000..be31c871b2 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/TestAppDbContextModelSnapshot.cs @@ -0,0 +1,65 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using System; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.TestApp.EntityFrameworkCore; + +namespace Volo.Abp.EntityFrameworkCore.Tests.Migrations +{ + [DbContext(typeof(TestAppDbContext))] + partial class TestAppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Volo.Abp.TestApp.Domain.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Age"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("People"); + }); + + modelBuilder.Entity("Volo.Abp.TestApp.Domain.Phone", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Number"); + + b.Property("PersonId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("PersonId"); + + b.ToTable("AppPhones"); + }); + + modelBuilder.Entity("Volo.Abp.TestApp.Domain.Phone", b => + { + b.HasOne("Volo.Abp.TestApp.Domain.Person") + .WithMany("Phones") + .HasForeignKey("PersonId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj index 4186fa8882..7afc1932ae 100644 --- a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj @@ -15,16 +15,19 @@ + + - + + \ No newline at end of file diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs index 7f406993c9..37ef8850a1 100644 --- a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Autofac; +using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; using Volo.Abp.Modularity; using Volo.Abp.TestApp; using Volo.Abp.TestApp.EntityFrameworkCore; @@ -11,6 +12,7 @@ namespace Volo.Abp.EntityFrameworkCore [DependsOn(typeof(AbpEntityFrameworkCoreModule))] [DependsOn(typeof(TestAppModule))] [DependsOn(typeof(AbpAutofacModule))] + [DependsOn(typeof(AbpEfCoreTestSecondContextModule))] public class AbpEntityFrameworkCoreTestModule : AbpModule { public override void ConfigureServices(IServiceCollection services) @@ -22,6 +24,11 @@ namespace Volo.Abp.EntityFrameworkCore options.WithDefaultRepositories(); }); + services.AddAbpDbContext(options => + { + options.WithDefaultRepositories(); + }); + var inMemorySqlite = new SqliteConnection("Data Source=:memory:"); inMemorySqlite.Open(); @@ -36,7 +43,8 @@ namespace Volo.Abp.EntityFrameworkCore public override void OnPreApplicationInitialization(ApplicationInitializationContext context) { - context.ServiceProvider.GetRequiredService().Database.EnsureCreated(); + context.ServiceProvider.GetRequiredService().Database.Migrate(); + context.ServiceProvider.GetRequiredService().Database.Migrate(); } } } diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Basic_Repository_Tests.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Basic_Repository_Tests.cs index ada013b3d9..a3d33b4f71 100644 --- a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Basic_Repository_Tests.cs +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Basic_Repository_Tests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Domain.Repositories; +using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; using Volo.Abp.TestApp.Domain; using Xunit; @@ -12,18 +13,26 @@ namespace Volo.Abp.EntityFrameworkCore.Repositories public class Basic_Repository_Tests : EntityFrameworkCoreTestBase { private readonly IRepository _personRepository; + private readonly IRepository _bookRepository; public Basic_Repository_Tests() { _personRepository = ServiceProvider.GetRequiredService>(); + _bookRepository = ServiceProvider.GetRequiredService>(); } [Fact] - public void GetList() + public void GetPersonList() { _personRepository.GetList().Any().ShouldBeTrue(); } + [Fact] + public void GetBookList() + { + _bookRepository.GetList().Any().ShouldBeTrue(); + } + [Fact] public async Task InsertAsync() { diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs index d13a964fc5..572b4efc3f 100644 --- a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Domain.Repositories; +using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; using Volo.Abp.TestApp.Domain; using Volo.Abp.Uow; using Xunit; @@ -12,11 +13,13 @@ namespace Volo.Abp.EntityFrameworkCore public class Transaction_Tests : EntityFrameworkCoreTestBase { private readonly IRepository _personRepository; + private readonly IRepository _bookRepository; private readonly IUnitOfWorkManager _unitOfWorkManager; public Transaction_Tests() { _personRepository = ServiceProvider.GetRequiredService>(); + _bookRepository = ServiceProvider.GetRequiredService>(); _unitOfWorkManager = ServiceProvider.GetRequiredService(); } @@ -28,7 +31,7 @@ namespace Volo.Abp.EntityFrameworkCore try { - await WithUnitOfWorkAsync(async () => + await WithUnitOfWorkAsync(new UnitOfWorkOptions { IsTransactional = true }, async () => { await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); throw new Exception(exceptionMessage); @@ -48,15 +51,44 @@ namespace Volo.Abp.EntityFrameworkCore { var personId = Guid.NewGuid(); - await WithUnitOfWorkAsync(async () => + await WithUnitOfWorkAsync(new UnitOfWorkOptions { IsTransactional = true }, async () => { _unitOfWorkManager.Current.ShouldNotBeNull(); + await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); + await _unitOfWorkManager.Current.RollbackAsync(); }); var person = await _personRepository.FindAsync(personId); person.ShouldBeNull(); } + + [Fact] + public async Task Should_Rollback_Transaction_Manually_With_Double_DbContext_Transaction() + { + var personId = Guid.NewGuid(); + var bookId = Guid.NewGuid(); + + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (uowManager.Begin(new UnitOfWorkOptions { IsTransactional = true })) + { + _unitOfWorkManager.Current.ShouldNotBeNull(); + + await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); + await _bookRepository.InsertAsync(new BookInSecondDbContext { Id = bookId, Name = bookId.ToString() }); + + await _unitOfWorkManager.Current.SaveChangesAsync(); + + //Will automatically rollback since not called the Complete! + } + } + + (await _personRepository.FindAsync(personId)).ShouldBeNull(); + (await _bookRepository.FindAsync(bookId)).ShouldBeNull(); + } } } diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContextFactory.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContextFactory.cs new file mode 100644 index 0000000000..abe6ec9f70 --- /dev/null +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContextFactory.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace Volo.Abp.TestApp.EntityFrameworkCore +{ + public class TestAppDbContextFactory : IDesignTimeDbContextFactory + { + public TestAppDbContext CreateDbContext(string[] args) + { + var builder = new DbContextOptionsBuilder(); + builder.UseSqlite(@"Data Source=d:\temp\VoloAbpEfCoreTestModule.db;"); + return new TestAppDbContext(builder.Options); + } + } +} diff --git a/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Phone.cs b/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Phone.cs index def35e0962..8519bea092 100644 --- a/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Phone.cs +++ b/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Phone.cs @@ -1,8 +1,10 @@ using System; +using System.ComponentModel.DataAnnotations.Schema; using Volo.Abp.Domain.Entities; namespace Volo.Abp.TestApp.Domain { + [Table("AppPhones")] public class Phone : Entity { public virtual Guid PersonId { get; set; }