Skip to content

Commit

Permalink
PT-9963: DB Agnostic architecture with SqlServer, MySql and PostgreSQL (
Browse files Browse the repository at this point in the history
#66)

feat: DB Agnostic architecture with SqlServer, MySql and PostgreSQL
fix: Normalize line endings
fix: Remove EnsureCreated()
  • Loading branch information
OlegoO authored Dec 20, 2022
1 parent b575e34 commit f041898
Show file tree
Hide file tree
Showing 35 changed files with 1,668 additions and 115 deletions.
21 changes: 21 additions & 0 deletions VirtoCommerce.WebhooksModule.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtoCommerce.WebhooksModul
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtoCommerce.WebhooksModule.Tests", "tests\VirtoCommerce.WebHooksModule.Tests\VirtoCommerce.WebhooksModule.Tests.csproj", "{3E36EF7C-272F-46D0-9480-D179E95F43CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtoCommerce.WebhooksModule.Data.SqlServer", "src\VirtoCommerce.WebhooksModule.Data.SqlServer\VirtoCommerce.WebhooksModule.Data.SqlServer.csproj", "{5EFB42BE-8650-49E1-BBDF-E04ED30DF379}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtoCommerce.WebhooksModule.Data.MySql", "src\VirtoCommerce.WebhooksModule.Data.MySql\VirtoCommerce.WebhooksModule.Data.MySql.csproj", "{5C7452A3-5C64-4981-817C-C0C8BE230BBF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtoCommerce.WebhooksModule.Data.PostgreSql", "src\VirtoCommerce.WebhooksModule.Data.PostgreSql\VirtoCommerce.WebhooksModule.Data.PostgreSql.csproj", "{EE92F629-CD6C-4CCA-BC7C-C06F69BAB7D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -37,6 +43,18 @@ Global
{3E36EF7C-272F-46D0-9480-D179E95F43CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E36EF7C-272F-46D0-9480-D179E95F43CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E36EF7C-272F-46D0-9480-D179E95F43CC}.Release|Any CPU.Build.0 = Release|Any CPU
{5EFB42BE-8650-49E1-BBDF-E04ED30DF379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EFB42BE-8650-49E1-BBDF-E04ED30DF379}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EFB42BE-8650-49E1-BBDF-E04ED30DF379}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EFB42BE-8650-49E1-BBDF-E04ED30DF379}.Release|Any CPU.Build.0 = Release|Any CPU
{5C7452A3-5C64-4981-817C-C0C8BE230BBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5C7452A3-5C64-4981-817C-C0C8BE230BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C7452A3-5C64-4981-817C-C0C8BE230BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5C7452A3-5C64-4981-817C-C0C8BE230BBF}.Release|Any CPU.Build.0 = Release|Any CPU
{EE92F629-CD6C-4CCA-BC7C-C06F69BAB7D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE92F629-CD6C-4CCA-BC7C-C06F69BAB7D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE92F629-CD6C-4CCA-BC7C-C06F69BAB7D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE92F629-CD6C-4CCA-BC7C-C06F69BAB7D1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -46,6 +64,9 @@ Global
{36A4AB75-873E-44C2-B49B-F792DF228294} = {60ADC1B3-8809-429A-A274-6E53BF6D4AB0}
{3E4B18DB-F043-4001-A425-B130180977FF} = {60ADC1B3-8809-429A-A274-6E53BF6D4AB0}
{3E36EF7C-272F-46D0-9480-D179E95F43CC} = {A14F288A-E9E8-4D2C-A123-287B6853E880}
{5EFB42BE-8650-49E1-BBDF-E04ED30DF379} = {60ADC1B3-8809-429A-A274-6E53BF6D4AB0}
{5C7452A3-5C64-4981-817C-C0C8BE230BBF} = {60ADC1B3-8809-429A-A274-6E53BF6D4AB0}
{EE92F629-CD6C-4CCA-BC7C-C06F69BAB7D1} = {60ADC1B3-8809-429A-A274-6E53BF6D4AB0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {929FD73A-C34A-43FB-BAF4-019ECCB82CBF}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Reflection;
using EntityFrameworkCore.Triggers;
using Microsoft.EntityFrameworkCore;
using VirtoCommerce.WebhooksModule.Data.Models;
Expand Down Expand Up @@ -45,6 +46,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity<WebHookFeedEntryEntity>().HasIndex(x => new { x.WebHookId, x.RecordType }).HasDatabaseName("IX_WebHookIdAndRecordType");

base.OnModelCreating(modelBuilder);

// Allows configuration for an entity type for different database types.
// Applies configuration from all <see cref="IEntityTypeConfiguration{TEntity}" in VirtoCommerce.WebhooksModule.Data.XXX project. />
switch (this.Database.ProviderName)
{
case "Pomelo.EntityFrameworkCore.MySql":
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.Load("VirtoCommerce.WebhooksModule.Data.MySql"));
break;
case "Npgsql.EntityFrameworkCore.PostgreSQL":
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.Load("VirtoCommerce.WebhooksModule.Data.PostgreSql"));
break;
case "Microsoft.EntityFrameworkCore.SqlServer":
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.Load("VirtoCommerce.WebhooksModule.Data.SqlServer"));
break;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Data.Infrastructure;
Expand All @@ -12,8 +9,6 @@ namespace VirtoCommerce.WebhooksModule.Data.Repositories
{
public class WebHookRepository : DbContextRepositoryBase<WebhookDbContext>, IWebHookRepository
{
private readonly int _batchSize = 50;

public WebHookRepository(WebhookDbContext dbContext) : base(dbContext)
{
}
Expand Down Expand Up @@ -44,75 +39,26 @@ public Task<WebHookFeedEntryEntity[]> GetWebHookFeedEntriesByIdsAsync(string[] i

public async Task DeleteWebHookFeedEntriesByIdsAsync(string[] ids)
{
if (!ids.IsNullOrEmpty())
{
var skip = 0;
do
{
var batchEntries = ids.Skip(skip).Take(_batchSize);

await ExecuteSqlCommandAsync("DELETE FROM WebHookFeedEntry WHERE Id IN ({0})", batchEntries);
var models = await GetWebHookFeedEntriesByIdsAsync(ids);

skip += _batchSize;
}
while (skip < ids.Length);
foreach (var model in models)
{
Remove(model);
}

}

protected virtual Task ExecuteSqlCommandAsync(string commandTemplate, IEnumerable<string> parameterValues)
{
if (parameterValues?.Count() > 0)
{
var command = CreateCommand(commandTemplate, parameterValues);
return DbContext.Database.ExecuteSqlRawAsync(command.Text, command.Parameters);
}
return Task.CompletedTask;
}


protected virtual Command CreateCommand(string commandTemplate, IEnumerable<string> parameterValues)
{
var parameters = parameterValues.Select((v, i) => new SqlParameter($"@p{i}", v));
var parameterNames = string.Join(",", parameters.Select(p => p.ParameterName));

return new Command
{
Text = string.Format(commandTemplate, parameterNames),
Parameters = parameters.OfType<object>(),
};
}

protected class Command
{
public string Text { get; set; }
public IEnumerable<object> Parameters { get; set; }
}

public async Task UpdateAttemptCountsAsync(WebHookFeedEntryEntity[] webHookFeedEntries)
public Task UpdateAttemptCountsAsync(WebHookFeedEntryEntity[] webHookFeedEntries)
{
if (!webHookFeedEntries.IsNullOrEmpty())
{
var skip = 0;
do
foreach (var model in webHookFeedEntries)
{
var batchEntries = webHookFeedEntries.Skip(skip).Take(_batchSize);
var sb = new StringBuilder();
var sqlParameters = new List<SqlParameter>();
var i = 0;
foreach (var entry in batchEntries)
{
sb.AppendLine($"UPDATE WebHookFeedEntry SET AttemptCount = @count{i} WHERE Id = @id{i}; ");
sqlParameters.Add(new SqlParameter($"@count{i}", entry.AttemptCount));
sqlParameters.Add(new SqlParameter($"@id{i}", entry.Id));
i++;
}

await DbContext.Database.ExecuteSqlRawAsync(sb.ToString(), sqlParameters.ToArray());

skip += _batchSize;
Update(model);
}
while (skip < webHookFeedEntries.Length);
}
}

return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public async Task DeleteByIdsAsync(string[] ids)
using (var repository = _webHookRepositoryFactory())
{
await repository.DeleteWebHookFeedEntriesByIdsAsync(ids);
repository.UnitOfWork.Commit();
}
}

Expand Down Expand Up @@ -108,7 +109,7 @@ public async Task<WebHookFeedSearchResult> SearchAsync(WebhookFeedSearchCriteria
return result;
}
}

#region IWebHookFeedReader

public async Task<IDictionary<string, int>> GetSuccessCountsAsync(string[] webHookIds)
Expand Down Expand Up @@ -155,6 +156,7 @@ public async Task UpdateCountAttemps(WebhookFeedEntry[] webhookLogEntries)
await repository.UpdateAttemptCountsAsync(
webhookLogEntries.Select(x => AbstractTypeFactory<WebHookFeedEntryEntity>.TryCreateInstance().FromModel(x, pkMap))
.ToArray());
repository.UnitOfWork.Commit();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,6 @@
<ItemGroup>
<PackageReference Include="Hangfire" Version="1.7.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="VirtoCommerce.Platform.Core" Version="3.200.0" />
<PackageReference Include="VirtoCommerce.Platform.Data" Version="3.200.0" />
Expand Down
40 changes: 35 additions & 5 deletions src/VirtoCommerce.WebHooksModule.Web/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,47 @@
using VirtoCommerce.Platform.Core.Security;
using VirtoCommerce.Platform.Core.Settings;
using VirtoCommerce.Platform.Data.Extensions;
using VirtoCommerce.WebhooksModule.Data.MySql;
using VirtoCommerce.WebhooksModule.Data.PostgreSql;
using VirtoCommerce.WebhooksModule.Data.Repositories;
using VirtoCommerce.WebhooksModule.Data.Services;
using VirtoCommerce.WebhooksModule.Data.SqlServer;
using VirtoCommerce.WebHooksModule.Core;
using VirtoCommerce.WebHooksModule.Core.Services;
using VirtoCommerce.WebHooksModule.Data.Services;

namespace VirtoCommerce.WebHooksModule.Web
{
public class Module : IModule
public class Module : IModule, IHasConfiguration
{
public ManifestModuleInfo ModuleInfo { get; set; }
public IConfiguration Configuration { get; set; }


public void Initialize(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<IWebHookRepository, WebHookRepository>();

serviceCollection.AddDbContext<WebhookDbContext>((provider, options) =>
options.UseSqlServer(provider.GetRequiredService<IConfiguration>().GetConnectionString("VirtoCommerce")));
{
var databaseProvider = Configuration.GetValue("DatabaseProvider", "SqlServer");
var connectionString = Configuration.GetConnectionString(ModuleInfo.Id) ?? Configuration.GetConnectionString("VirtoCommerce");

switch (databaseProvider)
{
case "MySql":
options.UseMySqlDatabase(connectionString);
break;
case "PostgreSql":
options.UsePostgreSqlDatabase(connectionString);
break;
default:
options.UseSqlServerDatabase(connectionString);
break;
}
});


serviceCollection.AddTransient<Func<IWebHookRepository>>(provider => () => provider.CreateScope().ServiceProvider.GetRequiredService<IWebHookRepository>());

serviceCollection.AddTransient<IWebHookService, WebHookService>();
Expand Down Expand Up @@ -57,12 +81,18 @@ public void PostInitialize(IApplicationBuilder appBuilder)
var webHookManager = appBuilder.ApplicationServices.GetService<IWebHookManager>();
webHookManager.SubscribeToAllEvents();

//Force migrations
// Force migrations
using (var serviceScope = appBuilder.ApplicationServices.CreateScope())
{
var databaseProvider = Configuration.GetValue("DatabaseProvider", "SqlServer");

var dbContext = serviceScope.ServiceProvider.GetRequiredService<WebhookDbContext>();
dbContext.Database.MigrateIfNotApplied(MigrationName.GetUpdateV2MigrationName(ModuleInfo.Id));
dbContext.Database.EnsureCreated();

if (databaseProvider == "SqlServer")
{
dbContext.Database.MigrateIfNotApplied(MigrationName.GetUpdateV2MigrationName(ModuleInfo.Id));
}

dbContext.Database.Migrate();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VirtoCommerce.WebHooksModule.Core\VirtoCommerce.WebhooksModule.Core.csproj" />
<ProjectReference Include="..\VirtoCommerce.WebhooksModule.Data.MySql\VirtoCommerce.WebhooksModule.Data.MySql.csproj" />
<ProjectReference Include="..\VirtoCommerce.WebhooksModule.Data.PostgreSql\VirtoCommerce.WebhooksModule.Data.PostgreSql.csproj" />
<ProjectReference Include="..\VirtoCommerce.WebhooksModule.Data.SqlServer\VirtoCommerce.WebhooksModule.Data.SqlServer.csproj" />
<ProjectReference Include="..\VirtoCommerce.WebHooksModule.Data\VirtoCommerce.WebhooksModule.Data.csproj" />
</ItemGroup>
</Project>
6 changes: 3 additions & 3 deletions src/VirtoCommerce.WebHooksModule.Web/module.manifest
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<module xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<id>VirtoCommerce.WebHooks</id>
<version>3.207.0</version>
Expand All @@ -16,12 +16,12 @@
</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<releaseNotes />
<copyright>Copyright © 2022 Virto Commerce. All rights reserved</copyright>
<copyright>Copyright © 2023 Virto Commerce. All rights reserved</copyright>
<tags>webhooks module</tags>
<assemblyFile>VirtoCommerce.WebhooksModule.Web.dll</assemblyFile>
<moduleType>VirtoCommerce.WebHooksModule.Web.Module, VirtoCommerce.WebhooksModule.Web</moduleType>
<dependencies>
<dependency id="VirtoCommerce.Core" version="3.200.0" />
</dependencies>
<useFullTypeNameInSwagger>false</useFullTypeNameInSwagger>
</module>
</module>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.EntityFrameworkCore;

namespace VirtoCommerce.WebhooksModule.Data.MySql
{
public static class DbContextOptionsBuilderExtensions
{
/// <summary>
/// Configures the context to use PostgreSql.
/// </summary>
public static DbContextOptionsBuilder UseMySqlDatabase(this DbContextOptionsBuilder builder, string connectionString) =>
builder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), db => db
.MigrationsAssembly(typeof(MySqlDbContextFactory).Assembly.GetName().Name));
}
}
Loading

0 comments on commit f041898

Please sign in to comment.