A multi-tenant application is generally defined as a single instance application that serves multiple tenants or user organizations from a single IIS application instance. Each tenant has their own set of users and company data. It would be very bad if a user from tenant A could see tenant B’s data.
There are several ways to implement such a system especially in regards to the database. The two basic schools of thought on the database is have one for the application that contains all tenant’s data on it partitioning the data using a tenant field on each table. This is the approach that is taken in the Litware HR reference app from Microsoft’s patterns group. The second approach is a separate database for each tenant. That is the approach that we have taken. It has several advantages as far as back-up restore is more easily done by tenant and we have the ability to easliy provide a backup to the customer if they decide to self-host or quit our services.
What this means is that each time the context is created the database connection string must be specific to the tenant that the current user belongs to. The out-of-the-box connection string implantation for Entity Framework is that the EntityConnection string, which contains the store connection string within it is stored in the web.config file.
When the Entity Framework designer generates code the object context class for the model is build with a constructor that specifics the name of the connection string which matches the name of the context object class. For example:
public ConfigEntities() : base("name=ConfigEntities", "ConfigEntities") { this.ContextOptions.LazyLoadingEnabled = true; OnContextCreated(); }
There is also a constructor that will accept an alternate name or you can actually pass in an EntityConnection string.
ConfigEntities context = new ConfigEntites(MyConnString);
The other side of the picture is RIA services. When you create a domain service class in RIA servers which work with Entity Framework the domain service is derived from a generic LinqToEntitiesDomainService<> class. Since it is this class itself that is instantiating the object context that you pass in as the generic type there is no way to inject a connection string. Looking in reflector was no help.
Due to this design when the RIA service domain service is instantiated and it instantiates the Entity Framework object context the default constructor is used. So, how can we inject the correct connection string into the object context so that our user will see the correct data?
The first thought was to customize the T4 code generation template for Entity Framework. However, I quickly dismissed that idea since our application actually have a database for each module in the application. So, we have 5 models. I didn’t want to create a custom T4 template for each model, especially considering the possibility of more modules in the future.
I decided the best way was to set the connection string in the OnContextCreated() partial method provided in the ObjectContext class. This method is called after the class in instantiated and seemed the perfect place to inject the correct connection string. The object context class generated by Entity Framework is a partial class so you can simply imolement this partial method in another class file that won’t be touched by the code generator.
There is an EntiyStringBuilder class which allows you to easily build the three part connection string needed that consists of the metadata, store connection string and provider name. Here is the pretty simple code which builds the EntityConnection string:
// Create an EntityConnection using the EntityConnectionStringBuilder EntityConnectionStringBuilder ecsb = new EntityConnectionStringBuilder(); ecsb.ProviderConnectionString = GetConnectionString(); ecsb.Provider = "System.Data.SqlClient"; ecsb.Metadata = "res:///Config.csdl|res:///Config.ssdl|res://*/Config.msl";
I will leave it up to you to code your own GetConnectionString() method. Essentially this just needs to return a store connection string. I have hard coded the provider and metadata but of course you could pull this from config or other place if you wished.
So far so good. Next set is just to provide that EntityConnection string to the object context class. The context class has a property named Connection which holds a reference to an entity connection. You can simple call ToString() on the EntityConnectionStringBuilder object to get the connection string. So here we go:
this.Connection = new EntityConnection(ecsb.ToString());
WAIT … NOT SO FAST !!!
Of course, Microsoft doesn’t think you should have access to change the Connection. So, that public property only has a getter. If you look in reflector you will see that the _connection backing field is private so we can’t set that in a derived class. I had this same problem with the Membership and Profile ASP.Net providers. In that case I had to resort to getting the published code for the providers and modifying it so I could inject the tenants connection string into the provider.
Doing multi-tenant SaaS with Microsoft tools is NOT fun. We also had to play all kinds of games to use SQL Server Reporting services with our app… but that’s another blog post.
Reflection to the rescue. While I don’t love this is did solve the problem. Reflection lefts you set the value of private fields. Here is the simple code, once you know how to do it:
FieldInfo fi = typeof(ObjectContext).GetField("_connection" , BindingFlags.Instance | BindingFlags.NonPublic); fi.SetValue(this, new EntityConnection(ecsb.ToString()));
After this was all in place everything was happy. The domain service instantiated the object context which called the OnContextCreated() method. My code built the correct connection string and injected it into the _connection field and that connection was used to connect to the database.
I hope this helps someone. If there is a better way that you think I am missing please let me know.
Comments
Pingback
[...] RIA Services and Entity Framework 4 in a multi-tenant application | No Long Days bob.archer.net/content/ria-services-and-entity-framework-4-multi-tenant-application – view page – cached A multi-tenant application is generally defined as a single instance application that serves multiple tenants or user organizations from a single IIS application instance. Each tenant has their own set of users and company data. It would be very bad if a user from tenant A could see tenant B’s Tweets about this link Topsy.Data.Twitter.User['pilotbob'] = {"location":"Tampa, Florida, USA","photo":"http://a1.twimg.com/profile_images/52011836/gravatar_normal.JPG","name":"Bob Archer","url":"http://twitter.com/pilotbob","nick":"pilotbob","description":".Net developer, former VFP developer future Ruby developer.","influence":""}; pilotbob: “Blogged: RIA Services and Entity Framework 4 in a multi-tenant application (http://bob.archer.net/node/32) ” 2 hours ago view tweet retweet Filter tweets [...]
Pingback
[...] you read my previous blog post about the trials and tribulations of providing a connection string to the Entity Framework context [...]