Hands-on teaching you: let EF dynamic support new tables, dynamic support multiple databases

Source: Internet
Author: User
Tags mssql connectionstrings

Noun Explanation: This dynamic runtime dynamic, so that the EF dynamic support new tables, dynamic switching database is intended to not change the core framework of the project,

The header is achieved by adding or replacing components.

First, a little simple, dynamic support for multiple databases

Appdbcontext implementation:

public class Appdbcontext:dbcontext
    {
        Public Appdbcontext (String configkey)
            : Base (Configkey)
        {
        }
        protected override void Onmodelcreating (Dbmodelbuilder modelbuilder)
        {
            Base. Onmodelcreating (ModelBuilder);
        }
    }

Add the Configkey parameter to the Appdbcontext constructor, specifying the configuration entry for the connection string in the configuration file through the Configkey parameter;

Create the Idbcontextprovider interface as follows:

public interface Idbcontextprovider
    {
        Appdbcontext get ();
    }

The intention is obvious, through Idbcontextprovider to provide appdbcontext, so we first will appdbcontext and business layer decoupling;

Continue to create 2 projects: Mssqlprovider, Mysqlprovider, implementing the Idbcontextprovider interface respectively:

MSSQL:

public class Mssqlprovider:idbcontextprovider
    {
        Appdbcontext m_appdbcontext = null;
        Public Appdbcontext Get ()
        {
            Return M_appdbcontext?? New Appdbcontext ("MSSQL");
        }
    }

Mysql:

  public class Mysqlprovider:idbcontextprovider
    {
        Appdbcontext m_appdbcontext = null;
        Public Appdbcontext Get ()
        {
            Return M_appdbcontext?? New Appdbcontext ("MYSQL");
        }
    }

Continue to explain dynamic support/toggle Dbcontextprovider, yes ... Smart you should have thought of it from the start. Dependency injection, this time we need to use dependency injection to complete the mission;

I have MEF as an example to demonstrate how to dynamically get 2 kinds of Dbcontextprovider:

First add [InheritedExport] tags to our idbcontextprovider, and then add [Export] tags to two kinds of provider respectively;

"The use of MEF also invites everyone to familiarize themselves, I will only use it, not proficient in"

Then add app.config and test code to the demo;

App.config:

<?xml version= "1.0" encoding= "Utf-8"?>
<configuration>
  <connectionStrings>
    <add name= "MSSQL" connectionstring= "Data source=liang-hu-pc;initial catalog=appbase;integrated Security=True; Pooling=false "providername=" System.Data.SqlClient "/>
    <add name= "MYSQL" connectionstring= "server=localhost; User Id=root;password=mysql; Persist security Info=true;database=appbase "Providername=" MySql.Data.MySqlClient "/>
  </connectionStrings>
</configuration>

Here to remind you: to enable MySQL to support the EF use, need to go to the MySQL official download the latest driver;

The test code is as follows:

