Giter Site home page Giter Site logo

saaskit's Introduction

SaasKit

Join the chat at https://gitter.im/saaskit/saaskit

SaasKit is a .NET toolkit for building SaaS (Software As A Service) applications.

The goal of the project is to help developers build SaaS products without getting in the way. It aims to be platform agnostic and as simple to use as possible.

Getting Started

ASP.NET MVC 5 / Web API 2
ASP.NET Core

Contribute

You can contribute in a number of ways. If you have questions or would like to discuss the future development of SaasKit, please join us on Gitter or create a GitHub issue. It's always a good idea to chat with us before embarking on a big pull request to ensure your developments align with the goals of the project.

Support

This project is open source and I do my best to support it in my spare time. If you'd like to discuss commercial support options or need help implementing multi-tenancy or building SAAS applications in .NET then get in touch. If you want to check my credentials - check out my blog or Fabrik, the SAAS startup I built on the .NET stack.

saaskit's People

Contributors

benfoster avatar gitter-badger avatar joeaudette avatar rdefreitas avatar rondefreitas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

saaskit's Issues

Does SaasKit support loading routes per tenant?

The above question also relates a lot to the ability to load modularized parts of code with SaasKit per tenant as these modules would also bring with them controllers with attribute routes defined.

ASP.NET Core Logger solution for muti-tenancy.

Saaskit is a great resource, and solving muti-tenancy just fine for us.

I'm hoping you might have an elegant solution for the ASP.NET Core ILogger. I've started with this, but of course the loggerFactory is a global and nature.

`
// Setup per-tenant middleware pipeline.

        app.UsePerTenant<AppTenant>((ctx, builder) =>
        {
            // Logging
            builder.Use(async (context, next) =>
            {
                loggerFactory.AddSerilog(GetSerilogLogger(ctx.Tenant.LogConnectionString));
                await next.Invoke();
            });
        });

`

UsePerTenant and Authentication

Hi Ben -

Saaskit is amazing! Following your examples, I've been able to make almost everything work, with the exception of multi-tenant authentication using the "UsePerTenant" extension with OIDC.

When I start the app, it lands on the home page fine (auth not required) but when I hit a controller method decorated with [Authorize], I get the following error:

System.InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic

Here is the "configure" method from my startup:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMultitenancy<AppTenant>();
        app.UsePerTenant<AppTenant>((ctx, builder) =>
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationScheme = $"{ctx.Tenant.Id}.Cookies",
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                SlidingExpiration = true,
                CookieName = $"{ctx.Tenant.Id}.AspNet.Cookies"
            });

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
            {
                AuthenticationScheme = "oidc",
                SignInScheme = $"{ctx.Tenant.Id}.Cookies",
                Authority = "http://localhost:5555",
                RequireHttpsMetadata = false,
                ClientId = ctx.Tenant.Hostname,
                ClientSecret = "secret",
                ResponseType = "code id_token",
                Scope = { "openid", "profile", "roles", "mtt_webapi", "offline_access" },
                GetClaimsFromUserInfoEndpoint = true,
                SaveTokens = true,
                TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                }
            });
        });

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
  }

Any advice you could give would be largely appreciated. I'm at a loss.

Thanks!

Are there any way to configure ServiceCollections per route?

Hi,

When I using owin I combine web application (using Map) at single host to provide api from single port. Write modular deploy monolith think :)

Here the example: https://github.com/oguzhaneren/OwinComposition

In asp.net core, ServiceCollections initiated before middleware configuration so I cannot configure servicecollection per web application.

Another problem for me: if I have register and configure Mvc in ConfigureServices(IServiceCollection services) method, I couldn't use diffent mvc configuration in the other web application.

I need to host multiple web project at the same port in one application instance. Is there any way?

Thanks,

Provide a config file tenant resolver

For scenarios that don't require automated onboarding or tenant provisioning or where the number of tenants does not change that often, provide a configuration file based tenant resolver.

