Permalink
Browse files

Merge pull request #40 from saaskit/dev

v1.14
  • Loading branch information...
benfoster committed Jul 17, 2016
2 parents 03dfe3d + 8a8a1a9 commit b761ef9295331f9758c5347d8b50b9def7b3589b
View
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.24720.0
+VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C3B2A3F2-5EF9-4128-8035-255A1AC1AF7B}"
ProjectSection(SolutionItems) = preProject
@@ -82,6 +82,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF
ClientId = clientId,
ClientSecret = clientSecret
+
});
}
});
@@ -93,9 +93,9 @@
"web.config"
]
},
- "scripts": {
- "prepublish": [ "bower install", "dotnet bundle" ],
- "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
- },
+ "scripts": {
+ "prepublish": [ "bower install", "dotnet bundle" ],
+ "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
+ },
"userSecretsId": "aspnet5-AspNetMvcAuthSample-20160229033356"
}

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -0,0 +1 @@
+// Write your Javascript code.
@@ -4,16 +4,18 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
+using SaasKit.Multitenancy;
namespace AspNetMvcSample.Controllers
{
- public class HomeController : Controller
+
+ public class HomeController : Controller
{
- private AppTenant tenant;
+ private readonly AppTenant tenant;
- public HomeController(AppTenant tenant)
+ public HomeController(ITenant<AppTenant> tenant)
{
- this.tenant = tenant;
+ this.tenant = tenant?.Value;
}
public IActionResult Index()
@@ -88,8 +88,8 @@
"web.config"
]
},
- "scripts": {
- "prepublish": [ "bower install", "dotnet bundle" ],
- "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
- }
+ "scripts": {
+ "prepublish": [ "bower install", "dotnet bundle" ],
+ "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
+ }
}

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -0,0 +1 @@
+// Write your Javascript code.
@@ -27,6 +27,7 @@
}
},
"tools": {
+ "BundlerMinifier.Core": "2.0.238",
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
},
"buildOptions": {
@@ -50,6 +51,7 @@
]
},
"scripts": {
+ "prepublish": [ "bower install", "dotnet bundle" ],
"postpublish": "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%"
}
}
@@ -23,12 +23,13 @@
}
}
},
- "tools": {
- "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
- "version": "1.0.0-preview2-final",
- "imports": "portable-net45+win8+dnxcore50"
- }
- },
+ "tools": {
+ "BundlerMinifier.Core": "2.0.238",
+ "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
+ "version": "1.0.0-preview2-final",
+ "imports": "portable-net45+win8+dnxcore50"
+ }
+ },
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
@@ -49,8 +50,8 @@
"web.config"
]
},
- "scripts": {
- "prepublish": [ "bower install", "dotnet bundle" ],
- "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
- }
+ "scripts": {
+ "prepublish": [ "bower install", "dotnet bundle" ],
+ "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
+ }
}
@@ -1,45 +1,49 @@
{
- "version": "1.1.3",
- "description": "Multi-tenancy support for ASP.NET Core using StructureMap.",
- "authors": [ "Ben Foster" ],
+ "version": "1.1.4",
+ "description": "Multi-tenancy support for ASP.NET Core using StructureMap.",
+ "authors": [ "Ben Foster" ],
"packOptions": {
"authors": [ "Ben Foster" ],
"owners": [ "Ben Foster" ],
- "tags": [ "Multi-tenancy", "Multi-tenant", "Saas", "StructureMap" ],
- "releaseNotes": "Updated to support ASP.NET Core RC-2",
+ "tags": [ "Multi-tenancy", "Multitenant", "Multitenancy", "Multitenant", "Saas", "StructureMap" ],
+ "releaseNotes": "Updated to support ASP.NET Core RTM. Resolves an issue with MemoryCache cache dependencies.",
"projectUrl": "http://saaskit.net",
"licenseUrl": "https://github.com/saaskit/saaskit/blob/master/LICENSE.md",
+ "requireLicenseAcceptance": false,
"repository": {
"type": "git",
"url": "https://github.com/saaskit/saaskit/"
}
},
- "dependencies": {
- "NETStandard.Library": "1.6.0",
- "Microsoft.AspNetCore.Http.Abstractions": "1.0.0",
- "Microsoft.AspNetCore.Http.Extensions": "1.0.0",
- "Microsoft.Extensions.Caching.Abstractions": "1.0.0",
- "Microsoft.Extensions.Caching.Memory": "1.0.0",
- "Microsoft.Extensions.Logging.Abstractions": "1.0.0",
- "StructureMap.Dnx": "0.5.1-rc2-final",
- "SaasKit.Multitenancy": { "target": "project" }
- },
- "frameworks": {
- "net46": { },
- "net452": { },
- "net451": { },
- "netstandard1.6": {
- "dependencies": {
- "Microsoft.CSharp": "4.0.1-rc2-*",
- "System.Collections": "4.0.11-rc2-*",
- "System.Linq": "4.1.0-rc2-*",
- "System.Runtime": "4.1.0-rc2-*",
- "System.Threading": "4.0.11-rc2-*"
- },
- "imports": [
- "dotnet5.4",
- "portable-net45+win8"
- ]
- }
- }
+ "dependencies": {
+ "NETStandard.Library": "1.6.0",
+ "Microsoft.AspNetCore.Http.Abstractions": "1.0.0",
+ "Microsoft.AspNetCore.Http.Extensions": "1.0.0",
+ "Microsoft.Extensions.Caching.Abstractions": "1.0.0",
+ "Microsoft.Extensions.Caching.Memory": "1.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "1.0.0",
+ "StructureMap.Dnx": "0.5.1-rc2-final",
+ "SaasKit.Multitenancy": { "target": "project" }
+ },
+ "frameworks": {
+ "net46": {},
+ "net452": {},
+ "net451": {},
+ "netstandard1.6": {
+ "dependencies": {
+ "Microsoft.CSharp": "4.0.1",
+ "System.Collections": "4.0.11",
+ "System.Linq": "4.1.0",
+ "System.Runtime": "4.1.0",
+ "System.Threading": "4.0.11"
+ },
+ "imports": [
+ "dotnet5.4",
+ "portable-net45+win8"
+ ]
+ }
+ },
+ "buildOptions": {
+ "optimize": true
+ }
}
@@ -4,9 +4,12 @@
namespace SaasKit.Multitenancy
{
- /// <summary>
+ using System.Diagnostics;
+
+ /// <summary>
/// Helper class that will throw exceptions when conditions are not satisfied.
/// </summary>
+ [DebuggerStepThrough]
public static class Ensure
{
/// <summary>
@@ -0,0 +1,11 @@
+namespace SaasKit.Multitenancy
+{
+ /// <summary>
+ /// Used to retreive configured TTenant instances.
+ /// </summary>
+ /// <typeparam name="TTenant">The type of tenant being requested.</typeparam>
+ public interface ITenant<out TTenant>
+ {
+ TTenant Value { get; }
+ }
+}
@@ -4,7 +4,7 @@
namespace SaasKit.Multitenancy.Internal
{
- public class TenantResolutionMiddleware<TTenant>
+ public class TenantResolutionMiddleware<TTenant>
{
private readonly RequestDelegate next;
private readonly ILogger log;
@@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
namespace SaasKit.Multitenancy
@@ -11,22 +13,28 @@ public abstract class MemoryCacheTenantResolver<TTenant> : ITenantResolver<TTena
{
protected readonly IMemoryCache cache;
protected readonly ILogger log;
+ protected readonly MemoryCacheTenantResolverOptions options;
public MemoryCacheTenantResolver(IMemoryCache cache, ILoggerFactory loggerFactory)
+ : this(cache, loggerFactory, new MemoryCacheTenantResolverOptions())
+ {
+ }
+
+ public MemoryCacheTenantResolver(IMemoryCache cache, ILoggerFactory loggerFactory, MemoryCacheTenantResolverOptions options)
{
Ensure.Argument.NotNull(cache, nameof(cache));
Ensure.Argument.NotNull(loggerFactory, nameof(loggerFactory));
+ Ensure.Argument.NotNull(options, nameof(options));
this.cache = cache;
this.log = loggerFactory.CreateLogger<MemoryCacheTenantResolver<TTenant>>();
+ this.options = options;
}
protected virtual MemoryCacheEntryOptions CreateCacheEntryOptions()
{
return new MemoryCacheEntryOptions()
- .SetSlidingExpiration(new TimeSpan(1, 0, 0))
- .RegisterPostEvictionCallback((key, value, reason, state)
- => DisposeTenantContext(key, value as TenantContext<TTenant>));
+ .SetSlidingExpiration(new TimeSpan(1, 0, 0));
}
protected virtual void DisposeTenantContext(object cacheKey, TenantContext<TTenant> tenantContext)
@@ -67,7 +75,7 @@ protected virtual void DisposeTenantContext(object cacheKey, TenantContext<TTena
if (tenantIdentifiers != null)
{
- var cacheEntryOptions = CreateCacheEntryOptions();
+ var cacheEntryOptions = GetCacheEntryOptions();
log.LogDebug("TenantContext:{id} resolved. Caching with keys \"{tenantIdentifiers}\".", tenantContext.Id, tenantIdentifiers);
@@ -85,5 +93,35 @@ protected virtual void DisposeTenantContext(object cacheKey, TenantContext<TTena
return tenantContext;
}
+
+ private MemoryCacheEntryOptions GetCacheEntryOptions()
+ {
+ var cacheEntryOptions = CreateCacheEntryOptions();
+
+ if (options.EvictAllEntriesOnExpiry)
+ {
+ var tokenSource = new CancellationTokenSource();
+
+ cacheEntryOptions
+ .RegisterPostEvictionCallback(
+ (key, value, reason, state) =>
+ {
+ tokenSource.Cancel();
+ })
+ .AddExpirationToken(new CancellationChangeToken(tokenSource.Token));
+ }
+
+ if (options.DisposeOnEviction)
+ {
+ cacheEntryOptions
+ .RegisterPostEvictionCallback(
+ (key, value, reason, state) =>
+ {
+ DisposeTenantContext(key, value as TenantContext<TTenant>);
+ });
+ }
+
+ return cacheEntryOptions;
+ }
}
}
@@ -0,0 +1,29 @@
+namespace SaasKit.Multitenancy
+{
+ /// <summary>
+ /// Configuration options for <see cref="MemoryCacheTenantResolver{TTenant}"/>.
+ /// </summary>
+ public class MemoryCacheTenantResolverOptions
+ {
+ /// <summary>
+ /// Creates a new <see cref="MemoryCacheTenantResolverOptions"/> instance.
+ /// </summary>
+ public MemoryCacheTenantResolverOptions()
+ {
+ EvictAllEntriesOnExpiry = true;
+ DisposeOnEviction = true;
+ }
+
+ /// <summary>
+ /// Gets or sets a setting that determines whether all cache entries for a <see cref="TenantContext{TTenant}"/>
+ /// instance should be evicted when any of the entries expire. Default: True.
+ /// </summary>
+ public bool EvictAllEntriesOnExpiry { get; set; }
+
+ /// <summary>
+ /// Gets or sets a setting that determines whether cached tenant context instances should be disposed
+ /// when upon eviction from the cache. Default: True.
+ /// </summary>
+ public bool DisposeOnEviction { get; set; }
+ }
+}
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection.Extensions;
using SaasKit.Multitenancy;
+using SaasKit.Multitenancy.Internal;
using System.Reflection;
namespace Microsoft.Extensions.DependencyInjection
@@ -18,15 +19,15 @@ public static class MultitenancyServiceCollectionExtensions
// No longer registered by default as of ASP.NET Core RC2
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
- // Make Tenant and TenantContext injectable
- services.AddScoped(prov =>
- prov.GetService<IHttpContextAccessor>()?.HttpContext?.GetTenant<TTenant>());
+ // Make Tenant and TenantContext injectable
+ services.AddScoped(prov => prov.GetService<IHttpContextAccessor>()?.HttpContext?.GetTenantContext<TTenant>());
+ services.AddScoped(prov => prov.GetService<TenantContext<TTenant>>()?.Tenant);
- services.AddScoped(prov =>
- prov.GetService<IHttpContextAccessor>()?.HttpContext?.GetTenantContext<TTenant>());
+ // Make ITenant injectable for handling null injection, similar to IOptions
+ services.AddScoped<ITenant<TTenant>>(prov => new TenantWrapper<TTenant>(prov.GetService<TTenant>()));
- // Ensure caching is available for caching resolvers
- var resolverType = typeof(TResolver);
+ // Ensure caching is available for caching resolvers
+ var resolverType = typeof(TResolver);
if (typeof(MemoryCacheTenantResolver<TTenant>).IsAssignableFrom(resolverType))
{
services.AddMemoryCache();
Oops, something went wrong.

0 comments on commit b761ef9

Please sign in to comment.