Skip to content

Commit 6bc3619

Browse files
authored
Merge pull request #9 from abpframework/containerization-to-deploy
Deploy CMS Kit Demo application to cms-kit-demo.abpdemo.com
2 parents 69aef33 + f025c6d commit 6bc3619

19 files changed

+652
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
2+
# # More GitHub Actions for Azure: https://github.com/Azure/actions
3+
4+
# name: Build and deploy ASP.Net Core app to Azure Web App - cms-kit-demo
5+
6+
# on:
7+
# push:
8+
# branches:
9+
# - containerization-to-deploy
10+
# workflow_dispatch:
11+
12+
# jobs:
13+
# build:
14+
# runs-on: ubuntu-latest
15+
16+
# steps:
17+
# - uses: actions/checkout@v2
18+
19+
# - name: Set up .NET Core
20+
# uses: actions/setup-dotnet@v1
21+
# with:
22+
# dotnet-version: '7.x'
23+
# include-prerelease: true
24+
25+
# - name: Build with dotnet
26+
# run: dotnet build --configuration Release
27+
28+
# - name: dotnet publish
29+
# run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp
30+
31+
# - name: Copy CmsKitDemo.db to publish directory
32+
# run: cp src/CmsKitDemo.db ${{env.DOTNET_ROOT}}/myapp
33+
34+
# - name: Upload artifact for deployment job
35+
# uses: actions/upload-artifact@v2
36+
# with:
37+
# name: .net-app
38+
# path: ${{env.DOTNET_ROOT}}/myapp
39+
40+
# deploy:
41+
# runs-on: ubuntu-latest
42+
# needs: build
43+
# environment:
44+
# name: 'Production'
45+
# url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
46+
47+
# steps:
48+
# - name: Download artifact from build job
49+
# uses: actions/download-artifact@v2
50+
# with:
51+
# name: .net-app
52+
53+
# - name: Deploy to Azure Web App
54+
# id: deploy-to-webapp
55+
# uses: azure/webapps-deploy@v2
56+
# with:
57+
# app-name: 'cms-kit-demo'
58+
# slot-name: 'Production'
59+
# publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_B8EE580013C3409F9EA266AF29C41EAD }}
60+
# package: .

src/CmsKitDemo/CmsKitDemo.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@
102102
<Content Remove="Logs\**" />
103103
<EmbeddedResource Remove="Logs\**" />
104104
<None Remove="Logs\**" />
105+
<None Update="CmsKitDemoDb\CmsKitDemo.db">
106+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
107+
</None>
105108
</ItemGroup>
106109

107110
<ItemGroup>

src/CmsKitDemo/CmsKitDemoConsts.cs

+2
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ public class CmsKitDemoConsts
99
public const string ImageGalleryEntityType = "Image";
1010

1111
public const int MaxDescriptionLength = 512;
12+
13+
public static readonly List<string> IgnoredUserAgents = new();
1214
}
1315
}
File renamed without changes.

src/CmsKitDemo/CmsKitDemoModule.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@
5555
using Volo.CmsKit.Web;
5656
using Volo.Abp.Threading;
5757
using Microsoft.AspNetCore.Mvc.RazorPages;
58+
using Volo.Abp.Data;
59+
using Volo.Abp.IO;
5860
using Volo.CmsKit.Reactions;
5961
using Volo.CmsKit.Comments;
62+
using Microsoft.EntityFrameworkCore.Query;
6063

6164
namespace CmsKitDemo;
6265

@@ -316,7 +319,8 @@ private void ConfigureEfCore(ServiceConfigurationContext context)
316319
{
317320
options.Configure(configurationContext =>
318321
{
319-
configurationContext.UseSqlite();
322+
configurationContext.UseSqlite()
323+
.ReplaceService<IMethodCallTranslatorProvider, CmsKitDemoSqliteMethodCallTranslatorProvider>();
320324
});
321325
});
322326