Resolving Tenants in ReBus

Hi,

We are working on a project which is using Rebus. Since we are already using SaasKit for tenant resolution for the HTTP Context we have created a first rough implementation which is able to resolve tenants based on Rebus in stead of HTTP. This could come in handy for other developers as well.

At this moment Saaskit is build directly on the HttpContext, but if we can abstract this further we can create different implementations.

  • Http implementation
  • Rebus implementation
  • ...

If you are interested in this, we could work this out, but their will be some major changes in the project structure.

At this moment the biggest change is that the TenantResolver is using an Object as parameter

public interface ITenantResolver<TTenant> { Task<TenantContext<TTenant>> ResolveAsync(object context); }

Afterwards the correct context is choosen.
You can see more details at https://github.com/robvanpamel/saaskit

Kind regards
Rob

Skip checking tenant id, everytime, attaching tenant Id to natively to user object

Hi, in the context where the tennats are sharing the same database, I was going through this and I had a concern, if the source is tampered where the tenant Id was edited, would the other tenants data be corrupted/exposed.

Would it be possible to natively extended/integrate the user object to include the tenant Id, where we can transparently continue the operations on the serverside, without have to check the tenant every-time before a save or edit?

Thanks

TenantPipelineMiddleware

This is an error that appear multiple time in my log.

System.OperationCanceledException: The operation was canceled.
at System.Threading.CancellationToken.ThrowOperationCanceledException()
at Microsoft.AspNetCore.Http.Extensions.StreamCopyOperation.d1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.StaticFiles.StaticFileContext.d47.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at SaasKit.Multitenancy.Internal.TenantPipelineMiddleware1.d__5.MoveNext() in C:\Users\eddy\Documents\eddy\src\SaasKit.Multitenancy\Internal\TenantPipelineMiddleware.cs:line 44 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at SaasKit.Multitenancy.Internal.TenantResolutionMiddleware1.d3.MoveNext() in C:\Users\eddy\Documents\eddy\src\SaasKit.Multitenancy\Internal\TenantResolutionMiddleware.cs:line 42
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.ApplicationInsights.AspNetCore.ExceptionTrackingMiddleware.d4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.d__6.MoveNext()

WebApi 2 Examples

Your docs say "More samples coming soon!" since 8 Jul 2015

Do you have any example how to resolve the tenent from a route fragment?
/api/whatever/{tenantid}/something

StructureMap Child Container - being disposed?

Hello,

I am using the per tenant depenency injection, I have followed the example.

The first request to my application works just fine.
However the second request this happens:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'StructureMap Child/Profile Container'.
   at StructureMap.Container.assertNotDisposed()
   at StructureMap.Container.GetNestedContainer()
   at SaasKit.Multitenancy.StructureMap.Internal.MultitenantContainerMiddleware`1.<Invoke>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at SaasKit.Multitenancy.Internal.TenantResolutionMiddleware`1.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Gluon.Module.Migrator.Middleware.MigrationsCheckerMiddleware.<Invoke>d__6.MoveNext() in D:\Repos\gluon2\src\Gluon.Module.Migrator\Middleware\MigrationsCheckerMiddleware.cs:line 103
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Gluon.Module.Migrator.Middleware.SystemSetupMiddleware.<Invoke>d__9.MoveNext() in D:\Repos\gluon2\src\Gluon.Module.SystemSetup\Middleware\SystemSetupMiddleware.cs:line 48
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.<Invoke>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__6.MoveNext()

I'm not sure where I am going wrong. Does the child container get disposed and re-initialised on every request?

Get the tenant while in the Global.asax or Startup.cs

Hi mate!
I hope you can help me. Im using SaaSkit with an asp.net mvc project.

I'm not using Nancy as I don't want to rewrite all my controller code.

