EFcore와 동적 모델(둘)

13091 단어
이전 글에서 ef를 사용하여 동적 유형을 관리하는 방법을 소개했다. 예를 들어 우리는 ShopDbContext를 정의하고 동적 모델 정보를 등록했다. 다음 코드는 동적 정보의 증가를 실현했다.
Type modelType = IRuntimeModelProvider.GetType(1);//  id=1     
object obj = Activator.CreateInstance(modelType);//    
entity = obj as DynamicEntity;//    ,       
entity["Id"]=1;
entity["Name"]="  ";
ShopDbContext.Add(entity);
ShopDbContext.SaveChanges();

위의 방법은 프로그램이 실행되기 전에 모델을 설정한 다음에 프로그램을 시작할 수 있을 뿐이다. 프로그램이 실행되는 동안 모델의 정보를 동적으로 바꿀 수 없다. 이제 우리는 앞의 기능을 개선한다.
1, 온라인 모델 구조 설정 관리 실현
2, 모델 구성 변화 후 동적 생성 데이터베이스 테이블
3, 런타임 시 모델 정보를 DbContext에 등록
1. 온라인 모델 구조 설정 관리 실현
이전 글의 내용에서 우리는 모델 정보를 프로필에 저장했고 프로그램이 시작될 때 프로필을 불러오고 해석하며 모델의 컴파일을 완성했다.현재 우리는 설정 정보를 데이터베이스에 넣고 하나의 데이터 테이블로 설정 정보를 저장해야 한다.먼저 앞에서 언급한 RuntimeModelMeta 클래스를 다음과 같이 변경합니다.
public class RuntimeModelMeta
   {
       public int ModelId { get; set; }
       public string ModelName { get; set; }//    
       public string ClassName { get; set; }//   
       public string Properties{get;set;}//    json     
        
       public class ModelPropertyMeta
       {
           public string Name { get; set; }//       
           public string PropertyName { get; set; } //      
      public int Length { get; set; }//    ,    string  
 
       public bool IsRequired { get; set; }//      ,      
       public string ValueType { get; set; }//    ,      ,  ,bool 
       }
   }

public ModelProperty Meta[] ModelProperties {get; set;}속성이 String 유형으로 변경되고 모델 구성 관리를 위한 DbContext를 직접 정의합니다. 코드는 다음과 같습니다.
  public class ModelDbContext : DbContext
    {
        public ModelDbContext(DbContextOptions options) :base(options)
        {
        }

        public DbSet Metas { get; set; }
    }

이 DbContext가 있으면 런타임 모델 메타를 조작하는 것이 비교적 간단하다.또한 모델 속성 데이터의 조작을 편리하게 하기 위해 다음과 같은 확장 방법을 추가합니다.
public static class RuntimeModelMetaExtensions
    {
        //        
        public static RuntimeModelMeta.ModelPropertyMeta[] GetProperties(this RuntimeModelMeta meta)
        {
            if (string.IsNullOrEmpty(meta.Properties))
            {
                return null;
            }

            return JsonConvert.DeserializeObject(meta.Properties);
        }
      //          ,    
        public static void SetProperties(this RuntimeModelMeta meta, RuntimeModelMeta.ModelPropertyMeta[] properties)
        {
            meta.Properties = JsonConvert.SerializeObject(properties);
        }
    }

  
조작은 매우 간단하지만 문제는 모델 정보가 변할 때 DbContext에게 어떻게 알려주는가이다. 우리가 세 번째 부분에 이르렀을 때 여기에 설정 정보 관리만 완성하면 된다는 것이다.
2. 모델 설정 변화 후 동적 생성 데이터베이스 테이블
SQL 문을 사용하여 데이터베이스를 직접 조작할 수 있으며 간단한 패키지 클래스는 다음과 같습니다.
public static class ModelDbContextExtensions
    {
       //    
        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:
                        addFieldCmd.CommandText += "nvarchar(max)";
                        break;
                }

                addFieldCmd.ExecuteNonQuery();
            }
        }
        //    
        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();
            }
        }
        //     
        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();
            }

        }
    }

  