@@ -358,6 +362,13 @@ private void ConfigureCmsKit(ServiceConfigurationContext context)
358362
});
359363
}
360364

365+
public override async Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
366+
{
367+
DirectoryHelper.CreateIfNotExists(context.GetConfiguration()["App:DbFolderName"] ?? "CmsKitDemoDb");
368+
var connString = await context.ServiceProvider.GetRequiredService<IConnectionStringResolver>().ResolveAsync();
369+
await context.ServiceProvider.GetRequiredService<CmsKitDemoDbMigrationService>().MigrateAsync(connString);
370+
}
371+
361372
public override void OnApplicationInitialization(ApplicationInitializationContext context)
362373
{
363374
var app = context.GetApplicationBuilder();
@@ -375,6 +386,8 @@ public override void OnApplicationInitialization(ApplicationInitializationContex
375386
app.UseErrorPage();
376387
}
377388

389+
app.UseMiddleware<DbMigrationMiddleware>();
390+
378391
app.UseCorrelationId();
379392
app.UseStaticFiles();
380393
app.UseRouting();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Microsoft.Extensions.Options;
2+
using Volo.Abp.Data;
3+
using Volo.Abp.DependencyInjection;
4+
using Volo.Abp.Threading;
5+
6+
namespace CmsKitDemo.Data;
7+
8+
[Dependency(ReplaceServices = true)]
9+
public class CmsKitConnectionStringResolver : DefaultConnectionStringResolver
10+
{
11+
private readonly CmsKitDemoUserIdResolver _demoNameResolver;
12+
private readonly IConfiguration _configuration;
13+
14+
public CmsKitConnectionStringResolver(
15+
IOptionsMonitor<AbpDbConnectionOptions> options,
16+
CmsKitDemoUserIdResolver demoNameResolver,
17+
IConfiguration configuration)
18+
: base(options)
19+
{
20+
_demoNameResolver = demoNameResolver;
21+
_configuration = configuration;
22+
}
23+
24+
[Obsolete("Use ResolveAsync method.")]
25+
public override string Resolve(string connectionStringName = null)
26+
{
27+
return AsyncHelper.RunSync(() => ResolveInternalAsync(connectionStringName));
28+
}
29+
30+
public async override Task<string> ResolveAsync(string connectionStringName = null)
31+
{
32+
return await ResolveInternalAsync(connectionStringName);
33+
}
34+
35+
private async Task<string> ResolveInternalAsync(string connectionStringName = null)
36+
{
37+
var dbFolder = _configuration["App:DbFolderName"]?.EnsureEndsWith(Path.DirectorySeparatorChar);
38+
if (dbFolder.IsNullOrWhiteSpace())
39+
{
40+
return await base.ResolveAsync(connectionStringName);
41+
}
42+
43+
var demoUserId = _demoNameResolver.GetDemoUserIdOrNull() ?? _configuration["App:DefaultDbName"];
44+
45+
var dbFilePath = $"{dbFolder}{demoUserId}.db";
46+
if (!File.Exists(dbFilePath))
47+
{
48+
File.Copy(_configuration["App:DbFolderName"]?.EnsureEndsWith(Path.DirectorySeparatorChar) + _configuration["App:DefaultDbName"] + ".db", dbFilePath);
49+
}
50+
51+
return $"Data Source={dbFilePath};Cache=Shared";
52+
}
53+
}

src/CmsKitDemo/Data/CmsKitDemoDbContextFactory.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ public class CmsKitDemoDbContextFactory : IDesignTimeDbContextFactory<CmsKitDemo
77
{
88
public CmsKitDemoDbContext CreateDbContext(string[] args)
99
{
10-
1110
var configuration = BuildConfiguration();
1211

12+
var dbFolder = configuration["App:DbFolderName"]!.EnsureEndsWith(Path.DirectorySeparatorChar);
13+
1314
var builder = new DbContextOptionsBuilder<CmsKitDemoDbContext>()
14-
.UseSqlite(configuration.GetConnectionString("Default"));
15+
.UseSqlite($"Data Source={dbFolder}{configuration["App:DefaultDbName"]}.db;");
1516

1617
return new CmsKitDemoDbContext(builder.Options);
1718
}

src/CmsKitDemo/Data/CmsKitDemoDbMigrationService.cs

+52-2
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,73 @@ public class CmsKitDemoDbMigrationService : ITransientDependency
1717
private readonly CmsKitDemoEFCoreDbSchemaMigrator _dbSchemaMigrator;
1818
private readonly ITenantRepository _tenantRepository;
1919
private readonly ICurrentTenant _currentTenant;
20+
private readonly IConfiguration _configuration;
2021

2122
public CmsKitDemoDbMigrationService(
2223
IDataSeeder dataSeeder,
2324
CmsKitDemoEFCoreDbSchemaMigrator dbSchemaMigrator,
2425
ITenantRepository tenantRepository,
25-
ICurrentTenant currentTenant)
26+
ICurrentTenant currentTenant,
27+
IConfiguration configuration)
2628
{
2729
_dataSeeder = dataSeeder;
2830
_dbSchemaMigrator = dbSchemaMigrator;
2931
_tenantRepository = tenantRepository;
3032
_currentTenant = currentTenant;
33+
_configuration = configuration;
3134

3235
Logger = NullLogger<CmsKitDemoDbMigrationService>.Instance;
3336
}
37+
38+
private async Task MigrateHostDatabaseAsync(string connectionString)
39+
{
40+
Logger.LogInformation("Migrating host database schema...");
41+
42+
if (!connectionString.Contains(_configuration["App:DefaultDbName"] + ".db"))
43+
{
44+
TryToCopyFromDefaultDb(connectionString);
45+
}
46+
else
47+
{
48+
await _dbSchemaMigrator.MigrateAsync(connectionString);
49+
}
3450

35-
public async Task MigrateAsync()
51+
Logger.LogInformation("Executing host database seed...");
52+
53+
await _dataSeeder.SeedAsync(
54+
new DataSeedContext()
55+
.WithProperty("AdminPassword", "123456")
56+
);
57+
58+
Logger.LogInformation("Successfully completed host database migrations.");
59+
}
60+
61+
private void TryToCopyFromDefaultDb(string connectionString)
3662
{
63+
try
64+
{
65+
var filePath = connectionString!.Replace("Data Source=", "").Split(";")[0];
66+
File.Copy(_configuration["App:DbFolderName"]?.EnsureEndsWith(Path.DirectorySeparatorChar) + _configuration["App:DefaultDbName"] + ".db", filePath);
67+
}
68+
catch
69+
{
70+
//...
71+
}
72+
}
73+
74+
public async Task MigrateAsync(string? connectionString = null)
75+
{
76+
if (!connectionString.IsNullOrWhiteSpace())
77+
{
78+
Logger.LogInformation("Started database migrations...");
79+
80+
await MigrateHostDatabaseAsync(connectionString!);
81+
82+
Logger.LogInformation("Successfully completed database migrations.");
83+
84+
return;
85+
}
86+
3787
var initialMigrationAdded = AddInitialMigrationIfNotExist();
3888

3989
if (initialMigrationAdded)

src/CmsKitDemo/Data/CmsKitDemoEFCoreDbSchemaMigrator.cs

+16-13
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@ public class CmsKitDemoEFCoreDbSchemaMigrator : ITransientDependency
77
{
88
private readonly IServiceProvider _serviceProvider;
99

10-
public CmsKitDemoEFCoreDbSchemaMigrator(
11-
IServiceProvider serviceProvider)
10+
public CmsKitDemoEFCoreDbSchemaMigrator(IServiceProvider serviceProvider)
1211
{
1312
_serviceProvider = serviceProvider;
1413
}
1514

16-
public async Task MigrateAsync()
15+
public async Task MigrateAsync(string? connectionString = null)
1716
{
18-
/* We intentionally resolve the CmsKitDemoDbContext
19-
* from IServiceProvider (instead of directly injecting it)
20-
* to properly get the connection string of the current tenant in the
21-
* current scope.
22-
*/
17+
if (connectionString.IsNullOrWhiteSpace())
18+
{
19+
await _serviceProvider.GetRequiredService<CmsKitDemoDbContext>().Database.MigrateAsync();
20+
return;
21+
}
2322

24-
await _serviceProvider
25-
.GetRequiredService<CmsKitDemoDbContext>()
26-
.Database
27-
.MigrateAsync();
23+
var options = new DbContextOptionsBuilder<CmsKitDemoDbContext>()
24+
.UseSqlite(connectionString)
25+
.Options;
26+
27+
using (var dbContext = new CmsKitDemoDbContext(options))
28+
{
29+
await dbContext.Database.MigrateAsync();
30+
}
2831
}
29-
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using JetBrains.Annotations;
2+
using Microsoft.EntityFrameworkCore.Diagnostics;
3+
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.EntityFrameworkCore.Query;
5+
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
6+
using Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
7+
using System.Reflection;
8+
using Volo.Abp;
9+
10+
namespace CmsKitDemo.Data
11+
{
12+
public class CmsKitDemoSqliteMethodCallTranslatorProvider : SqliteMethodCallTranslatorProvider
13+
{
14+
public CmsKitDemoSqliteMethodCallTranslatorProvider([NotNull] RelationalMethodCallTranslatorProviderDependencies dependencies)
15+
: base(dependencies)
16+
{
17+
var sqlExpressionFactory = dependencies.SqlExpressionFactory;
18+
19+
AddTranslators(
20+
new IMethodCallTranslator[]
21+
{
22+
new SqliteMathTranslator(sqlExpressionFactory),
23+
new SqliteDateTimeAddTranslator(sqlExpressionFactory.As<SqliteSqlExpressionFactory>()),
24+
new CmsKitDemoSqliteStringMethodTranslator(sqlExpressionFactory)
25+
});
26+
}
27+
}
28+
29+
public class CmsKitDemoSqliteStringMethodTranslator : SqliteStringMethodTranslator
30+
{
31+
private static readonly MethodInfo _containsMethodInfo
32+
= typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) });
33+
34+
private readonly ISqlExpressionFactory _sqlExpressionFactory;
35+
36+
public CmsKitDemoSqliteStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) : base(sqlExpressionFactory)
37+
{
38+
_sqlExpressionFactory = sqlExpressionFactory;
39+
}
40+
41+
public override SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments, IDiagnosticsLogger<DbLoggerCategory.Query> logger)
42+
{
43+
Check.NotNull(method, nameof(method));
44+
Check.NotNull(arguments, nameof(arguments));
45+
46+
if (_containsMethodInfo.Equals(method))
47+
{
48+
var pattern = arguments[0];
49+
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, pattern);
50+
51+
instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping);
52+
pattern = _sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping);
53+
54+
// this basically changes query to "instr(upper(left_expression), upper(right_expression))"
55+
return _sqlExpressionFactory.OrElse(
56+
_sqlExpressionFactory.Equal(
57+
pattern,
58+
_sqlExpressionFactory.Constant(string.Empty, stringTypeMapping)),
59+
_sqlExpressionFactory.GreaterThan(
60+
_sqlExpressionFactory.Function(
61+
"instr",
62+
new[]
63+
{
64+
_sqlExpressionFactory.Function("upper", new[]{instance}, false, new []{ false }, typeof(string)),
65+
_sqlExpressionFactory.Function("upper", new[]{pattern}, false, new []{ false }, typeof(string))
66+
},
67+
false,
68+
new[] { false, false },
69+
typeof(int)), _sqlExpressionFactory.Constant(0)));
70+
}
71+
72+
return base.Translate(instance, method, arguments, logger);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)