Dynamic MySQL database connection for Entity Framework 6

前端 未结 2 1911
面向向阳花
面向向阳花 2020-11-27 14:42

I wish to pass a dynamic connection string to the entity framework context. I have over 150 schemas which are identical (one per account) and I would like to select the conn

2条回答
  •  有刺的猬
    2020-11-27 15:08

    It's now 2019 of course things have changed a bit but Franciso's example really helped me on this. This is the simplest solution I could find and the only one that actually worked. I did change it a bit from what he has shown. Follow this to completion you should end up with a working solution.

    I had to change a few things. I am going to be very explicit in what has to be done and I am going to use my actual file names etc so that you don't have to guess about substitutions. Many examples are also short on how to make it work at the end. This example has everything you need to know.

    This was built on visual studio 2015 Entityframework 6 using MySql server 8.0.16.0.
    Unfortunately the MySql connectors and libraries are a complete mess. The 8.0.xx.0 connector / net and MySql.Data.Entity.EF6 and MySql.Data are completely useless. I have installed Connector Net 6.10.7.0, MySql.Data.Entity.EF6 6.10.7.0, and MySql.Data 6.10.7.0. That works for me and I will vigorously oppose changing this.

    This is for MySql but I really don't know why it could not work for any db.

    Scenario

    I have a multi tenant situation where I have a common db and multiple tentant databases, one per customer The customer id is kept in the common db for login purposes and authorizaton and the customer id directs which database to use. The client db's are all called myclientdb_x where x is the client number. myclientdb_1, myclientdb_2, myclientdb_35 and so on.

    I need to dynamically switch to whatever clientdb_x the code is currently serving. There is a initial database client called myclient_0 which is the template for all of the other myclient_x databases.

    Step1

    I created a specific connection string in my Web.config for this it looks like this. It allows connections to the clientdb_0

    
    

    Step2

    I created a new entity called ClientDbUserUpdater using the wizard. The data entity is called

    ClientDbUserUpdater.edmx

    I told it to use "DefaultClientConnection" as the DB connection I told it to save this new connection string in the Web.config

    This created new entity connection string in the Web.config file and it will look like

    
    

    You might have to dig a bit because the wizard is not good about putting in \n in appropriate places.

    Notice that this connection string is fundamentally the same as the initial connection string except for its name and the fact that it has

        res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
        res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
        res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
    

    The res: strings are needed by the data entity and its why you can't just send a standard connection string into the data entity.

    If you try to send in the initial connection string

     
    

    you will get an exception from

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                throw new UnintentionalCodeFirstException();
            }
    

    Step3

    This new connection string is the one you need to alter. I have not tested it but I am pretty sure if change the data entity model with the wizard you will need to make this change again. Take string:

    
    

    and change it to:

    
    

    Notice that the only part changed is database=myclient_0 to database={0}

    Step 4

    The data entity created some code behind ClientDbUserUpdater.edmx. The file is called ClientDbUserUpdater.Context.cs.

    The code is ...

    namespace what.ever.your.namespace.is
    {
        using System;
        using System.Data.Entity;
        using System.Data.Entity.Infrastructure;
    
        public partial class client_0Entities : DbContext
        {
            public client_0Entities()
                : base("name=client_0Entities")
            {
            }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                throw new UnintentionalCodeFirstException();
            }
    
            public virtual DbSet users { get; set; }
        }
    }
    

    Notice that this a partial class. This means you can extend this class and add a new constructor.

    Add the following class.

    using System;
    using System.Configuration ;
    using System.Data.Entity ;
    
    namespace what.ever.your.namespace.is
    {  
      public partial class client_0Entities : DbContext
      {
        public client_0Entities(string dbName) : base(GetConnectionString(dbName))
        {
        }
    
        public static string GetConnectionString(string dbName)
        {       
           var connString = ConfigurationManager.ConnectionStrings["client_0Entities"].ConnectionString.ToString();
          // obviously the next 2 lines could be done as one but creating and 
          // filling a string is better for debugging.  You can see what happened 
          // by looking a  conn
          // return  String.Format(connString, dbName);
          string conn =  String.Format(connString, dbName);
          return conn ;
        }
      } 
    }
    

    The class adds a new constructor which allows you to get the base connection string for the data entity model which from above looks like:

    
    

    and modfiy it at run time to change the schema.

    The String.Format() call in the new partial class swaps out the database schema name in this connection string at run time.

    At this point all configuration is done.

    Step 5

    Now you can make it go. For better understanding of this example it is nice to know what the model looks like for this entity. It is very simple because I was just testing and trying to make it go.

    Drilling down through ClientDbUserUpdater.edmx and into into ClientDbUserUpdater.tt you will find your model in modelname.cs . My model is called "user" so my file name is called user.cs

    namespace what.ever.your.namespace.is
    {
        using System;
        using System.Collections.Generic;
    
        public partial class user
        {
            public int UserId { get; set; }
            public string Email { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public Nullable Active { get; set; }
        }
    }
    
    

    Now you can generally access your model like this.

     client_0Entities _client_0Entities = new client_0Entities("schemaName");
    

    and this code can be anywhere in your solution that can see class client_0Entities.

    which in practice is a line similar to any of the 3 below which are connection to databases client_19, client_47 and client_68 respectively.

     client_0Entities _client_0Entities = new client_0Entities("client_19");
     client_0Entities _client_0Entities = new client_0Entities("client_47");
     client_0Entities _client_0Entities = new client_0Entities("client_68");
    

    the following is an actual code example that works on my system. Obviously I am going to not hard code in "client_19" but its better for demo purposes.

    here is actual code with real names that works and adds a new row to the user table on database client_19

      string _newSchema = "client_19"
      using(client_0Entities _client_0Entities = new client_0Entities(_newSchema))
      {
         user _user = new user();
         _user.UserId = 201;
         _user.Email = "someone@someplace.com"
         _user.FirstName ' "Someone"; 
         _user.LastName  = "New";
         _user.Active = true;
    
         client_0Entities.users.Add ( _user ) ;
         client_0Entities.SaveChangesAsync ( ) ;
      }
    

    Hopefully this helps some people. I spent about 20 hrs looking at different solutions which simply did not work or provide enough information to complete them. As I said, finding Franciso's example allowed me to get it working.

    Regards,

提交回复
热议问题