I would like to add the tenant name into the ViewEngines view locator, but this is best to do in the application_start phase of the bootstrapping and the HttpContext here cannot get the current tenant id as it does not resolve yet.

Can you advise whether it will be possible to get the tenant at application_start phase so that view search locations may be initialised using the tenant data?

Thanks heaps. This is a great system and got it all working so far except for this..

Regs.

Could not create a model binder for model object of type 'SaasKit.Multitenancy.ITenant

Hi
I have very odd issue. I implement SaasKit example in my project and when I try to inject AppTenant in HomeController I get issue:
InvalidOperationException: Could not create a model binder for model object of type 'SaasKit.Multitenancy.ITenant1[Core.Multitenancy.AppTenant]'.`

In my HomeController I have ctor like:
public IActionResult Index(ITenant<AppTenant> tenantt)

If I put
public IActionResult Index(AppTenant tenantt)
then I don't get issue, but all tenant properties are null.

I can confirm that AppTenantResolver are able to resolve tenant and put object in TenantContext.
Also, on request, in Startup.Configure, in context I'm able to see selected tenant in Context.Items, with all values.

In ConfigureServices I have
services.AddMultitenancy<AppTenant, CachingAppTenantResolver>();
and

services.Configure<RazorViewEngineOptions>(options =>
{
   options.ViewLocationExpanders.Add(new TenantViewLocationExpander());
});
services.Configure<MultitenancyOptions>(Configuration.GetSection("Multitenancy"));

in Configure

app.UseMultitenancy<AppTenant>();

after app.UseStaticFiles(); and before app.UseMvc

Any idea what can be wrong?
Thanks.

Crash when TenantResolver returns a null Tenant

Using nuget 0.0.2pre it's not possible to return null in a resolver's ResolveAsync method - e.g. where no Tenant can be located. Returning null causes a NullReferenceException in TenantResolutionMiddleware.cs.

Easily reproduced in the MVC Sample project

UserStore multitenancy

I am wondering whether it is possible to use saaskit with asp net identity in such a way that each tenant has its own user store.

I have created a custom user store in a way that is somewhat similar to this blog post. However I am using AspNet Identity Core.

Any suggestion?

Memory Cache Resolver should check tenant identifiers is not null

This line will throw if derived class returns null

                if (tenantContext != null)
                {
                    var tenantIdentifiers = GetTenantIdentifiers(tenantContext);
                    var cacheEntryOptions = CreateCacheEntryOptions();

                    log.LogDebug("TenantContext:{id} resolved. Caching with keys \"{tenantIdentifiers}\".", tenantContext.Id, tenantIdentifiers);

                    foreach (var identifier in tenantIdentifiers)
                    {
                        cache.Set(identifier, tenantContext, cacheEntryOptions);
                    }
                }

TenantContext and concurrency

Given that TenantContext can be cached in a MemoryCache - it means the same instance can be re-used on multiple concurrent requests. Given that is the case, this code with the dictionary doesn't look thread safe?

https://github.com/saaskit/saaskit/blob/master/src/SaasKit.Multitenancy/TenantContext.cs#L20

I am wondering if attempting to keep a bunch of state (properties) for the tenant is a good idea. As a proposal, how about:

  1. Scrap TenantContext.
  2. if people want state associated with the tenant, they can properties to their own custom TTenant implementation.

What do you think?

DI issue

Hi, I read the blogs, thanks for the great library!

I am just trying to use Saaskit with my existing application, but hitting this error when a I run the application and browse to a page that has the Tenant injected:

Autofac.Core.DependencyResolutionException: A delegate registered to create instances of 'SaasKit.Multitenancy.TenantContext`1[Gluon.Module.Tenant.Data.TenantDomainApplication]' returned null.
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.MultitenancyServiceCollectionExtensions.<>c__0`2.<AddMultitenancy>b__0_1(IServiceProvider prov)
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetRequiredService(Type serviceType)
   at Microsoft.AspNetCore.Mvc.Razor.RazorPageActivator.<>c__DisplayClass16_0.

I am using Autofac.

My configure services looks like this:

             services.AddDbContext<AppTenantContext>(options => options.UseSqlServer(connection));         
            services.AddScoped<IAppTenantContext>(provider => provider.GetService<AppTenantContext>());

            services.AddMultitenancy<TenantDomainApplication, CachingAppTenantResolver>();

and Configure():

  app.UseMultitenancy<TenantDomainApplication>();

I'm then injecting the tenant into my _Layout.cshtml, and I get this error when I browse to a page..

Dbcontext Migration not working sue to AppTenant Null

The below code is not working , The AppTenant is null in the SqlServerApplicationDbContext constructor.
please help he how to fix this issue.

using (var serviceScope = app.ApplicationServices.GetRequiredService()
.CreateScope())
{
serviceScope.ServiceProvider.GetService()
.Database.Migrate();
}

ChildContainer disposing of TenantContext

This bug can be seen with the sample if you make a few tweaks.

Problem

Take the structure map sample, then add a service or controller which has the AppTenant injected into it's constructor.

What seems to happen is this:

  1. The middleware sets HttpContext.RequestServices to a nested container.
  2. After downstream middleware has been awaited (i.e and your service / controller has been resolved) the TenantContext appears to now be registered in the nested container.
  3. The middleware disposes of the nested container, which now triggers a dispose on the TenantContext.
  4. The TenantContext disposes of it's properties, including the TenantContainer but it's left in the memory cache
  5. Subsequent requests, the middleware resolves the TenantContext from the cache (the disposed instance) and get;s the disposed TenantContainer which it attempts to use, resulting in an exception.

The reason you don't see this problem in the current sample, is because AppTenant is not injected anywhere. You also only see it from the second request onwards.

InvalidOperationException: Unable to resolve service for type

I am using saaskit multi tenancy project for multi tenant application.
I am able to create the tenant context and able to access in all controllers.
However, sometimes it give me following error

An unhandled exception has occurred: Unable to resolve service for type 'LMS.Model.AppTenant' while attempting to activate 'LMS.Controllers.OrganizationController'.

This error occurs randomly for any controller and on any action method executed. The nature of error is not specific to any controller/action method.

I can see my code enters the TenantResolver successfully which does in fact return a Tenant but when it comes for DI to inject it into my controller it complains with the error listed

After this error occurs my application stop completely and it shows white screen.

This issue occurs on production server and not on localhost.

Any help on this !

Tenant injected in all controller like this

public class HomeController
    {
        public HomeController(TenantContext<AppTenant> tenantContext)
        {

        }
    }

and below is the extension class for where tenantContext is made injectable

public static class MultitenancyServiceCollectionExtensions
    {
        public static IServiceCollection AddMultitenancy<TTenant, TResolver>(this IServiceCollection services)
            where TResolver : class, ITenantResolver<TTenant>
            where TTenant : class
        {
            Ensure.Argument.NotNull(services, nameof(services));

            services.AddScoped<ITenantResolver<TTenant>, TResolver>();

            // 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?.GetTenantContext<TTenant>());
			services.AddScoped(prov => prov.GetService<TenantContext<TTenant>>()?.Tenant);

			// 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);
            if (typeof(MemoryCacheTenantResolver<TTenant>).IsAssignableFrom(resolverType))
            {
                services.AddMemoryCache();
            }

            return services;
        }
}

When the white screen appears it gives me

Network Error: 500 Internal Server Error Could not find the related path.

I have set TenanContext in Session in AppTenantResolver class, so that I do not need to resolve tenant again and again. But this session value sometimes comes null for any action method executed. I have set like context.Session.SetObjectAsJson("TenantContext", tenantContext)

Why this HttpContext session comes null sometimes ? Because of this I am not able to get the tenant context

Api Controller TTenant Always null

When i use the following code in a api controller then the tenant will be always null also in the Sample Code

public IHttpActionResult Get()
        {
            var tenant = Request.GetTenant<AppTenant>();
            return Ok(tenant);
        }

Default Tenant Instance

So that SaasKit can even be used in single-tenant scenarios, a default tenant instance should always be provided so you can use the library OOTB.

This could be done by providing an "Always matching" ITenantResolver on the current SaasKitConfiguration.

It should also be possible to override the "default" tenant or provide a fallback in multi-tenant scenarios i.e. if no other tenants match, use "this" one.

Hadle 404 issue in saaskit

Hello All,

How can I handle 404 error using saaskit ?

I have tried with several way, but still I am getting the white screen if my url is not matched.

Below code I have added in Startup.cs to handle the session time out issue


app.Use(async (ctx, next) =>
            {
                if (ctx.GetTenantContext<AppTenant>() == null && !ctx.Request.Path.ToString().Contains("/Home/Redirect"))
                {

                    string redirect = Configuration["RedirectUrlSettings:RedirectUrl"];
                    string appTye = ctx.Request.Headers["Referer"];
                    bool logoff = ctx.Request.Path.ToString().Contains("/Account/LogOff") ? true : false;

                    if (appTye != null)
                    {
                        redirect += appTye.Contains("Admin") ? "/Home/Redirect/Admin/" + logoff : "/Home/Redirect/Trainee/" + logoff;

                        ctx.Response.Redirect(redirect);
                    }

                }
                else
                {
                    if (ctx.Response.StatusCode == Convert.ToInt32(HttpStatusCode.NotFound))
                    {
                        ctx.Response.Redirect("/Home/Error/{0}");
                    }
                    await next();
                }
            });

I am looking for this urgently !

Thanks for the help.

Regards,
Rohit

Tenant IoC support

The ability to scope dependencies per tenant, ideally container agnostic.

Sample code works only for one tenant

The sample code works for only one tenant. Ex:- localhost:60000 works. But if i enter Tenant 2 url: localhost:60002 it just says unable to connect. It doesn't break in TenantResolver either.

I haven't change a single line of code.

Am I doing something wrong?

Samples page missing

The Samples page for ASP.NET MVC - Basic in-memory resolution based on hostname is returning a 404.

Identity Cookie and multi tenancy

I was wondering whether it was possible to use saaskit with identity in such a way that each tenant has its own application sign in cookie.
During startup, in configureservices(), when you AddIdentity() you can configure the cookie details (its name, the domain etc) however it doesnt appear you can configure identity for multiple seperate domains. I'm wondering if anyone has tried the structuremap approach perhaps with identity, so that identity is configured per tenant?

Support multiple tenant identifiers

A fairly common scenario (that we ourselves have to cater for at fabrik) is that a tenant may have multiple hostnames mapped to the application.

The tenant resolve interface should support returning additional identifiers when a tenant is resolved so that we do not resolve duplicate instances.

It would also be beneficial to maintain some heirarchy of identifiers so that it would be possible to redirect to a primary url (or perform some other action) should the primary identifier not be used.

Unable to resolve service for type 'Tenant' while attempting to activate 'HomeController'.

Hi,

I get the following error which I am really struggling to nail down.
Unable to resolve service for type 'Tenant' while attempting to activate 'HomeController'.

I can see my code enters the TenantResolver successfully which does in fact return a Tenant but when it comes for DI to inject it into my controller it complains with the error listed.

Are there any further steps I am missing to debug?

Thanks,

AspNetCore RC2: Kestrel server won't bind to multiple server.urls

The server is not honoring server url bindings when launching web apps using .Net Core RC2 VS tools, eg this doesn't work:

var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseUrls("http://localhost:60000", "http://localhost:60002")
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

host.Run();

However, everything works fine when launching this way:

project-root > dotnet run

I've been digging around, and can't find much on this topic other than a few related issues:

I tried moving the urls to a server.urls variable in an external hosting.json file and using the configuration builder as suggested in the 2nd link, but that's not working either.

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("hosting.json", optional: true)
    .Build();

var host = new WebHostBuilder()
    .UseKestrel()
    .UseConfiguration(config)
    .UseContentRoot(Directory.GetCurrentDirectory())         
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

host.Run();

//  contents of hosting.json, 
// {
//   "server.urls": "http://localhost:60000 http://localhost:60002"
// }

The hosting.json file is located in the project root along side project.json and was created based on the only example I could find:

Although seemlingly promising, that didn't work either.Are you having this issue too or has anyone else reported it?

I'm using 1.0.0-preview1-002702.

Does not compile with latest ASP MVC core - dependecy issues

Hi, I was trying to run the samples with the current ASP MVC Core release (latest) and it would not compile, it seems there are many dependency issues that the nuget is not able to resolve.

Since there were quite a few, I was not able to resolve the dependencies myself can you please update nugets and check-in .

thanks
Papy

SaasKit.Multitenancy.Mvc Not found

Hi,
I am new to SaasKit and wants to use my MVC 5 application.

When trying to add this package I am getting below error
PM> Install-Package SaasKit.Multitenancy.Mvc
Install-Package : Unable to find package 'SaasKit.Multitenancy.Mvc'
At line:1 char:1

  • Install-Package SaasKit.Multitenancy.Mvc
  •   + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
      + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand
    
    

Can you please help me how to add mvc package into my application.

Support for .Net Core 1.0

Please add support for .Net Core 1.0, on my end I've tried and it's just changing the project.json and it will compile just fine. Thanks for your time

Trying to get started with SaasKit but...

Hello,
I'm implementing (or at least trying to implement) my first multi-tenant app using SaasKit by following the post in http://benfoster.io/blog/asp-net-5-multitenancy "Building multi-tenant applications with ASP.NET Core (ASP.NET 5)". But when I open up cmd prompt and type dnx . web I get:

D:\saaskit-master\Multi-Tenant_Demo>dnx . web
System.InvalidOperationException: Failed to resolve the following dependencies f
or target framework 'DNXCore,Version=v5.0':
EntityFramework.Commands 7.0.0-beta5
EntityFramework.SqlServer 7.0.0-beta5
Microsoft.AspNet.Authentication.Cookies 1.0.0-beta5
Microsoft.AspNet.Authentication.Facebook 1.0.0-beta5
Microsoft.AspNet.Authentication.Google 1.0.0-beta5
Microsoft.AspNet.Authentication.MicrosoftAccount 1.0.0-beta5
Microsoft.AspNet.Authentication.Twitter 1.0.0-beta5
Microsoft.AspNet.Diagnostics 1.0.0-beta5
Microsoft.AspNet.Diagnostics.Entity 7.0.0-beta5
Microsoft.AspNet.Identity.EntityFramework 3.0.0-beta5
Microsoft.AspNet.Mvc 6.0.0-beta5
Microsoft.AspNet.Mvc.TagHelpers 6.0.0-beta5
Microsoft.AspNet.Server.IIS 1.0.0-beta5
Microsoft.AspNet.Server.WebListener 1.0.0-beta5
Microsoft.AspNet.StaticFiles 1.0.0-beta5
Microsoft.AspNet.Tooling.Razor 1.0.0-beta5
Microsoft.AspNetCore.Http 1.0.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.DependencyInjection.Abstractions 1.0.0
Microsoft.Extensions.Logging.Abstractions 1.0.0
Microsoft.Framework.Configuration.Abstractions 1.0.0-beta5
Microsoft.Framework.Configuration.Json 1.0.0-beta5
Microsoft.Framework.Configuration.UserSecrets 1.0.0-beta5
Microsoft.Framework.Logging 1.0.0-beta5
Microsoft.Framework.Logging.Console 1.0.0-beta5
Microsoft.VisualStudio.Web.BrowserLink.Loader 14.0.0-beta5
NETStandard.Library 1.6.0
SaasKit.Multitenancy 1.1.4

Searched Locations:
D:\TostoneT\saaskit-master{name}\project.json
D:\TostoneT\saaskit-master\src{name}\project.json
D:\TostoneT\saaskit-master\test{name}\project.json

Try running 'dnu restore'.

at Microsoft.Framework.Runtime.DefaultHost.GetEntryPoint(String applicationNa
me)
at Microsoft.Framework.ApplicationHost.Program.ExecuteMain(DefaultHost host,
String applicationName, String[] args)
at Microsoft.Framework.ApplicationHost.Program.Main(String[] args)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Framework.Runtime.Common.EntryPointExecutor.Execute(Assembly ass
embly, String[] args, IServiceProvider serviceProvider)
at dnx.host.Bootstrapper.RunAsync(List`1 args, IRuntimeEnvironment env)
at dnx.host.RuntimeBootstrapper.ExecuteAsync(String[] args)
at dnx.host.RuntimeBootstrapper.Execute(String[] args)

