12

I have the situation where I will have multiple companies accessing a single instance of my application, but I need to segregate their data to avoid accidentally loading data for another company, and to ease per-company database backups.

Ideally, I'd like to split the data up into different databases as follows:

  • One SQL Server database for a list of all the users, together with any tables that are common across all users / companies

  • N number of company specific databases, each with the same schema but different data for each company. On logging in, I would dynamically select the database for the specific user's company

I'd like to be able to query the company specific data tables, but perform joins onto the shared database (for example, so I could store a foreign key in the company database table, which joins onto the primary key of a shared table in the other database).

I've discovered SQL Server Synonyms which look like they could do the job, and it seems I can successfully join between the two databases by using a query in SQL Server Management Studio.

Is it possible to use a Synonym table in Entity Framework Code First, and if so what would my POCO classes / DbContext need to look like?

Thanks :)

Matt Wilson
  • 8,159
  • 8
  • 33
  • 55
  • You won't be able to create a foreign key between databases, whether you are using a synonym or not, so I don't believe they could "do the job." See: http://stackoverflow.com/questions/1424327/foreign-keys-on-table-from-different-database If you don't need an explicit foreign key relationship, then can't you just join the local table to `centraldatabase.dbo.table` instead of creating synonyms to it all over the place? Or does EF not let you join across databases at all? – Aaron Bertrand Jan 21 '13 at 17:10
  • I didn't realise you can join directly between two databases without a synonym - I've never had the need to before but that is useful. A fair point about not being able to use foreign keys, I guess this would never work - but even without an explicit relationship it still might be a way forward if I could get EF to support this. As far as I can tell, code first EF only supports a single database connection, and referencing a synonym as a table doesn't work. – Matt Wilson Jan 21 '13 at 17:18
  • 3
    From what I can tell, this is not the last EF/CF limitation you'll come across if your project does not fit into the strict parameters of that model. Is EF/CF something you have to use, or are you using it because it's "all the rage"? I suspect if you want to do anything moderately complex you'll quickly get frustrated with all of its boundaries and seek other alternatives. – Aaron Bertrand Jan 21 '13 at 17:22
  • 1
    Have you thought about views which would expose the data in the shared database to (and through) the customer specific database? – Pawel Jan 21 '13 at 18:02
  • @AaronBertrand - Fair comments and from my experiences so far I'm inclined to agree. We're not required to use EF for this project, but felt it would be a worthwhile exercise to try. Seems to have it's pros and cons - I like how it eliminates the requirement for writing thousands and thousands of lines of CRUD, handles the mapping to/from the domain objects, and querying with LINQ is great (especially it's ability to fetch hierarchical results). But I don't like the amount of magic / loss of control. Also trying http://www.telerik.com/products/orm.aspx which seems to have much nicer tooling. – Matt Wilson Jan 23 '13 at 14:27
  • @Pawel - Thanks for this suggestion. Having read up further I have learnt that what I'm after is a multi-tenant architecture. Were you referring to the use of "tenant view filters" as in this article? http://msdn.microsoft.com/en-us/library/aa479086.aspx#mlttntda_tvf - seems like a neat way to do it, but I fear that working with views instead of tables for the whole app won't play very nicely with ORMs. – Matt Wilson Jan 23 '13 at 14:30
  • I was no aware of this document but this is basically what I meant. EF suports views so it may be the way to go. EF does not support multiple databases but you should be able to use the view to kind expose the common data in each database as if it was a table there. – Pawel Jan 23 '13 at 17:20
  • Related question (but not a duplicate) for a non-code-first solution : [Making an Entity Framework Model span multiple databases](http://stackoverflow.com/q/6036357/302677) – Rachel Jul 24 '14 at 16:05

1 Answers1

9

If I understood properly, you have a SharedServer and some LocalServers (company specific) which want to have all the objects of both (one shared, one the company specific) in a single context.

I'll give you two scenarios:

  1. Many-to-Many: in this case, table to have relation are in sharedDB, but the third table joining them, is in company specific DB.
  2. Single-to-Many: which one of tables are in SharedDB and the other one in company specific DB.

Many-to-Many

1. Create Your Synonym in SQL side

First you have to create the synonym in your local (or company specific) DB:

CREATE SYNONYM [dbo].[StudentCources] FOR [SharedServer].[SharedDB].[dbo].[StudentCources]

let's suppose that your shared table has two columns (doesn't care) named studentID and courseID.

2. Create the POCOs

Let's suppose we have two tables on local DB which have Many-to-Many relationship between each other. and let's suppose the third joiner table (which contains the keys) is located in shared DB!! (I think it's the worst way). so your POCOs will look like this:

Public Class Student
    Public Property studentID as Integer
    Public Property Name as String
    Public Property Courses as ICollection(Of Course)
End Class

and

Public Class Course
    Public Property courseID as Integer
    Public Property Name as String
    Public Property Students as ICollection(Of Student)
End Class

and the Shared one:

Public Class StudentCources
    Public Property courseID as Integer
    Public Property studentID as Integer
End Class

and the context look like:

Partial Public Class LocalContext
    Inherits DbContext

    Public Sub New()
        MyBase.New("name=LocalContext")        
    End Sub

    Public Overridable Property Students As DbSet(Of Student)
    Public Overridable Property Courses As DbSet(Of Course)

    Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
        modelBuilder.Entity(Of Student).HasMany(Function(e) e.Courses).WithMany(Function(e) e.Students).Map(Sub(e)
            e.MapLeftKey("studentID")
            e.MapRightKey("courseID")
            e.ToTable("StudentCources", "dbo")
    End Sub)

    End Sub
End Class

the code in the OnModelCreating tells the model builder that the relation table is a synonym (not directly). and we know that the synonym is in SharedDB.

One-to-Many

No steps! Just modify the OnModelCreating to:

modelBuilder.Entity(Of Student).ToTable("Students", "dbo")

and note that in this case Students is a Synonym. then create the relationship :)

Community
  • 1
  • 1
Hamed Zakery Miab
  • 738
  • 2
  • 12
  • 34
  • I'm doing this for one-to-many and it appears to work, but my context.Student is always empty even though there's data in the db (and I didn't have that problem when I used a different context to get the data). Did you know anything about this? – levininja Dec 30 '14 at 02:03
  • 1
    could you please explain more? – Hamed Zakery Miab Dec 30 '14 at 04:47
  • 1
    worked like a charm. I would like to point that I had to fake a migration without actually creating the rable. – MetalGeorge Feb 19 '16 at 04:15