-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from IgniteUI/DNenchev/35-implement-multi-tena…
…nt-db-context Implement Multi-Tenant DbContext for SQLite With Db Expiration
- Loading branch information
Showing
6 changed files
with
203 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-cw.html | ||
|
||
files: | ||
"/opt/aws/amazon-cloudwatch-agent/bin/config.json": | ||
mode: "000600" | ||
owner: root | ||
group: root | ||
content: | | ||
{ | ||
"agent":{ | ||
"metrics_collection_interval":60, | ||
"logfile":"/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log", | ||
"run_as_user":"cwagent" | ||
}, | ||
"metrics":{ | ||
"namespace":"CWAgent/AppBuilderData", | ||
"append_dimensions":{ | ||
"InstanceId":"${aws:InstanceId}", | ||
"InstanceType":"${aws:InstanceType}", | ||
"AutoScalingGroupName":"${aws:AutoScalingGroupName}" | ||
}, | ||
"aggregation_dimensions":[ | ||
[ "AutoScalingGroupName", "InstanceId" ], | ||
[ ] | ||
], | ||
"metrics_collected":{ | ||
"cpu":{ | ||
"resources":[ | ||
"*" | ||
], | ||
"measurement":[ | ||
"time_idle", | ||
"time_iowait", | ||
"time_system", | ||
"time_user", | ||
"usage_steal", | ||
"usage_system", | ||
"usage_user", | ||
"usage_iowait" | ||
] | ||
}, | ||
"mem":{ | ||
"measurement":[ | ||
"used_percent", | ||
"total", | ||
"available_percent" | ||
] | ||
} | ||
} | ||
} | ||
} | ||
|
||
container_commands: | ||
start_cloudwatch_agent: | ||
command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a append-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json |
35 changes: 35 additions & 0 deletions
35
NorthwindCRUD/Middlewares/TenantHeaderValidationMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System.Text.RegularExpressions; | ||
|
||
namespace NorthwindCRUD.Middlewares | ||
{ | ||
public class TenantHeaderValidationMiddleware | ||
{ | ||
private const string TenantHeaderKey = "X-Tenant-ID"; | ||
|
||
private readonly RequestDelegate next; | ||
|
||
public TenantHeaderValidationMiddleware(RequestDelegate next) | ||
{ | ||
this.next = next; | ||
} | ||
|
||
public async Task InvokeAsync(HttpContext context) | ||
{ | ||
var tenantHeader = context.Request.Headers[TenantHeaderKey].FirstOrDefault(); | ||
|
||
if (tenantHeader != null && !IsTenantValid(tenantHeader)) | ||
{ | ||
context.Response.StatusCode = StatusCodes.Status400BadRequest; | ||
await context.Response.WriteAsync($"Invalid format for Header {TenantHeaderKey}"); | ||
return; | ||
} | ||
|
||
await next(context); | ||
} | ||
|
||
private bool IsTenantValid(string tenantId) | ||
{ | ||
return Regex.IsMatch(tenantId, "^[A-Za-z0-9-_]{0,40}$"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
using System.Globalization; | ||
using Microsoft.Data.Sqlite; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.Caching.Memory; | ||
using NorthwindCRUD.Helpers; | ||
|
||
namespace NorthwindCRUD.Providers | ||
{ | ||
public class DbContextConfigurationProvider | ||
{ | ||
private const string DefaultTenantId = "default-tenant"; | ||
private const string TenantHeaderKey = "X-Tenant-ID"; | ||
private const string DatabaseConnectionCacheKey = "Data-Connection-{0}"; | ||
|
||
private readonly IHttpContextAccessor context; | ||
private readonly IMemoryCache memoryCache; | ||
private readonly IConfiguration configuration; | ||
|
||
public DbContextConfigurationProvider(IHttpContextAccessor context, IMemoryCache memoryCache, IConfiguration configuration) | ||
{ | ||
this.context = context; | ||
this.memoryCache = memoryCache; | ||
this.configuration = configuration; | ||
} | ||
|
||
public void ConfigureOptions(DbContextOptionsBuilder options) | ||
{ | ||
var dbProvider = configuration.GetConnectionString("Provider"); | ||
|
||
if (dbProvider == "SqlServer") | ||
{ | ||
options.UseSqlServer(configuration.GetConnectionString("SqlServerConnectionString")); | ||
} | ||
else if (dbProvider == "SQLite") | ||
{ | ||
var tenantId = GetTenantId(); | ||
|
||
var cacheKey = string.Format(CultureInfo.InvariantCulture, DatabaseConnectionCacheKey, tenantId); | ||
|
||
if (!memoryCache.TryGetValue(cacheKey, out SqliteConnection connection)) | ||
{ | ||
var connectionString = this.GetSqlLiteConnectionString(tenantId); | ||
connection = new SqliteConnection(connectionString); | ||
memoryCache.Set(cacheKey, connection, GetCacheConnectionEntryOptions()); | ||
} | ||
|
||
// For SQLite in memory to be shared across multiple EF calls, we need to maintain a separate open connection. | ||
// see post https://stackoverflow.com/questions/56319638/entityframeworkcore-sqlite-in-memory-db-tables-are-not-created | ||
connection.Open(); | ||
|
||
options.UseSqlite(connection).EnableSensitiveDataLogging(); | ||
|
||
SeedDb(options); | ||
} | ||
} | ||
|
||
private static void SeedDb(DbContextOptionsBuilder optionsBuilder) | ||
{ | ||
using var dataContext = new DataContext(optionsBuilder.Options); | ||
DBSeeder.Seed(dataContext); | ||
} | ||
|
||
private static void CloseConnection(object key, object value, EvictionReason reason, object state) | ||
{ | ||
//Used to clear datasource from memory. | ||
(value as SqliteConnection)?.Close(); | ||
} | ||
|
||
private MemoryCacheEntryOptions GetCacheConnectionEntryOptions() | ||
{ | ||
var defaultAbsoluteCacheExpirationInHours = this.configuration.GetValue<int>("DefaultAbsoluteCacheExpirationInHours"); | ||
var cacheEntryOptions = new MemoryCacheEntryOptions | ||
{ | ||
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(defaultAbsoluteCacheExpirationInHours), | ||
}; | ||
|
||
cacheEntryOptions.RegisterPostEvictionCallback(CloseConnection); | ||
|
||
return cacheEntryOptions; | ||
} | ||
|
||
private string GetSqlLiteConnectionString(string tenantId) | ||
{ | ||
var connectionStringTemplate = configuration.GetConnectionString("SQLiteConnectionString"); | ||
var unsanitizedConntectionString = string.Format(CultureInfo.InvariantCulture, connectionStringTemplate, tenantId); | ||
var connectionStringBuilder = new SqliteConnectionStringBuilder(unsanitizedConntectionString); | ||
var sanitizedConntectionString = connectionStringBuilder.ToString(); | ||
|
||
return sanitizedConntectionString; | ||
} | ||
|
||
private string GetTenantId() | ||
{ | ||
return context.HttpContext?.Request.Headers[TenantHeaderKey].FirstOrDefault() ?? DefaultTenantId; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters