Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature explicitdistributedtransaction #5

Merged
merged 7 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/adoscope-nuget-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:

- name: Run Tests
working-directory: ./Promethix.Framework.Ado.Tests
run: dotnet test --no-restore --verbosity normal
run: dotnet test --no-restore --verbosity normal --filter TestCategory="IntegrationTestsOnCI|Unit"

publish:
name: Publish Promethix.Framework.Ado
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Microsoft.Data.Sqlite;
using Microsoft.Data.SqlClient;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Promethix.Framework.Ado.Enums;
using Promethix.Framework.Ado.Implementation;
using Promethix.Framework.Ado.Interfaces;
using Promethix.Framework.Ado.Tests.TestSupport.DataAccess;
using Promethix.Framework.Ado.Tests.TestSupport.DataAccess.Mssql;
using Promethix.Framework.Ado.Tests.TestSupport.DataAccess.Sqlite;
using SQLitePCL;
using System.Data;
using System.Data.Common;
Expand All @@ -26,13 +28,15 @@ public static void AddIntegrationDependencyInjection(this IServiceCollection ser

// Still need to register ADO providers you will be using
DbProviderFactories.RegisterFactory("Microsoft.Data.Sqlite", SqliteFactory.Instance);
DbProviderFactories.RegisterFactory("Microsoft.Data.SqlClient", SqlClientFactory.Instance);

// Register your repositories et al
_ = services.AddSingleton<IAmbientAdoContextLocator, AmbientAdoContextLocator>();
_ = services.AddSingleton<IAdoScopeFactory, AdoScopeFactory>();
_ = services.AddSingleton<IAdoContextGroupFactory, AdoContextGroupFactory>();
_ = services.AddScoped<ISimpleTestRepository, SimpleTestRepository>();
_ = services.AddScoped<IMultiTestRepository, MultiTestRepository>();
_ = services.AddScoped<ISimpleMssqlTestRepository, SimpleMssqlTestRepository>();

// Register your ADO contexts
var adoContextConfiguration = new AdoContextConfigurationBuilder()
Expand Down
102 changes: 102 additions & 0 deletions Promethix.Framework.Ado.Tests/IntegrationTests/AdoScopeMssqlTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Microsoft.Extensions.DependencyInjection;
using Promethix.Framework.Ado.Interfaces;
using Promethix.Framework.Ado.Tests.DependencyInjection;
using Promethix.Framework.Ado.Tests.TestSupport.DataAccess.Mssql;
using Promethix.Framework.Ado.Tests.TestSupport.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Promethix.Framework.Ado.Tests.IntegrationTests
{
/// <summary>
/// These tests have a dependency on a local SQL Server instance.
/// Therefore are not configured to run on CI.
/// </summary>
[TestClass]
public class AdoScopeMssqlTests
{
private readonly ISimpleMssqlTestRepository simpleTestRepository;

private readonly IAdoScopeFactory adoScopeFactory;

public AdoScopeMssqlTests()
{
var services = new ServiceCollection();
services.AddIntegrationDependencyInjection();
var container = services.BuildServiceProvider();

simpleTestRepository = container.GetService<ISimpleMssqlTestRepository>() ?? throw new InvalidOperationException("Could not create test repository");
adoScopeFactory = container.GetService<IAdoScopeFactory>() ?? throw new InvalidOperationException("Could not create ado scope factory");
}

[TestInitialize]
public void TestInitialize()
{
using IAdoScope adoScope = adoScopeFactory.Create();
simpleTestRepository.DeleteAll();
}

[TestCategory("IntegrationTests"), TestMethod]
public void MssqlAdoScopeBasicTest()
{

using (IAdoScope adoScope = adoScopeFactory.CreateWithDistributedTransaction())
{
// Create a test entity
var newTestEntity = new TestEntity { Name = "CreateTest", Description = "Test Description", Quantity = 1 };

// Call our repository to add the entity
simpleTestRepository.Add(newTestEntity);
simpleTestRepository.AddWithDifferentContext(newTestEntity);

// Complete data related work
adoScope.Complete();
}

using (IAdoScope adoScope = adoScopeFactory.Create())
{
Assert.IsNotNull(simpleTestRepository.GetEntityByName("CreateTest"));
}
}

[TestCategory("IntegrationTests"), TestMethod]
public void MssqlAdoScopeDistributedTest()
{
int recordCountBefore = GetRecordCountFirstContext();

using (IAdoScope adoScope1 = adoScopeFactory.CreateWithDistributedTransaction())
{
// Create a test entity
var newTestEntity = new TestEntity { Name = "CreateTest", Description = "Test Description", Quantity = 1 };

try
{
// Call our repository to add the entity
simpleTestRepository.Add(newTestEntity);
simpleTestRepository.AddWithDifferentContext(newTestEntity);
simpleTestRepository.DivideByZero();

// Complete data related work
adoScope1.Complete();
}
catch
{
// Do nothing. We expect this to fail.
adoScope1.Dispose();
}
}

// If our distributed transaction has worked correctly, we shouldn't have additional records in the database.
Assert.AreEqual(recordCountBefore, GetRecordCountFirstContext());
}

private int GetRecordCountFirstContext()
{
using IAdoScope adoScope = adoScopeFactory.Create();
return simpleTestRepository.GetEntityCount();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Promethix.Framework.Ado.Interfaces;
using Promethix.Framework.Ado.Tests.DependencyInjection;
using Promethix.Framework.Ado.Tests.TestSupport.DataAccess;
using Promethix.Framework.Ado.Tests.TestSupport.DataAccess.Sqlite;
using Promethix.Framework.Ado.Tests.TestSupport.Entities;
using System.Data;
using System.Data.Common;

namespace Promethix.Framework.Ado.Tests.IntegrationTests
{
[TestClass]
public class AdoScopeTests
public class AdoScopeSqliteTests
{
private readonly ISimpleTestRepository simpleTestRepository;

private readonly IMultiTestRepository multiTestRepository;

private readonly IAdoScopeFactory adoScopeFactory;

public AdoScopeTests()
public AdoScopeSqliteTests()
{
var services = new ServiceCollection();
services.AddIntegrationDependencyInjection();
Expand All @@ -29,15 +29,10 @@ public AdoScopeTests()
multiTestRepository = container.GetService<IMultiTestRepository>() ?? throw new InvalidOperationException("Could not create test repository");
adoScopeFactory = container.GetService<IAdoScopeFactory>() ?? throw new InvalidOperationException("Could not create ado scope factory");

if (!File.Exists("mydatabase.db"))
{
File.Create("mydatabase.db").Dispose();
}

CreateSqliteSchema();
}

[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeCreateTest()
{
using IAdoScope adoScope = adoScopeFactory.Create();
Expand All @@ -54,7 +49,7 @@ public void SqliteAdoScopeCreateTest()
Assert.IsNotNull(adoScope);
}

[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeCreateSurpressTest()
{
using IAdoScope adoScope = adoScopeFactory.Create();
Expand Down Expand Up @@ -92,7 +87,7 @@ private void AddEntity(TestEntity testEntity)
/// It's unlikely you would have multiple contexts all with different settings in the same repository
/// like this, but this is just to demonstrate the different configuration options.
/// </summary>
[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteMultiContextAdoScopeCreateTest()
{
using (IAdoScope adoScope = adoScopeFactory.Create())
Expand All @@ -119,7 +114,7 @@ public void SqliteMultiContextAdoScopeCreateTest()
}
}

[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeCreateReadTest()
{
using IAdoScope adoScope = adoScopeFactory.Create();
Expand All @@ -136,7 +131,7 @@ public void SqliteAdoScopeCreateReadTest()
/// Please note as per DbContextScope this creates a dedicated connection for an explicit transaction.
/// So be careful, there are transaction options that can be set on the ADO context.
/// </summary>
[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeCreateWithTransactionTest()
{
using IAdoScope adoScope = adoScopeFactory.CreateWithTransaction(IsolationLevel.ReadCommitted);
Expand All @@ -147,7 +142,7 @@ public void SqliteAdoScopeCreateWithTransactionTest()
Assert.IsNotNull(adoScope);
}

[TestCategory("IntegrationTests"), TestMethod]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeTransactionDisposeTest()
{
using IAdoScope adoScope = adoScopeFactory.CreateWithTransaction(IsolationLevel.ReadCommitted);
Expand All @@ -164,9 +159,73 @@ public void SqliteAdoScopeTransactionDisposeTest()
Assert.IsNull(simpleTestRepository.GetEntityByName("TransactionTest"));
}

[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeBasicDistributedTest()
{
using (IAdoScope adoScope = adoScopeFactory.CreateWithDistributedTransaction())
{
// Create a test entity
var newTestEntity = new TestEntity { Name = "CreateTest", Description = "Test Description", Quantity = 1 };

// Call our repository to add the entity
simpleTestRepository.Add(newTestEntity);
simpleTestRepository.AddWithDifferentContext(newTestEntity);

// Complete data related work
adoScope.Complete();
}

using (IAdoScope adoScope = adoScopeFactory.Create())
{
// Get the entity from the database
TestEntity testEntity = simpleTestRepository.GetEntityByName("CreateTest");

// Assert that the entity was retrieved
Assert.IsNotNull(testEntity);
}
}

[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeDistributedTest()
{
int recordCountBefore = GetRecordCountFirstContext();

using (IAdoScope adoScope1 = adoScopeFactory.CreateWithDistributedTransaction())
{
// Create a test entity
var newTestEntity = new TestEntity { Name = "CreateTest", Description = "Test Description", Quantity = 1 };

try
{
// Call our repository to add the entity
simpleTestRepository.Add(newTestEntity);
simpleTestRepository.AddWithDifferentContext(newTestEntity);
// Sqlite won't throw an exception for divide by zero! So need another way to break it.
simpleTestRepository.BreakSqlite();

// Complete data related work
adoScope1.Complete();
}
catch
{
// Do nothing. We expect this to fail.
adoScope1.Dispose();
}
}

// If our distributed transaction has worked correctly, we shouldn't have additional records in the database.
Assert.AreEqual(recordCountBefore, GetRecordCountFirstContext());
}

private int GetRecordCountFirstContext()
{
using IAdoScope adoScope = adoScopeFactory.Create();
return simpleTestRepository.GetEntityCount();
}

#region Nested Scope Tests

[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeNestedAndSequentialTest()
{
// Testing nested scopes
Expand Down Expand Up @@ -201,7 +260,7 @@ public void SqliteAdoScopeNestedAndSequentialTest()
adoScope.Complete();
}

[TestMethod, TestCategory("IntegrationTests")]
[TestCategory("IntegrationTestsOnCI"), TestMethod]
public void SqliteAdoScopeTransactionNestedTest()
{
using IAdoScope adoScope = adoScopeFactory.CreateWithTransaction(IsolationLevel.ReadCommitted);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand All @@ -13,6 +13,10 @@
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0|AnyCPU'">
<DefineConstants>$(DefineConstants);WINDOWS</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.151" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.11" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Promethix.Framework.Ado.Tests.TestSupport.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Promethix.Framework.Ado.Tests.TestSupport.DataAccess.Mssql
{
public interface ISimpleMssqlTestRepository
{
void Add(TestEntity entity);

TestEntity GetEntityByName(string name);

void AddWithDifferentContext(TestEntity entity);

int GetEntityCount();

void DivideByZero();

void DeleteAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Promethix.Framework.Ado.Enums;
using Promethix.Framework.Ado.Implementation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Promethix.Framework.Ado.Tests.TestSupport.DataAccess.Mssql
{
public class MssqlContextExample1 : AdoContext
{
public MssqlContextExample1()
: base(
"MssqlContextExample1",
"Microsoft.Data.SqlClient",
"Server=(local);Database=AdoScopeTest1;Integrated Security=True;TrustServerCertificate=True",
AdoContextExecutionOption.NonTransactional
)
{
// No Implementation
}
}
}
Loading