EFcore and dynamic model (2). efcore Dynamic Model

Source: Internet
Author: User

EFcore and dynamic model (2). efcore Dynamic Model

The previous article introduced how to use ef to manage dynamic types. For example, we have defined ShopDbContext and registered dynamic model information. The following Code adds dynamic information:

Type modelType = IRuntimeModelProvider. getType (1); // obtain the model type object obj = Activator with id = 1. createInstance (modelType); // create entity = obj as DynamicEntity; // type conversion, in order to assign a value to entity ["Id"] = 1; entity ["Name"] = "Name"; ShopDbContext. add (entity); ShopDbContext. saveChanges ();

The above method can only be used to configure the model before running the program, and then start the program. It cannot dynamically change the model information during the program running. Now we can improve the previous functions:

1. Online Model Structure Configuration Management

2. dynamically generate database tables after Model configuration changes

3. register the model information to DbContext at runtime

1. Online Model Structure Configuration Management

In the previous article, we saved the model information in the configuration file. When the program starts, the configuration file is loaded and parsed to compile the model. Now we need to put the configuration information in the database and store the configuration information with a data table. First, modify the RuntimeModelMeta class mentioned above. The Code is as follows:

Public class RuntimeModelMeta {public int ModelId {get; set;} public string ModelName {get; set;} // model name public string ClassName {get; set ;} // class Name public string Properties {get; set;} // Attribute set json serialization result public class ModelPropertyMeta {public string Name {get; set ;} // The corresponding Chinese name public string PropertyName {get; set ;}// class property name public int Length {get; set ;}// Data Length, it is mainly used for string type public bool IsRequired {get; set ;}// whether the input is required. It is used for data verification public string ValueType {get; set ;}// data type, which can be a string, date, bool, etc }}

The public ModelPropertyMeta [] ModelProperties {get; set;} attribute is changed to the String type. Then, we define a DbContext for Model configuration management. The Code is as follows:

  public class ModelDbContext : DbContext    {        public ModelDbContext(DbContextOptions<ShopDbContext> options) :base(options)        {        }        public DbSet<RuntimeModelMeta> Metas { get; set; }    }

With this DbContext, it is easier to operate RuntimeModelMeta. In addition, to facilitate the operation of model attribute data, add some extension methods as follows:

Public static class RuntimeModelMetaExtensions {// get the set public static RuntimeModelMeta through deserialization. modelPropertyMeta [] GetProperties (this RuntimeModelMeta meta) {if (string. isNullOrEmpty (meta. properties) {return null;} return JsonConvert. deserializeObject <RuntimeModelMeta. modelPropertyMeta []> (meta. properties);} // serialize the set into a string to save the public static void SetProperties (this RuntimeModelMeta, RuntimeModelMeta. modelPropertyMeta [] properties) {meta. properties = JsonConvert. serializeObject (properties );}}

  

The operation is very simple, but the problem is how to tell DbContext when the model information changes. When we go to the third part, let's go into details. Here we just need to complete configuration information management.

Ii. dynamically generate database tables after Model configuration changes

Here we use SQL statements to operate the database. Below is a simple encapsulation class:

Public static class ModelDbContextExtensions {// Add the public static void AddField (this ModelDbContext context, RuntimeModelMeta model, RuntimeModelMeta. modelPropertyMeta property) {using (DbConnection conn = context. database. getDbConnection () {if (conn. state! = System. data. connectionState. open) {conn. open ();} DbCommand addFieldCmd = conn. createCommand (); addFieldCmd. commandText = $ "alert table {model. className} add {property. propertyName} "; switch (property. valueType) {case "int": addFieldCmd. commandText + = "int"; break; case "datetime": addFieldCmd. commandText + = "datetime"; break; case "bool": addFieldCmd. commandText + = "bit"; break; default: addF IeldCmd. commandText + = "nvarchar (max)"; break;} addFieldCmd. executeNonQuery () ;}}// Delete the public static void RemoveField (this ModelDbContext context, RuntimeModelMeta model, string property) {using (DbConnection conn = context. database. getDbConnection () {if (conn. state! = System. data. connectionState. open) {conn. open ();} DbCommand removeFieldCmd = conn. createCommand (); removeFieldCmd. commandText = $ "alert table {model. className} drop column {property} "; removeFieldCmd. executeNonQuery () ;}// create a model table public static void CreateModel (this ModelDbContext context, RuntimeModelMeta model) {using (DbConnection conn = context. database. getDbConnection () {if (conn. state! = System. data. connectionState. open) {conn. open ();} DbCommand createTableCmd = conn. createCommand (); createTableCmd. commandText = $ "create table {model. className} "; createTableCmd. commandText + = "{id int identity (1, 1)"; foreach (var p in model. getProperties () {createTableCmd. commandText + = $ ", {p. propertyName} "; switch (p. valueType) {case "int": createTableCmd. commandText + = "int"; break; case "datetime": createTableCmd. commandText + = "datetime"; break; case "bool": createTableCmd. commandText + = "bit"; break; default: createTableCmd. commandText + = "nvarchar (max)"; break ;}} createTableCmd. commandText + = "}"; createTableCmd. executeNonQuery ();}}}

  

When the Model configuration information changes, you can use the preceding encapsulation class to directly operate on the database to change the structure of the data table. Of course, there are few methods available here. You can extend it, such as modifying the field type, delete a table.

3. Running is to register model information to DbContext

We have previously rewritten the OnModelCreating method to register the model to DbContext, but this method will only be executed once. If the model information changes during the runtime, DbContext cannot be synchronized, so this method won't work. DbContext also provides another method called void ongrouping (DbContextOptionsBuilder optionsBuilder). This method is called every time DbContext is instantiated. How can we use this method to register model information. This method contains the DbContextOptionsBuilder parameter. This type provides a method that allows us to register a model. The method is as follows:

DbContextOptionsBuilder UseModel (IModel model)

IModel is the class for model information maintenance. Naturally, we will think of creating an IModel on our own and then registering it through the above method. Now the question is how to obtain the IModel? When we override the OnModelCreating method, we find that there is a ModelBuilder parameter. From this type of name, we may immediately think that we can get the information we need through it? By checking the source code of EntityFramework. Core, we can find that it is what we are looking. First, let's take a look at the constructor method of ModelBuilder:

ModelBuilder (ConventionSet conventions)

It requires a ConventionSet, which is directly translated as a set of constraints (if there is an error, you are welcome to make a brick), then how to get such an object? By viewing the ef source code, the framework uses the ConventionSet created by IConventionSetBuilder, so we also use it. We first reference ICoreConventionSetBuilder in DbContext through dependency injection. The Code is as follows:

Public class ShopDbContext: DbContext {private readonly ICoreConventionSetBuilder _ builder; public ShopDbContext (DbContextOptions <ShopDbContext> options, ICoreConventionSetBuilder builder): base (options) {_ builder = builder ;} protected override void OnConfiguring (DbContextOptionsBuilder optionsBuilder) {// complete ModelBuilder instantiation var modelBuilder = new ModelBuilder (_ builder. createConventionSet ());}}

With ModelBuilder, we can use ModelBuilder. Model to obtain an IMutableModel. This object can be used to register Model information. The Code is as follows:

Public class ShopDbContext: DbContext {private readonly extends _ builder; private readonly IRuntimeModelProvider _ modelProvider; public ShopDbContext (DbContextOptions <ShopDbContext> options, ICoreConventionSetBuilder builder, IRuntimeModelProvider modelProvider): base (options) {_ builder = builder; _ modelProvider = modelProvider;} protected override void ongrouping (DbContextOptionsBuilder optionsBuilder) {var modelBuilder = new ModelBuilder (_ builder. createConventionSet (); // _ modelProvider is mentioned in the previous article, but the implementation needs to be modified, because the current model information is stored in the database Type [] runtimeModels = _ modelProvider. getTypes (); foreach (var item in runtimeModels) {// Add model information modelBuilder. model. addEntityType (item);} // register optionsBuilder. useModel (modelBuilder. model); base. ongrouping ing (optionsBuilder );}}

  

  

In this way, the dynamic model information registration function is completed. If the name of the generated table needs to be customized, We can modify it as follows:

ModelBuilder. Model. AddEntityType (item). SqlServer (). TableName = ""

Because ICoreConventionSetBuilder is used above, we need to call AddEntityFramework in Startup for service registration. The Code is as follows:

     public void ConfigureServices(IServiceCollection services)        {            。。。。。。            services.AddEntityFramework().AddDbContext<ShopDbContext>(option => {                option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), sql => {                    sql.UseRowNumberForPaging();                    sql.MaxBatchSize(50);                });            });                     。。。。。。        }

  

As mentioned above, the OnConfiguring method is called every time DbContext is instantiated, so we need to build the model information every time, which is not very good. ef uses the cache method, we can also use it. The complete ShopDbContext code is as follows:

Public class ShopDbContext: DbContext {private readonly ICoreConventionSetBuilder _ builder; private readonly IRuntimeModelProvider _ modelProvider; private readonly IMemoryCache _ cache; private static string DynamicCacheKey = "DynamicModel "; public ShopDbContext (DbContextOptions <ShopDbContext> options, ICoreConventionSetBuilder builder, IRuntimeModelProvider modelProvider, IMemoryCache cache): base (options) {_ builder = builder; _ modelProvider = modelProvider; _ cache = cache ;} protected override void onindexing ing (DbContextOptionsBuilder optionsBuilder) {// read the model from the cache directly. If no model exists, build IMutableModel model = _ cache. getOrCreate (DynamicCacheKey, entry => {var modelBuilder = new ModelBuilder (_ builder. createConventionSet (); Type [] runtimeModels = _ modelProvider. getTypes (); foreach (var item in runtimeModels) {modelBuilder. model. addEntityType (item ). sqlServer (). tableName = "";} _ cache. set (DynamicCacheKey, modelBuilder. model); return modelBuilder. model;}); optionsBuilder. useModel (model); base. ongrouping ing (optionsBuilder );}

  

Okay. After all the work is done, you can fully implement the dynamic model configuration function during runtime.

The subsequent articles will continue to introduce the implementation of dynamic models and dynamic forms.

  

 

Related Article

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.