Class Program
    {
        [ImportMany]
        static ienumerable<idbcontextprovider> m_providers = null;
        static void Main (string[] args)
        {
            To find the MEF part by using the directory method
            var catalog = new Directorycatalog (AppDomain.CurrentDomain.BaseDirectory);
            Create container
            var container = new Compositioncontainer (catalog);
            Gets the export item, which is loaded directly and does not take lazy
            M_providers = container. Getexportedvalues<idbcontextprovider> ();
            if (m_providers!= null)
            {
                foreach (var provider in m_providers)
                {
                    Console.WriteLine (provider. Get (). Database.Connection.ConnectionString);
                }
            }
            Console.readkey (FALSE);
        }

OK, we will compile the test under the application directory when there is no provider when there is no gain to any provider, if only placed mysqlprovider the results are as follows:

When two provider components are placed, it is natural that two of them will be acquired and I will not demonstrate them;

Maybe a lot of people are going to be booed here, and you may ask some questions:

Like what:

1 Why not make a provider implementation that relies on the injection parameters in the Get () method or constructor.

In fact, the purpose of this is that we use Unitofwork and repository mode can be simple and convenient to obtain the DbContext;

You can see examples:

    <summary>
    Entity Framework Repository
    </summary>
    <typeparam name= "T" ></typeparam>
    public class Efrepository<t>:irepository<t>
        where T:class
    {
        readonly idatabasefactory m_databasefactory = null;
        Public efrepository (Idatabasefactory databasefactory)
        {
            if (databasefactory==null)
            {
                throw new ArgumentNullException ("Databasefactory");
            }
            M_databasefactory=databasefactory;
        }
        DbContext m_dbcontext = null;
        Protected DbContext DbContext
        {
            Get
            {
                Return M_dbcontext?? M_databasefactory.get ();
            }
        }
The use of Unitofwork mode is similar to this;

2 I only need a dbcontext, but sometimes need to switch the database, then how to do?

This problem is ICO and dependency injection aspect of the basic content, need you to learn oh;

So far, simple "dynamic" support for multiple database examples are complete ~ ~ ~ Our key or dynamic support for the new table, we will take a step-by-step practice it;

Second, "Dynamic" support new table, plan ahead

First we create the Modelbase class library, which holds some interfaces and base classes related to the entity, as shown in the diagram:

According to the project structure, I need to explain the meaning of each file;

IEntity interface and Abstractentitybase class, as the name suggests, you should guess that they are the entity base class, why to define it, is mainly convenient for us to write entities directly inherit the id attribute, (because all of our table primary keys are GUIDs and named ID)

public interface IEntity
    {
        Guid Id {get;}
    }
Public abstract class Abstractentitybase:ientity
    {
        Public Abstractentitybase ()
        {
            This. Id = Guid.NewGuid ();
        }
        [Key]
        [Required]
        Public Guid Id
        {
            Get
            protected set;
        }
    }

Another benefit is that we describe the primary key relationship directly in the base class, and when we write the entity directly, we can save a lot of repetitive operations. ^_^

Again, imapping and mapping, why should we have these 2 base-class interfaces, for the following reasons:

1) The mapping relationship between entity and database generates mapping class and DbContext class decoupling (this will be the case when the specific occurrence of the following)

2 through the Mappingbase base class to achieve some common operations, to avoid the duplication of each entity class operations, specific look at the code you will understand;

[InheritedExport]
    public interface Imapping
    {
        void Registto (Configurationregistrar configurationregistrar);
    }
public class mappingbase<tentity>: Entitytypeconfiguration<tentity>, imapping
        where tentity:class,ientity
    {
        Public Mappingbase ()
        {
            This. Map (M => m.totable typeof (Tentity). Name));
        }
        public void Registto (Configurationregistrar configurationregistrar)
        {
            Configurationregistrar.add (this);
        }
    }

Oh, with the "dynamic" support of multiple databases, here a lot of people should be able to guess how we "dynamic" support add table slightly; Notice the subtlety of the Imapping interface here. Oh, did you find it ... ;

Three, everything is ready, only the east wind

