Get the source code for this video for FREE → the-dotnet-weekly.ck.page/multitenancy Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt As a few awesome viewers have noted - the Tenant ID should come from a JWT or auth Cookie. I hoped that part was obvious since this is more of a proof of concept for Multitenancy. I wanted to focus on the EF features you can leverage to achieve this. And I left out the part about safely providing the Tenant ID. In any case, I'm updating this comment for posterity. TL;DR: For a production implementation, get the Tenant ID from the JWT/Cookie instead of passing it in a header. Join The .NET Weekly → bit.ly/4bfozSz It's a newsletter with 53,000+ engineers that teaches you how to improve at .NET and software architecture.
Could it be a service behind the auth gateway, where the header value is assigned by a trusted service? Although a requirement to have the token with every request surely makes me feel more secure.
I never really found joy in backend development. After your videos about DDD, code architecture etc. Im finally enjoying writing backend code. Keep it going!
No, despite some good ideas I think they didn't want me due to lack of commercial experience with multi tenancy. Thanks for asking! @@MilanJovanovicTech
Thank you Milan. You tought me again something new. Two weeks ago i enrolled in your Pragmatic Clean Architecture course after followed you on LinkedIn and TH-cam.
This is exactly how I implemented multi-tenancy in my last application, but like you said in your pinned comment, also passed in the tenantId from a JWT. In addition, instead of of adding a query filter on each modelBuilder, I added a global query filter that adds this to all my entities matching a base type I created for everything that would be tenant-specific
Milan this is not usable, this is not the way how multitenancy will be implemented. Maybe if you would implement both sides: frontend and backend. But as soon as you make your backend API public, it's not possible that other side on frontend will be controlling access to data via custom HTTP header. It would mean that any user who can log-in could use any TenantId in header and access to data of other users/tenants. The better solution is to either have own AuthN server (or at least AuthZ function after external login) and return TenantId as a claim in a JWT token which is issued by the server side and can be verified and not modified by client. Second part of video - the connection string for tenants are normally saved for each tenant in the database and loaded for ex. once at login and stored in local browser storage. Also it's not needed to store TenantId for the records. Better solution is to store just CompanyId. Imagine that common function is to move company to other Tenant. In this case you just change the relation between CompanyId and other TenantId and you're good. You have to have function like "Check TenantId against Company" for controlling of access to data of different tenants.
I know, this is a 15-minute video, for crying out loud 😅 Did you expect me to give you a production-ready solution? If we just update the part for how we fetch the Tenant ID from a different source, everything else holds true.
@@orlandomalo7032 As I have written. You store the records just with CompanyId. The relation between TenantId and CompanyId you got in a separate table and maybe database for fast access at the time of login/authentication.
@@MilanJovanovicTech Unfortunatelly it's very misleading and this solution gives the public the wrong way how to do it. In this case there is no difference between "home" and "production" version. Either it is done right or not right. And this is not. In 15 Minutes is possible to make the same video with other more secure solution like with JWT token etc.
Thank you so much for the informative video. Multitenancy, in general, is a bit challenging, especially when it comes to managing tenants and their subscriptions through a dedicated tenant administration portal. This is in conjunction with data isolation strategies such as a Shared Database for All Tenants and a Separate Database Per Tenant. We dedicated the past four years to analyzing and studying the best practices that should be considered when building a SaaS app using the Multitenancy approach.
@@MilanJovanovicTech Yes, I do have :) I tried to post a long comment here but it gets removed automatically by YT algorithm due the restricted policy. Any workaround?
I like the way you present the different tenant scenarios and how you automagically apply the tenantId in your queries. I didn't know you could use DI in the application DB context, to use values from an external service. Is this possible if using separate configuration files in combination with assembly scanning? I understand that you are only showing the basics in this video, but wondering if an improvement for a further video could be, using and validating JWT tokens. By accepting and reading the tenantId from the JWT token you protect against simply changing the tenantId in the header (if that's sensitive). You could just reference your video on JWT tokens in this video.
That's basically the same thing, except you're checking against the tenant's id in the token. When dealing with cookies, you can have different cookie schemas for each tenant.
| Is this possible if using separate configuration files in combination with assembly scanning? - It should be, what do you think would prevent this? I was sure I mention it in the video, but yes - the TenantId is more likely to live in a cookie or JWT.
@@MilanJovanovicTech well, when you say it, its the same functionality and process (ie per request handling) but getting it wired up is different, so you're pro'll right.
For the production app you need to check if a logged in user has access to a passed X-TenantId header. That way another user couldn't get data than doesn't belong to his tenant even if he somehow figures out the tenant identifier. This year my team was implementing a multi-tenant modular monolith application. We had some niche requirements where a user could have access to multiple tenants, single tenant or all tenants (i.e: super admin) defending on a role. It was the hardest use case of multi tenancy implemented by my lead
Doing this right now with my team. Would love some details on what y’all did. We have considered using the Claims table to identify membership/specific permissions for each tenant by including the tenantId in this table. A super admin claim with the tech support tenantId grants admin access to everything.
I wonder if we could apply the query filter by convention if the entity thas the TenantId field, then apply automagically, instead of adding the query filter to every single entity (or worse, the new intern forgetting to add the query filter to a new entity)
I saw something like that in stackoverflow if i'm not mistaken, it applied a configuration to all entities that implemented an interface, in this case could be something like ITenantModel
You can have your entities implement an interface, like ITenantId. The interface will have a single TenantId property. And then you should be able to say: modelBuilder.Entity().HasQueryFilter(). *Don't take this as 100% true, I didn't check in my IDE. But that's the idea.
Be careful with query filters, they are not additive (well they weren’t before 8 I’ve not confirmed that yet). We had a filter like this to add the tenant id automatically to queries. But if you have another (in our case IDeletable) then the second filter replaces the first.
Thanks for bringing clarity to this topic. Can you break down how to resolve tenants by using a sub-domain? Perhaps tenant-resolution is an abstraction in itself - perhaps by header, subdomain and even http-query-parameter.
what about multi-tenant database per tenant? Meaning only the databases are whats separated they share the same web api. Its been hard to learn how to do this with ef core. My idea is ill have a master database to store all tenant info and connection strings. When a tenant signs up for example, ill create there database on azure. Use ef core, add them to the master database. When I need to connect to the tenant database, I query the master database, grab the connection string some how make ef core work with dynamic connection strings. Create maybe a initial setup, where you run the first migration (similar to update-database in package manager console) on the tenants database. However most of this is theory, would love a video on this.
How would you implement this if you wanted to allow the SaaS admin to also sign in to this application and perform some actions like inviting the company via email or issuing broadcast notification for all tenants? This would require a different entity than Company.
@@MilanJovanovicTech single db. The tenants are allocated to certain areas in the UK, but when a participant moves they will be transferred to a different tenant. A tenant can only view data that is linked to them, but they will get the data from the previous tenant to enable continuous service for the participant. (Project is in the criminal justice system so we get a lot of movement as people are transferred between and out of custody)
That's a completely separate concern, IMO. It just comes down to how you fetch the Tenant ID value. Instead of getting it from a header, you'd fetch it form a JWT. Or use the User ID on the JWT to read the Tenant ID from the database. These are minor details in the overall scheme of things.
Isn't it through TenantId that multiple databases determine which database to connect to? Before logging in, I don't know TenantId. How can I connect to the database and obtain user information? Is there a separate database for login logic?
Hi Milan, thank you for your videos, they are great. I really like the part about filtering with EF Core, thanks :) I just have one point: I am not a big fan of doing the GetTenantId in the constructor because if there is an exception, it will be wrapped in a DI container exception (Autofac dependency injection exception). What do you think of the Finbuckle library for multi-tenant? I like it.
Hello, How would you do it if the connection strings would have to be dynamic as well for the single tenancy case? Ex: Company x and Company y both registered within the application and you have to build dynamic databases for both of them without modifying the current app.configuration file I also enjoy your content, keep it up!! Thank you!
Have a set of migration SQL scripts that create the required database structure. Run it in the background when a company registers. Something along those lines.
Good episode 👍. It would be enlightening if you used a more realistic scenario, instead of headers, determined tenantId using custom claims extracted from JWT. An idea for a future episode.😉
Thanks for the effort, your videos are good and I appreciate the time like people yourself put into them. One question, in a Clean architecture with the core entities seperate from the main Infrastructure/Identity, what are people's view on creating entities that have relationships back to the individual user, rather than just the Tenant. For example, consider a "Keys" table maybe that is unique to the user logged in?
Hi Milan, I am a dedicated follower and student of your two courses. Do you have any plans for a course focused on multitenancy or SaaS applications? Any recommendations you could give me for my project? I would like to follow the strategy of using EF Core for commands and Dapper for queries. Additionally, I would like to use Keycloak as the identity provider for the different tenants. Thank you for your videos; they are very helpful for our projects and for improving as software architects.
This may seem like a novice question, but if the SasS application is going to be used for multiple domain names, where each spins up its own instance of the application with its individual frontend, if anyone has an example of how to solve that issue, I'd be very interested to see how thats done.
@@MilanJovanovicTech Thank you, Milan, that sounds exciting. If you can let me know what the channel name is or a link to it, that would be most helpful! Also, just wanted to say, your videos are pretty damned interesting, the subject matter you are choosing to cover is awesome. Thanks again!
Hi Milan, I would like to note here that the firtst approach with the EF Query Filters is applicable when your users or tenants exists outside of this application. For example I have an MVC asp net core application and users are inside the same application (monolith) which makes it a mutlitenant application since every user can only access and modify their own data and all of them exist in the same databse. However the approach with the app user resolver service did not work for me. THe reason is because the OnModelCreating method is only called once for the project's lifetime. So before anyone would login the query filters had alreaady instatiated with null as the tenantId. I was actually around this issue for some time but for this type of application i mentioned it seems it is not an applicable solution. Great video, Keep up the good work!
true but my DbContext is scoped by default however the OnModelCreating get executed only the first time. A similar issue was discussed on StackOverflow about tenants on the same application.@@MilanJovanovicTech
@@MilanJovanovicTech I disagree. Whilst it would typically require a wildcard SSL certificate (so that requests for multiple subdomains hit the ingress), I'm not sure that qualifies as whole lot more infrastructure as typically you'd have an ssl cert anyway. For local development you can add a few entries to your etc/hosts file e.g "foo.localhost" pointing to loopback ip. I would say it requires some very minor configuration to achieve. The main thing is whether its a requirement that tenants have their own subdomain. The benefits are it allows tenant identification prior to user authentication - so they can be served customised "public" pages and they can also have their own authentication / login page based on tenant settings - one tenant may use their own SAML idp versus another who uses native login. In other words the decision is more requirements based than infrastructural.
Come on… It is basic concept of multi tenancy. It’s a great presentation. For the goal of the POC it was kept simple as possible. Of course it can be extended based on everyone’s requirements. Important and focus is EF support here, because this can be achieved also without EF Core.
The TenantProvider can be registered as Singleton. HttpContextAccessor is also a singleton. It uses AsyncLocal to ensure the scope. So, it is not important to register it as scoped ;-)
Get the source code for this video for FREE → the-dotnet-weekly.ck.page/multitenancy
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
As a few awesome viewers have noted - the Tenant ID should come from a JWT or auth Cookie. I hoped that part was obvious since this is more of a proof of concept for Multitenancy. I wanted to focus on the EF features you can leverage to achieve this. And I left out the part about safely providing the Tenant ID. In any case, I'm updating this comment for posterity.
TL;DR: For a production implementation, get the Tenant ID from the JWT/Cookie instead of passing it in a header.
Join The .NET Weekly → bit.ly/4bfozSz
It's a newsletter with 53,000+ engineers that teaches you how to improve at .NET and software architecture.
Great, just defined my question and sec later found the answer below (being already published ;)
Could it be a service behind the auth gateway, where the header value is assigned by a trusted service? Although a requirement to have the token with every request surely makes me feel more secure.
I never really found joy in backend development. After your videos about DDD, code architecture etc. Im finally enjoying writing backend code. Keep it going!
I always found backend work much more enjoyable 😁
WOW. I recently had an interview about multitenancy, and I just popped these ideas out of my head - of course without implementation details.
Did you pass?
No, despite some good ideas I think they didn't want me due to lack of commercial experience with multi tenancy. Thanks for asking! @@MilanJovanovicTech
Adding a tenant_id claim inside a JWT is also a good option
It's better option. I have written comment to this also.
I'm actually implementing tenant_id instide the JWT token right now lol
Right, I was sure I mentioned that in the video but sadly I didn't. Updating my pinned comment!
JWT is never a good option for general authn/authz despite it's popularity that all it's sprukers are trying to roll back now they know better.
Thank you Milan. You tought me again something new. Two weeks ago i enrolled in your Pragmatic Clean Architecture course after followed you on LinkedIn and TH-cam.
Fantastic! Glad to hear that :)
This is exactly how I implemented multi-tenancy in my last application, but like you said in your pinned comment, also passed in the tenantId from a JWT. In addition, instead of of adding a query filter on each modelBuilder, I added a global query filter that adds this to all my entities matching a base type I created for everything that would be tenant-specific
Smart way to solve the query filter part!👌
Great idea!
Milan this is not usable, this is not the way how multitenancy will be implemented. Maybe if you would implement both sides: frontend and backend. But as soon as you make your backend API public, it's not possible that other side on frontend will be controlling access to data via custom HTTP header. It would mean that any user who can log-in could use any TenantId in header and access to data of other users/tenants. The better solution is to either have own AuthN server (or at least AuthZ function after external login) and return TenantId as a claim in a JWT token which is issued by the server side and can be verified and not modified by client. Second part of video - the connection string for tenants are normally saved for each tenant in the database and loaded for ex. once at login and stored in local browser storage. Also it's not needed to store TenantId for the records. Better solution is to store just CompanyId. Imagine that common function is to move company to other Tenant. In this case you just change the relation between CompanyId and other TenantId and you're good. You have to have function like "Check TenantId against Company" for controlling of access to data of different tenants.
I know, this is a 15-minute video, for crying out loud 😅 Did you expect me to give you a production-ready solution?
If we just update the part for how we fetch the Tenant ID from a different source, everything else holds true.
What do you mean by "it's not needed to store TenantId for records", isn't it necessary to store the TenantId?
@@orlandomalo7032 As I have written. You store the records just with CompanyId. The relation between TenantId and CompanyId you got in a separate table and maybe database for fast access at the time of login/authentication.
@@MilanJovanovicTech Unfortunatelly it's very misleading and this solution gives the public the wrong way how to do it. In this case there is no difference between "home" and "production" version. Either it is done right or not right. And this is not. In 15 Minutes is possible to make the same video with other more secure solution like with JWT token etc.
@@MaxSupercars I think you and I have a different perspective of what a TH-cam video should convey
At last! Can't wait to watch on bigger screen! Thx!
Sorry for the long wait 😅
I like your approach, but couldn't you create a DBContext Factory which returns you back a DBContext based on the tenantId you provided ?
Sure, seems possible
That's a beautiful solution! Thanks for sharing!
Glad you like it!
Awesome video. It was what I needed in my project. Thank you !
Glad it was helpful!
Thank you so much for the informative video. Multitenancy, in general, is a bit challenging, especially when it comes to managing tenants and their subscriptions through a dedicated tenant administration portal. This is in conjunction with data isolation strategies such as a Shared Database for All Tenants and a Separate Database Per Tenant. We dedicated the past four years to analyzing and studying the best practices that should be considered when building a SaaS app using the Multitenancy approach.
Do you have these best practices categorized somewhere? :)
@@MilanJovanovicTech Yes, I do have :) I tried to post a long comment here but it gets removed automatically by YT algorithm due the restricted policy. Any workaround?
@@BlazorPlate Try splitting the link like mywebsite . com
Thanks for the suggestion! Try without spaces :)
docs. google. com/ document /d / 1cl37hdOfTLjVDo80gE5TPu7QDW5SZf1jQaLPH16k_Ig/ edit
@@MilanJovanovicTech Thanks for the suggestion. I also tried posting a comment as a link to google docs (with spaces) but I got caught!
I like the way you present the different tenant scenarios and how you automagically apply the tenantId in your queries. I didn't know you could use DI in the application DB context, to use values from an external service. Is this possible if using separate configuration files in combination with assembly scanning?
I understand that you are only showing the basics in this video, but wondering if an improvement for a further video could be, using and validating JWT tokens. By accepting and reading the tenantId from the JWT token you protect against simply changing the tenantId in the header (if that's sensitive). You could just reference your video on JWT tokens in this video.
That's basically the same thing, except you're checking against the tenant's id in the token. When dealing with cookies, you can have different cookie schemas for each tenant.
| Is this possible if using separate configuration files in combination with assembly scanning?
- It should be, what do you think would prevent this?
I was sure I mention it in the video, but yes - the TenantId is more likely to live in a cookie or JWT.
@@MilanJovanovicTech well, when you say it, its the same functionality and process (ie per request handling) but getting it wired up is different, so you're pro'll right.
For the production app you need to check if a logged in user has access to a passed X-TenantId header. That way another user couldn't get data than doesn't belong to his tenant even if he somehow figures out the tenant identifier.
This year my team was implementing a multi-tenant modular monolith application. We had some niche requirements where a user could have access to multiple tenants, single tenant or all tenants (i.e: super admin) defending on a role. It was the hardest use case of multi tenancy implemented by my lead
Agreed, I noted in the comments that you should deal with the authentication aspect properly as I didn't cover that in this video
Doing this right now with my team. Would love some details on what y’all did. We have considered using the Claims table to identify membership/specific permissions for each tenant by including the tenantId in this table. A super admin claim with the tech support tenantId grants admin access to everything.
I wonder if we could apply the query filter by convention if the entity thas the TenantId field, then apply automagically, instead of adding the query filter to every single entity (or worse, the new intern forgetting to add the query filter to a new entity)
I saw something like that in stackoverflow if i'm not mistaken, it applied a configuration to all entities that implemented an interface, in this case could be something like ITenantModel
You can have your entities implement an interface, like ITenantId. The interface will have a single TenantId property.
And then you should be able to say: modelBuilder.Entity().HasQueryFilter().
*Don't take this as 100% true, I didn't check in my IDE. But that's the idea.
Be careful with query filters, they are not additive (well they weren’t before 8 I’ve not confirmed that yet).
We had a filter like this to add the tenant id automatically to queries. But if you have another (in our case IDeletable) then the second filter replaces the first.
Do you have a video showing how you set up those logs to show in the docker window?
Just set the log level to Information
Always a good content quality! Thank you for sharing!
My pleasure!
How to use this multitenancy using hangfire too?
In what way?
Great video. How can I set the tenant id into the header after login or call login api ?
You'd store it in the JWT (instead of using a header)
can you teach completely how to use benchmarking with api projects .i love your videos.
Can do
thanks
I don't have a TenantId before logging in, how do I know which database to access?
You'll get one assigned when logging in. The value will typically be set in a JWT or Cookie
Thanks for bringing clarity to this topic. Can you break down how to resolve tenants by using a sub-domain? Perhaps tenant-resolution is an abstraction in itself - perhaps by header, subdomain and even http-query-parameter.
That'd be a topic for a different video
what if I got an unknown amount of tenants with same connection string but different database setting? How do I setup that one ?
Place the "tenant-specific" tables into a custom schema, that could be one option
what about multi-tenant database per tenant? Meaning only the databases are whats separated they share the same web api. Its been hard to learn how to do this with ef core. My idea is ill have a master database to store all tenant info and connection strings. When a tenant signs up for example, ill create there database on azure. Use ef core, add them to the master database. When I need to connect to the tenant database, I query the master database, grab the connection string some how make ef core work with dynamic connection strings. Create maybe a initial setup, where you run the first migration (similar to update-database in package manager console) on the tenants database. However most of this is theory, would love a video on this.
Well you've got a pretty good idea going there
Thank you for creating this helpful content, Milan ☺👏
Glad it was helpful!
How would you implement this if you wanted to allow the SaaS admin to also sign in to this application and perform some actions like inviting the company via email or issuing broadcast notification for all tenants? This would require a different entity than Company.
You can have a different API for the admin that handles that, without any query filters
@@MilanJovanovicTech But then the second API for SaaS admin can't access tables in the first DB right? Is there any other way to do that in one api?
great video, thank you, what about multi-tenancy with single db and many schemas? (each schema ==> tenant)
Similar to second scenario, except we'd configure the schema dynamically with HasSchema.
Provisioning at the DB level is another headache.
We have a multi tenant system where some data is shared. We created them to be hierarchical so tenants can create their own sub tenants
That's cool. Do you still have everything in a single DB or are you running in a multi DB setup?
@@MilanJovanovicTech single db. The tenants are allocated to certain areas in the UK, but when a participant moves they will be transferred to a different tenant.
A tenant can only view data that is linked to them, but they will get the data from the previous tenant to enable continuous service for the participant. (Project is in the criminal justice system so we get a lot of movement as people are transferred between and out of custody)
That’s the Microsoft docs, can you make a more realistic scenario? What about auth in these scenarios?
That's a completely separate concern, IMO. It just comes down to how you fetch the Tenant ID value. Instead of getting it from a header, you'd fetch it form a JWT. Or use the User ID on the JWT to read the Tenant ID from the database. These are minor details in the overall scheme of things.
I have a question here. In DB per tenant model, how identity storage should be structured and implemented?
Probably as a separate persistence store. But some applications will also require the users to be partitioned.
Love it ❤
How to handle MIGRATIONS for multi DB scenario ❗️❗️
Generate scripts, run them manually
@@MilanJovanovicTech How We will hadnle When Tenant Grows after 3 to 4 years of production, Code must be improve
Does TenantId need to be purchased? How to deal with it?
It's just a way to identify a customer or group of users
YEEEEEEEEEEES, I've been struggling with this theme a few weeks ago. Thank you so much for this video. Hope you can continue it
What does your solution look like?
That was a very useful video! Could you please post another one on implementing multi-tenancy using isolated databases for each tenant?
Great idea
Hi. How i can setup individual users which does not have tenant?
Update the query filter
Really useful, thanks, but in multiple database scenario, how to create a tenant ?
We could have a dedicated tenant DB where we would first create a tenant, and then provision the required database
Great video!
Thanks!
What is base way to use with dapper
Always include the tenant ID in the query
How you solve the Di of the service for running migrations? Since it throws a object null reference exception.
We can check if it's null and only add the query filter in the null case.
Another option is using a different DbContext for migrations.
What is the best database migration strategy in case each tenant has different databases?
Different migrations
EF still works, but at some point it becomes smart to manage the DB migrations yourself.
Wouldn't data leak to another tenant if they changed the header value that holds the tenant id?
Check the pinned comment
THANK YOU so mcuh! Such a gem information!
Happy to help :)
I study a lot from your videos tks ,and I have a question how to change database connetctiongs when add a new tenant
What's your current idea?
Isn't it through TenantId that multiple databases determine which database to connect to? Before logging in, I don't know TenantId. How can I connect to the database and obtain user information? Is there a separate database for login logic?
The identity service can live separately, yes. And after logging in you collect the required information.
Nice video. I have a question. How to make outbox pattern with separate databases? maybe job per tenant or something else
Each DB would need its Outbox processor, I guess
I wondering how DbContextConnectionPool would work ?
It should work, in theory. Worth testing out the second scenario though 🤔
Logging in with a separate database?
What do you mean?
Does anyone know the docs for creating a service? Please send the link.
What do you mean by service?
@@MilanJovanovicTech I mean the provider: Services > TenantProvidor th-cam.com/video/Gf1sCvikpgI/w-d-xo.html
@@MilanJovanovicTech I mean the provider
@@amirologi www.milanjovanovic.tech/blog/multi-tenant-applications-with-ef-core
Hi Milan, thank you for your videos, they are great. I really like the part about filtering with EF Core, thanks :)
I just have one point: I am not a big fan of doing the GetTenantId in the constructor because if there is an exception, it will be wrapped in a DI container exception (Autofac dependency injection exception).
What do you think of the Finbuckle library for multi-tenant? I like it.
But this isn't Autofac
Hello,
How would you do it if the connection strings would have to be dynamic as well for the single tenancy case?
Ex: Company x and Company y both registered within the application and you have to build dynamic databases for both of them without modifying the current app.configuration file
I also enjoy your content, keep it up!!
Thank you!
Have a set of migration SQL scripts that create the required database structure. Run it in the background when a company registers. Something along those lines.
@MilanJovanovicTech How about the schema based multi-tenant, I know it is not supported in EF core. Is there a way to do that ?
I think it's possible with the HasDefaultSchema method
Excellent. As always, there is no free repository related to the video. Any sales channel through Amazon to buy your complete course🤔
Not really, no
Good episode 👍. It would be enlightening if you used a more realistic scenario, instead of headers, determined tenantId using custom claims extracted from JWT. An idea for a future episode.😉
Didn't want to bother with JWTs/Auth, as I figured it would detract from the main point which is Multitenancy with EF
Thank you for the pro content
Sure thing!
Ok but when generate efcore migrations with efcore tool: Unable to create a 'DbContext' of type ''. The exception 'Tenant ID not present'
I like that 😁
Thanks for the effort, your videos are good and I appreciate the time like people yourself put into them. One question, in a Clean architecture with the core entities seperate from the main Infrastructure/Identity, what are people's view on creating entities that have relationships back to the individual user, rather than just the Tenant.
For example, consider a "Keys" table maybe that is unique to the user logged in?
I think it's fine, but wouldn't it still be scoped to a tenant also?
Hi Milan, I am a dedicated follower and student of your two courses. Do you have any plans for a course focused on multitenancy or SaaS applications? Any recommendations you could give me for my project? I would like to follow the strategy of using EF Core for commands and Dapper for queries. Additionally, I would like to use Keycloak as the identity provider for the different tenants. Thank you for your videos; they are very helpful for our projects and for improving as software architects.
Not in the recent future, no. I might do a longer video about multitenancy for YT
Will this resolves concurrency? As multiple concurrent tenants using at same time? e.g. in terms of lambda functions. I am new to this by the way..
Wouldn't each lambda run inside its own context?
This may seem like a novice question, but if the SasS application is going to be used for multiple domain names, where each spins up its own instance of the application with its individual frontend, if anyone has an example of how to solve that issue, I'd be very interested to see how thats done.
We should be able to figure out the tenant based on the domain name, I think Derek has a good video about that if you check out his channel
@@MilanJovanovicTech Thank you, Milan, that sounds exciting. If you can let me know what the channel name is or a link to it, that would be most helpful! Also, just wanted to say, your videos are pretty damned interesting, the subject matter you are choosing to cover is awesome. Thanks again!
Hi Milan, I would like to note here that the firtst approach with the EF Query Filters is applicable when your users or tenants exists outside of this application. For example I have an MVC asp net core application and users are inside the same application (monolith) which makes it a mutlitenant application since every user can only access and modify their own data and all of them exist in the same databse. However the approach with the app user resolver service did not work for me. THe reason is because the OnModelCreating method is only called once for the project's lifetime. So before anyone would login the query filters had alreaady instatiated with null as the tenantId. I was actually around this issue for some time but for this type of application i mentioned it seems it is not an applicable solution. Great video, Keep up the good work!
But if the DbContext is scoped - it should pulling the same scoped service to configure the Query Filter. Right?
true but my DbContext is scoped by default however the OnModelCreating get executed only the first time. A similar issue was discussed on StackOverflow about tenants on the same application.@@MilanJovanovicTech
Thank u for the video, can u make a video on migrations with multi tanécy please ?
Will consider
It doesnt have to come from the JWT it can also be inferred from the url like a subdomain per tenant.
That involves a whole lot more infrastructure, though
@@MilanJovanovicTech I disagree. Whilst it would typically require a wildcard SSL certificate (so that requests for multiple subdomains hit the ingress), I'm not sure that qualifies as whole lot more infrastructure as typically you'd have an ssl cert anyway. For local development you can add a few entries to your etc/hosts file e.g "foo.localhost" pointing to loopback ip. I would say it requires some very minor configuration to achieve. The main thing is whether its a requirement that tenants have their own subdomain. The benefits are it allows tenant identification prior to user authentication - so they can be served customised "public" pages and they can also have their own authentication / login page based on tenant settings - one tenant may use their own SAML idp versus another who uses native login. In other words the decision is more requirements based than infrastructural.
useful content
Glad you think so!
Come on… It is basic concept of multi tenancy. It’s a great presentation. For the goal of the POC it was kept simple as possible. Of course it can be extended based on everyone’s requirements. Important and focus is EF support here, because this can be achieved also without EF Core.
Everyone wants to see a production-ready tutorial in 10 minutes these days 😁
@@MilanJovanovicTech people just don’t want to think
Thank you! Btw. The "X-" convention in "X-TenantId" is no longer considered necessary as per RFC 6648 🙂
I prefer having a convention for custom headers to make them easier to track
No longer necessary but good practice
The TenantProvider can be registered as Singleton. HttpContextAccessor is also a singleton. It uses AsyncLocal to ensure the scope.
So, it is not important to register it as scoped ;-)
Not important, but this is a simplified example. A more real-world scenario would be fetching the Tenant from the DB, which could leverage EF
I hope people understand that this is an example only and you should NEVER EVER do this in production
Which part exactly? Because we definitely used Tenant-based query filters in production on a 1M+ users SaaS.
The security part obviously. Query filters are fine. You’re treating a tenant like an api key when you should do a lookup on the user credentials.
@@drhdev yeah, I bet the video would get more and more complex the closer it is to reality so Milan chose to cut it short
🥷🏾🔥
💪🚀
Tenant could be passed via part of route
Wouldn't that expose tenant info in the logs?