How can I resolve this issue?

MemoryCache eviction issue with multiple identifiers

When the memory cache evicts an identifier it disposes the TenantContext.
However there are no guard rails that it is the only identifier that used this TenantContext and as long as these cache entries stay warm, then these entries keep returning a disposed TenantContext.

I have fixed this in https://github.com/crowded/saaskit/commit/5e6897acc661acaf9fa0f878ede04e5e51edd349
For now but I have a few other ideas of dealing with this issue that might be neater.

EDIT:
It was late and I hadn't tested it through 'assumption is the...' ...
Your cache classes (ITenantResolver) are no singletons because you expect library users to only build it the MemoryCache way (or any other non direct cache object). Because otherwise you would have reasoned that this call might not have been the greatest to do if we are talking about classes that users might try to use for caching

// No singleton?! for cache classes?! 
services.AddScoped<ITenantResolver<TTenant>, TResolver>();

The next discovery was that right when I though I could just change this line it dawned on me that my own subclass of MemoryCacheResolver (which implements this ITenantResolver) rely on db connection's getting injected into them as an extra constructor argument so a singleton is out of the question as fresh/working connections are quite useful.

Then I thought ok lets do it like my proposed option 1. which then reminded me why I was so turned off by this solution in the first place as I now also need to add in my TenantIdentifiers while constructing the TenantContext... Ewhhh, and I thought it was gross already that I as a user even had to create the TenantContext in the first place.

Too many edge cases in the current implementation, the abstraction is wrong.

Ok rant is over, time to go to bed and rewrite this in the morning to the sane option 3. the dual layer model.
This gives you the minimum implementation burden as a library user and maximum control over the (optional) caching layer and other parts of the pipeline as the library author.

Dynamic Tenant Registration

Please consider the option of allowing dynamic tenant registration; my thoughts on the implementation would be to create a TenantResolver that uses a DistributedCache (allowing support for web farms) together with adding a new TenantRegisteration service to dependency injection, this service would/could then be used by a mvc view/controller update the backend persistent store and/or the DistributedCache with regards to tenant details and the endpoints (hostnames) for the tenant.

Redirection not working properly

Hi All,
I am trying to redirect to my controllers action method if tenant not found or in case of 404 error.
But redirection is going in loop only, I am using TenantUnresolved middleware.
It goeus to redirect method of middleware and then again goes to resolve the client and again redirect and so on
why this issue is occurring ?
Any help on this appreciated

Strong named assemblies support

I think the title describes the issue pretty good.

For my client's commercial software, I'd like to try out SaaSKit as it closely matches my own multi tenant structure, but we are using strong named assemblies so that'd require strong named assemblies for SaaSKit as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.