We first create a user entity and a role entity in the Modela class library, and create both usermapping and rolemapping, (why do I want to create the mapping class, I'll talk about it later)

USer, usermapping:

/*
     * Why not pass [Table] to indicate,
     * Not because we need EF's own way of showing support
     * instead we inherit from Abstractentitybase, where the base class has implemented to map the class name to the table name
     */
    public class User:abstractentitybase
    {
        [Required]
        public string Username {get; set;}
        [Required]
        public string Password {get; set;}
        /*
         * Note here, why don't I add a foreign key association by DataAnnotations Way?
         * Individuals think that the user entity is associated with the role entity and already has the role attribute.
         * If you add a roleid to represent a foreign key relationship, it makes me feel that the user class is not refreshing
         * So my approach is to add the Usermapping class to specify its relationship to the role entity
         
         * But one thing to note is that if you do not specify a foreign key, the default database foreign key is the table name _ primary KEY (role_id) type
         */
        public virtual role Role{get;set;}
    }
[Export ("usermapping")]
    public class Usermapping:mappingbase<user>
    {
        Public usermapping ()
        {
            This. Hasrequired (M => m.role)
                . Withmany (M => m.users);
            * Note there is no designated Hasforeignkey OH * *
        }
    }

Role class and the implementation of rolemapping is also the same, combined with the above code annotation content, I think we can understand my good intentions, if not understand, we look at how the DbContext is achieved:

/*
     * A very refreshing dbcontext that doesn't contain any dbset at all.
     * Load table structure through mapping
     */
    public class Appdbcontext:dbcontext
    {
        Public Appdbcontext (String configkey)
            : Base (Configkey)
        {
            Can be set to create tables in reverse mode Oh, but the purpose of our demo is not that
            Database.setinitializer (New dropcreatedatabaseifmodelchanges<appdbcontext> ());
            All imapping implementations under load directory
            var catalog = new Directorycatalog (AppDomain.CurrentDomain.BaseDirectory);
            var container = new Compositioncontainer (catalog);
            M_mappings = container. Getexportedvalues<imapping> ();
        }
        [ImportMany]
        Ienumerable<imapping> m_mappings = null;
        protected override void Onmodelcreating (Dbmodelbuilder modelbuilder)
        {
            if (m_mappings!= null)
            {
                This is the key.
                foreach (var mapping in m_mappings)
                {
                    Mapping. Registto (modelbuilder.configurations);
                }
            }
            Base. Onmodelcreating (ModelBuilder);
        }
    }

Yes, our goal is to let DbContext completely rely on imapping to load interpretation of the structure of the relationship, so that is to ensure that DbContext does not contain a large number of dbset, but also very good to the dbcontext and entity decoupling,

More importantly, we use the DataAnnotations and Fluent API, so that our entities are also very refreshing;

Here, in fact, I have the core of the content has been shown, for the dynamic use of new tables is similar to the most previous "dynamic" support for multiple databases, we only need to rely on injecting all the imapping implementation, You can let DbContext automatically interpret all the table structures (so DbContext's Onmodelcreating method is the key).

OK, then we'll add a new Modelb as a carrier for the new table Newmodel entity to demonstrate whether our example can dynamically support new tables and entities without changing the core framework, as described in the title.

public class Newmodel:abstractentitybase
    {
        [Required]
        public string Name {get; set;}
    }

We add a appdemo to the Cmddemo and add the app.config file, and create a Dataviewcontrol custom control to display the data;

Let's take a look at Appdemo's demo:

Its specific implementation is:

    <summary>
    The interactive logic of MainWindow.xaml
    </summary>
    public partial class Mainwindow:window
    {
        Public MainWindow ()
        {
            InitializeComponent ();
        }
        private void Window_Loaded (object sender, RoutedEventArgs e)
        {
            Idbcontextprovider Provider = new Mssqlprovider.mssqlprovider ();
            Appdbcontext DbContext = provider. Get ();
            var users = dbcontext.set<user> ();
            if (Users!= null)
            {
                Users. ADD (New User ()
                {
                    Username = "Admin",
                    Password = "Admin",
                    role = new Role () {Name = "Administrators"}
                });
                Dbcontext.savechanges ();
                Dataviewcontrol usersviewcontrol=new Dataviewcontrol ();
                Usersviewcontrol.binding (Users. ToList ());
                TabItem item = new TabItem ();
                Item. Header = "User table display";
                Item. Content = Usersviewcontrol;
                THIS.MYTABCONTROL.ITEMS.ADD (item);
            }
            var roles = dbcontext.set<role> ();
            if (roles!= null)
            {
                Dataviewcontrol Rolesviewcontrol = new Dataviewcontrol ();
                Rolesviewcontrol.binding (roles. ToList ());
                TabItem item = new TabItem ();
                Item. Header = "Role table display";
                Item. Content = Rolesviewcontrol;
                THIS.MYTABCONTROL.ITEMS.ADD (item);
            }
            /*
             * Please note here that our Newmodel is still coupled with the application,
             * And there's no dynamic loading like our headline says;
             * The main purpose here is to demonstrate convenience, I don't do entity and business layer decoupling,
             * Generally our application may be a separate UI module and its corresponding entity coupling, rather than UI framework coupling
             * Load UI components for different modules only when needed
             *
             */
            var newmodels = dbcontext.set<newmodel> ();
            if (newmodels!= null)
            {
                Dataviewcontrol Newmodelsviewcontrol = new Dataviewcontrol ();
                Newmodelsviewcontrol.binding (Newmodels.tolist ());
                TabItem item = new TabItem ();
                Item. Header = "Newmodel table display";
                Item. Content = Newmodelsviewcontrol;
                THIS.MYTABCONTROL.ITEMS.ADD (item);
            }
        }
    }

Need to explain is Appdemo not very good demo how to dynamically support a new table, in fact, I explained earlier Modelb in Newmodel is the new table, mainly in order to show you the realization of ideas, I did not go to the Newmodel and Appdemo to decouple, So there is no good demo effect, but in fact there is no problem, which is closely related to our specific application.

Here, we have fully explained the entire process, for which I also write blog while creating projects, in the end will be attached to the full project source code, interested in downloading their own learning; If this article for you have inspired, or let you learn something, that I am very happy to see, but also hope that you do not despise the master.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.