모델 설정 정보가 변화할 때 위의 봉인류를 통해 데이터베이스를 직접 조작하여 데이터 테이블 구조의 변화를 완성한다. 물론 여기에 제공된 방법은 매우 적기 때문에 모두가 다시 확장할 수 있다. 예를 들어 필드 유형을 수정하고 테이블을 삭제하는 등이다.
3. 실행은 모델 정보를 DbContext에 등록하는 것이다
우리는 앞에서 OnModelCreating 방법을 다시 써서 모델을 DbContext에 등록했지만 이 방법은 한 번만 실행될 뿐 실행할 때 모델 정보가 바뀌면 DbContext는 동기화할 수 없기 때문에 이 방법은 통하지 않는다.DbContext는 또 다른 방법인void On Configuring(DbContext Options Builder options Builder)도 제공했다. 이 방법은 DbContext를 실례화할 때마다 호출된다. 그러면 우리는 이 방법을 이용하여 모델 정보의 등록을 어떻게 하는가.이 메서드에는 다음과 같은 방법으로 모델을 등록할 수 있는 방법인 DbContextOptionsBuilder 매개변수가 포함되어 있습니다.
DbContextOptionsBuilder UseModel(IModel model)
IModel은 모델 정보 유지보수의 유형이다. 자연히 우리는 자신이 IModel을 만들고 위의 방법을 통해 등록을 완성할 것이라고 생각할 것이다.그럼 지금 문제는 IModel은 어떻게 얻나요?On Model Creating 방법을 다시 쓸 때 Model Builder 파라미터가 있는 것을 발견했습니다. 이 유형의 이름에서 우리가 필요로 하는 정보를 얻을 수 있을지 없을지 바로 생각할 수 있습니다.EntityFramework를 보십시오.Core 원본, 그것이 바로 우리가 찾는 물건이라는 것을 발견했다.먼저 ModelBuilder의 구성 방법을 살펴보겠습니다.
ModelBuilder(ConventionSet conventions)
그것은 하나의 Convention Set이 필요합니다. 직접 번역하면 제약의 집합입니다. (오류가 있으면 벽돌을 찍는 것을 환영합니다.) 그러면 어떻게 이런 대상을 얻을 수 있습니까?ef 원본 코드를 보면 프레임워크에서 IConventionSet Builder를 통해 만들어진 ConventionSet을 사용할 수 있습니다. 따라서 DbContext에서 주입에 의존하는 방식으로 ICoreConventionSet Builder를 인용합니다. 코드는 다음과 같습니다.
public class ShopDbContext:DbContext
    {
        private readonly ICoreConventionSetBuilder _builder;
        public ShopDbContext(DbContextOptions options, ICoreConventionSetBuilder builder) :base(options)
        {
            _builder = builder;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //  ModelBuilder   
            var modelBuilder = new ModelBuilder(_builder.CreateConventionSet());
            
        }
    }

Model Builder가 생기면 Model Builder를 통과할 수 있습니다.Model은 다음과 같은 모델 정보 등록을 완료할 수 있는 IMutable Model을 가져옵니다.
public class ShopDbContext:DbContext
{     
     private readonly ICoreConventionSetBuilder _builder;
        private readonly IRuntimeModelProvider _modelProvider;
        public ShopDbContext(DbContextOptions options, ICoreConventionSetBuilder builder, IRuntimeModelProvider modelProvider) :base(options)
        {
            _builder = builder;
            _modelProvider = modelProvider;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var modelBuilder = new ModelBuilder(_builder.CreateConventionSet());
            //_modelProvider          ,          ,                 
            Type[] runtimeModels = _modelProvider.GetTypes();
            foreach (var item in runtimeModels)
            {
                //      
                modelBuilder.Model.AddEntityType(item);
            }
            //    
            optionsBuilder.UseModel(modelBuilder.Model);
            base.OnConfiguring(optionsBuilder);
        }
}

  
  
이렇게 해서 우리는 동적 모델 정보를 등록하는 기능을 완성했다.만약 생성된 테이블 이름이 개성화되어야 한다면 우리는 다음과 같은 방식으로 수정할 수 있다.
 modelBuilder.Model.AddEntityType(item).SqlServer().TableName=""
위에서 ICoreConventionSetBuilder를 사용했기 때문에 Startup에서 AddEntityFramework를 호출하여 서비스 등록을 해야 합니다. 코드는 다음과 같습니다.
     public void ConfigureServices(IServiceCollection services)
        {
            。。。。。。
            services.AddEntityFramework().AddDbContext(option => {
                option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), sql => {
                    sql.UseRowNumberForPaging();
                    sql.MaxBatchSize(50);

                });
            });
         
            。。。。。。
        }

  
위에서 언급한 바와 같이 OnConfiguring 방법은 DbContext가 실례화될 때마다 호출된다. 그러면 우리의 모델 정보는 매번build를 사용해야 하기 때문에 좋지 않다. f는 캐시를 사용하는 방법이다. 그러면 우리는 자연히 사용할 수 있다.최종 ShopDbContext의 전체 코드는 다음과 같습니다.
 public class ShopDbContext:DbContext
    {
        private readonly ICoreConventionSetBuilder _builder;
        private readonly IRuntimeModelProvider _modelProvider;
        private readonly IMemoryCache _cache;
        private static string DynamicCacheKey = "DynamicModel";
        public ShopDbContext(DbContextOptions options, ICoreConventionSetBuilder builder, IRuntimeModelProvider modelProvider, IMemoryCache cache) :base(options)
        {
            _builder = builder;
            _modelProvider = modelProvider;
            _cache = cache;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //       model,      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.OnConfiguring(optionsBuilder);
        }

모델 설정이 바뀌었을 때 캐시를 정리해서 다음에 다시 접근할 때 새 설정에 따라 다시 Build를 만들 수 있습니다.
OK. 모든 작업을 끝낸 후에 실행할 때 동적 모형을 설정하는 기능을 완전히 실현할 수 있다.
뒤의 글은 동적 모델과 동적 표의 실현 방법을 계속 소개할 것이다.
  

좋은 웹페이지 즐겨찾기