Summary
I am currently prototyping a (very straight-forward?) multi-tenant web-application where users (stored in database 1) can register to different tenants (stored in a database per tenant (same db schema). An architecture that I thought would apply to a lot of multi tenant solutions.
Sadly, I found out that cross database relations are not supported in Entity Framework (I assumed it's still the case for EF6). I provided the links below.
The next short sections explain my problem, and ultimately my question(s).
The rational behind the design
I choose to have separate databases; one for users (1), and one for each tenant with their customer specific information. That way a user does not have to create a new account when he joins another tenant (one customer can have different domains for different departments).
How it's implemented
I implemented this using two different DbContexts, one for the users, and one for the tenant information. In the TenantContext I define DbSets which holds entities which refer to the User entity (navigation properties).
The 'per-tenant' context:
public class CaseApplicationContext : DbContext, IDbContext
{
public DbSet<CaseType> CaseTypes { get; set; }
public DbSet<Case> Cases { get; set; }
// left out some irrelevant code
}
The Case entity:
[Table("Cases")]
public class Case : IEntity
{
public int Id { get; set; }
public User Owner { get; set; } // <== the navigation property
public string Title { get; set; }
public string Description { get; set; }
public Case()
{
Tasks = new List<Task>();
}
}
The User entity
[Table("Users")]
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
This User entity is also contained by the Users database by my other DbContext derivative:
public class TenantApplicationContext : DbContext, IDbContext
{
public DbSet<Tenant> Tenants { get; set; }
public DbSet<User> Users { get; set; } // <== here it is again
// left out irrelevant code
}
Now, what goes wrong?
Expected:
What I (in all my stupidity) thought that would happen is that I would actually create a cross database relation:
The 'per-tenant' database contains a table 'Cases'. This table contains rows with a 'UserID'. The 'UserID' refers to the 'Users' database.
Actual:
When I start adding Cases I am also creating another table 'Users' in my 'per-tenant' database. In my 'cases' table the UserID refers to the table in the same database.
Cross database relations do not exist in EF
So I started googling, and found that this feature simply is not supported. This made me think, should I even use EF for an application like this? Should I move towards NHibernate instead?
But I also can't imagine that the huge market for multi tenant applications simply is ignored by Microsoft's Entity Framework?! So I most probably am doing something rather stupid.
Finally, the question...
I think the main question is about my 'database design'. Since I am new to EF and learning as I go, I might have taken the wrong turn on several occasions (is my design broken?). Since SO is well represented with EF experts I am very eager to learn which alternatives I could use to achieve the same thing (multi tenant, shared users, deployable in azure). Should I use one single DbContext and still be able to deploy a multi tenant web-application with a shared Users database?
I'd really appreciate your help!
Things learned:
NHibernate does support cross database relations (but I want to deploy into Azure and rather stick to microsoft technologies)
Views or Synomyms can be an alternative (not sure if that will cause more difficulties in Azure)
Cross database relations are not supported by EF:
EF4 cross database relationships
ADO.Net Entity Framework across multiple databases
Entity framework 4 and multiple database
(msdn forum with EF devs) http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/cad06147-2168-4c20-ac23-98f32987b126
PS: I realize this is a lengthy question. Feel free to edit the question and remove irrelevant parts to improve the readability.
PPS: I can share more code if needed
Thank you so much in advance. I will gladly reward you with upvotes for all your efforts!
I don't quite understand why do you need cross database relations at all. Assuming your application can talk to the two databases, the user database and a tenant database, it can easily use the first database for authentication and then find related user in the tenant database with "by name" convention.
For example, if you authenticate a user JOHN using user database then you search for a user JOHN in the tenant database.
This would be much easier to implement and still match your requirements, users are stored in users database together with their passwords and "shadow copies" of user records but with no passwords are stored in tenant databases and there is NO physical relation between these two.
Related
Intro
I have problems mocking the database for my one-to-many relationship between a user and the employments. The employment is added correctly to the mock database, but when the same entity is added to an Collection of Employments in the User entity, it does not load.
Code
These are just the important parts for this case of the two classes
User
public virtual ICollection<Employement> Employments { get; } = new List<Employment>();
Employment
[Required]
[ForeignKey(nameof(User))]
public Guid UserId { get; set; }
public virtual User? User { get; set; }
Example
I use something called EntityHelper to create the entities. I create the employment entity, use it when I create the User entity (these are all created correctly when I debug). I use db.AddRange() to add the entities to the database.
Here is how that looks.
The problem
As I mentioned earlier, almost everything is added correctly to the database. The user with its data, and the employment. The collection of employments in the user is however empty (even though the user entity I created have it). I'm not sure if this is a problem with EF Core or XUnit or what it might be.
Some other facts that might be good to know:
The test uses an SqLite in-memory database
It is based on a DbContext
The answer was a missing .include(u => u.Employments) in the LINQ query that fetched the users from the database.
I have a question regarding the setup of foreign keys in entity framework 6. Our project stores data from a few other services (to have faster access to the data) and provides the users with charts and statistics depending on the stored data. For the storage of the data we´ve setup a cronjob which runs daily at about 3 AM.
Here are 2 example database models:
public class Project {
public string Id { get; set; }
public string Title { get; set; }
}
public class Issue {
public string Id { get; set; }
public string Title { get; set; }
[ForeignKey("Project")]
public string ProjectId { get; set; }
[ForeignKey("ProjectId")]
public Project Project { get; set; }
}
The problem now is for some issues we don´t save the project it depends on but we have to save the ProjectId (because at a later point it might be possible that the project exists in our database). So when I try to save this issues it tells me that I can´t save them because the project does not exist.
Is there any way I can tell entity framework that it doesn´t matter if the project exists or not? Currently I´ve just removed the ForeignKeys but this makes it very slow when I try to get the full list of issues with their projects.
Or is there any other way to read out all issues with their projects if there are no foreign keys? Currently I´m using a foreach loop to go threw each issue and then I search for the project but with more than 10.000 issues this get´s very slow.
The navigation property you've defined is requiring the data in the Project table in order to save an Issue. This isn't Entity Framework, this is a SQL Server foreign key constraint issue. Entity Framework is doing preliminary validation to not waste a connection that will ultimately fail. While you can turn off enforcing the foreign key constraint, there is not a good way to turn this validation off in Entity Framework
Keep in mind, having a foreign key does not mean it will help with your query's performance. It's simply a way to enforce referential integrity. I suspect that your real problem is the way you've written your query. Without seeing your query and metrics around "slow", it be hard to point you in the right direction.
I have a SQL Server based ASP.NET MVC 5 app, and I'm using Entity Framework 6 to talk to the database.
We're using a "hybrid" approach - we manage all the database structure with classic SQL scripts which we deploy onto our DB server, and then we generate the "code-first" classes from that SQL Server database. This works reasonably well, for the most part.
One thing that bugs me is the if a given table has multiple FK link to another table, the naming convention used by the EF6 code generation is pretty lame....
Assume I have a table (and therefore entity) Site which represents a site somewhere, and this site has three links to a Contact table for various roles - the "main" contact, the "support" contact, and a "sales" contact. So my table in SQL Server looks something like this:
CREATE TABLE dbo.Site
(
SiteID INT NOT NULL
CONSTRAINT PK_Site PRIMARY KEY CLUSTERED,
.... some other properties, of no interest or relevance here .....
MainContactId INT NOT NULL
CONSTRAINT FK_Site_MainContact FOREIGN KEY REFERENCES dbo.Contact(ContactId),
SalesContactId INT NOT NULL
CONSTRAINT FK_Site_SalesContact FOREIGN KEY REFERENCES dbo.Contact(ContactId),
SupportContactId INT NOT NULL
CONSTRAINT FK_Site_SupportContact FOREIGN KEY REFERENCES dbo.Contact(ContactId)
)
I had been hoping that the EF6 code-first from existing database generation would be smart enough to read those column names and come up with meaningful names for the navigation properties on the entity - but alas, this is what I get instead:
[Table("Site")]
public partial class Site
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SiteID { get; set; }
public int MainContactId { get; set; }
public int SalesContactId { get; set; }
public int SupportContactId { get; set; }
public virtual Contact Contact { get; set; }
public virtual Contact Contact1 { get; set; }
public virtual Contact Contact2 { get; set; }
}
While the actual FK columns are OK - the "deduced" navigation properties are horrible - Contact, Contact1 and Contact2 - seriously, is this the best naming??? I think not... I would much prefer to called them "MainContact", "SalesContact", "SupportContact" - wouldn't that make a lot more sense? And be clearer for later use?
I installed the custom T4 templates (Nuget package "EntityFramework.CodeTemplates.CSharp"), and I see that there are a few interesting and potentially very useful helper classes being used (CSharpCodeHelper, EdmHelper, EntitySet, DbModel from the Microsoft.Data.Entity.Design and System.Data.Entity.Infrastructure namespaces) - unfortunately, most of them are sparsely documented, and also often their constructors are internal - so I cannot really build my own tool based on those ready-made classes.
Any other approach? I'd really like to teach the code generation a few smarts - this right now is just not up to usual standards and requires me to make a lot of manual changes to generated files - a labor in vain, flushed down the digital toilet each time I need to re-generate the classes from the database.....
I'm developing a web app (not ASP.NET), and I encountered a small architectural problem:
So, i have two classes to work with users.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
// Other properties...
}
public class Profile
{
public int Id { get; set; }
public string PhotoUrl { get; set; }
public string DisplayName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PostItem> Posts { get; set; }
}
I had to split these classes because there is a feature that allows you to view profile of the certain member, and obviously you don't want to retrieve data from database that contains user's password, name and other private stuff (though it's not displayed in view). So i'm storing this data in different tables: table Users contains personal infomation, while table Profiles contains public one (it can be viewed by anyone).
But at the same time, in order not to break Single responsibility principle, i had to implement UserRepository and ProfileRepository classes that does some checking, adding and other stuff.
And here they come:
Issue 1: code that handles user registration is turned into real hell now, i have to check if record with specific username exists in the two different tables by instantiating two repositories.
Issue 2: Also on the page where you can view public data, there is a need to display latest posts, but here is another problem: i can't store complicated values in one column, so i have to store posts in another table too. It means that i need to implement PostRepository and at the same time property Posts in Profile class is useless (though i need it to display latest posts in view), because in order to retrieve latest posts you need to look through other table inside UserRepository, but it should be handled by PostRepository. For example the same goes for comments.
So, this is my small problem. Any advices?
Ok, taking each item in turn;
1) Its perfectly normal to have the Identity of a user checked through one repository and their permissions to your application stored in another. In fact this is the basic idea behind federated identity. Consider that your might extend your application to allow Identity to be provided by Facebook, but permissions by your own application, and you will see that separating them makes sense.
2) Yes, absolutely. What makes you think that a high volume store like Posts is best served by the same repository that you store a low-change-rate set of data like Permissions in ? One might be in Mongo, the other in Active Directory, with the Identity being OAUTH. You see that since your own the whole application you see these as being unnecessary complexities, whereas they represent good architectural separation.
Identity => not owned by your application. Slow change rate.
Permissions => owned by your application. Slow change rate.
Posts => owned by your application. Fast change rage.
Just looking at those three use-cases, it seems that using different repositories would be a good idea since they have such different profiles. If ultimately your repositories all map to a SQL Server (or other) implementation, then so be it; but by separating these architecturally you can use the best possible underlying implementation.
I'm currently creating a web application using ASP.NET 4.5 MVC with MySQL backing my models.
Upon first login (and whenever they want to afterwards), the user enters their persistent information required for the web app (name, DoB, address, etc.)
There will be a couple to a few dozen settings which are required, and I've heard that best practice dictates that even going past as few as 10 columns in a table calls for reevaluation of your paradigm.
Let's say theorietically that I could break the settings down into different types. Would there be any performance/organization/usability/etc. benefit from creating different tables for each different section?
Follow up question: should persistent user data be stored in the same table as your table of users, or should Settings have its own table? I notice that there is an AspNetUsers table which stores some basic information. Could I use this to store settings?
Thank you!
You want to extend on the Identity 2 Framework:
http://typecastexception.com/post/2014/06/22/ASPNET-Identity-20-Customizing-Users-and-Roles.aspx
In my opinion, there is 0 gain on breaking all the user information on different tables. Do it only if you have some 1:N relation, but there is no problem if you use a single table with a lot of columns.
If you use Entity Framework and Asp.Net Identity, it's possible to extend your AspNetUsers table with custom information. Just create a class that extends from IdentityUser:
public class User : IdentityUser
{
[MaxLength(50)]
[Required]
public string MyAdditionalData { get; set; }
[Required]
public int Age { get; set; }
}
And then in the Context class:
class YourAppContext : IdentityDbContext<User>
{
public YourAppContext()
: base("YourAppContext")
{
}
}
Using migrations, Entity Framework is capable to add/modify columns as requested, based on this class.
Greetings