EFcore與動態模型(三)


緊接着上面的內容,我們繼續看下動態模型頁面交互實現方式,內容如下:

1,如何實現動態表單

2,如何接收表單數據並綁定到動態模型上

一、如何實現動態表單

由於模型信息都是后台自定義配置的,並不是固定不變的結構,所以沒有辦法直接在頁面上寫出對應的表單數據,而需要通過解析模型的結構,動態的生成對應的表單。在說具體實現方法前,我們先來看下我們想要達到的效果。

Html.Raw(FormGenerator.Generate(Model,Properties))

FormGenerator.Generate包含兩個參數,一個動態模型對象,一個需要呈現的屬性列表,方法返回最終生成的form表單html,然后通過Html.Raw呈現到頁面上。

下面介紹一下實現過程,首先定義一個IDynamicFormGenerator接口,代碼如下:

   public interface IDynamicFormGenerator
    {
        string Generate(object obj, IEnumerable<RuntimeModelMeta.ModelPropertyMeta> properties);
    }

  接口中只包含一個方法,就是我們上面實例代碼用到的方法。接口實現邏輯上很簡單,只需要循環每一個屬性,根據屬性的特點生成一個表單,並把對象數據綁定到表單上,比如屬性如果是bool類型,我們可以生成一個checkbox,然后根據obj對應的屬性值是true還是false,進而設置checkbox的選中狀態。具體實現代碼:

    public class DynamicFormGenerator : IDynamicFormGenerator
    {
        
        public string Generate(object obj, IEnumerable<RuntimeModelMeta.ModelPropertyMeta> properties)
        {
            StringBuilder builder = new StringBuilder();
            //循環屬性集合
            foreach (var item in properties)
            {
                //TODO:根據屬性信息生成表單並綁定數據
         string fieldhtml="";
               //把生成的html加入到stringbulder中
               builder.Append(fieldhtml);

            }
            //返回最終的html結果
            return builder.ToString();
        }
    }

  這個方法里最主要的部分就是如何根據屬性的類型生成對應的表單,就是如何得到上面的fieldhtml?我們再來看下前面定義的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等
       }
   }

  ModelPropertyMeta里包含了一個ValueType信息,就是當前屬性的數據類型,那我們是否可以根據這個來確定生成的表單形式?答案是否定的,因為即使是同一種類型也會呈現不同的表單,比如都是字符串,可能有的要求呈現下拉框,有的要求呈現復選框,所以只依靠ValueType還不夠,我們可以給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 string ShowType { get; set; }//表單形式
       }
   }

  

  有了ShowType,我們就可以根據設置的類型來生成對應的表單。首先先定義個表單生成器接口,代碼如下:

 public interface IDynamicFormFieldGenerator
    {
        //根據傳遞的屬性及對象,生成表單
        string Generate(object obj, RuntimeModelMeta.ModelPropertyMeta meta,bool onlyform=false);
        //這個表示當前的實現是針對哪一種ShowType的
        string ForType { get; }
    }

然后針對每一種ShowType實現一個生成器,比如針對checkbox類型的ShowType,我們實現一個生成器,代碼如下:

public class CheckboxFieldGenerator : IDynamicFormFieldGenerator
    {
        public string ForType
        {
            get
            {
                return "checkbox";
            }
        }

        public string Generate(object obj, RuntimeModelMeta.ModelPropertyMeta meta,bool onlyform=false)
        {
            //把動態對象轉換成一個DynamicEntity,為的是后面獲取數據方便,因為DynamicEntity支持通過索引獲取屬性數據
            DynamicEntity entity = obj as DynamicEntity;
            if (obj == null)
            {
                throw new NullReferenceException("DynamicEntity");
            }
            //通過entity[meta.PropertyName]獲取到屬性數據
            return string.Format("<input id='{1}' name='{1}' type='checkbox'  value='{0}'/>",
                 entity[meta.PropertyName]?.ToString(), meta.PropertyName);
            
        }
    }

  

  其他ShowType類型,可以根據自己系統的需要,直接實現即可,這里不再一一列舉了。

    有了表單構造器,我們再回頭完善下DynamicFormGenerator,在DynamicFormGenerator中,我們需要根據屬性的ShowType信息獲取到IDynamicFormFieldGenerator,我們可以定義一個DynamicFormFieldGeneratorProvider,方便我們得到我們所需要的IDynamicFormFieldGenerator,具體實現代碼:

    //提供者接口定義
    public interface IDynamicFormFieldGeneratorProvider
    {
        IDynamicFormFieldGenerator Get(string type);
    }
public class DynamicFormFieldGeneratorProvider: IDynamicFormFieldGeneratorProvider
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private IEnumerable<IDynamicFormFieldGenerator> _generators;
        public DynamicFormFieldGeneratorProvider(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        public IDynamicFormFieldGenerator Get(string type)
        {
            //通過依賴注入,獲取到構造器實現集合,所以我們需要把所有的構造器實現都注冊到ServiceCollection中
            if (_generators==null)
            {
                _generators= _httpContextAccessor.HttpContext.RequestServices.GetServices<IDynamicFormFieldGenerator>(); 
            }
            if (_generators==null)
            {
                throw new NotSupportedException("IDynamicFormFieldGenerator");
            }
            //根據type找到第一個符合條件的構造器,並返回
            IDynamicFormFieldGenerator g = _generators.FirstOrDefault(m => m.ForType == type);
            if (g==null)
            {
                throw new NotSupportedException("not supproted for " + type + "'s form field generator");
            }
            return g;
        }
    }

  

  條件都准備好了,直接完善DynamicFormGenerator,最終代碼如下:

public class DynamicFormGenerator : IDynamicFormGenerator
    {
        private readonly IDynamicFormFieldGeneratorProvider _fieldGeneratorProvider;
        public DynamicFormGenerator(IDynamicFormFieldGeneratorProvider provider)
        {
            _fieldGeneratorProvider = provider;
        }
        public string Generate(object obj, IEnumerable<RuntimeModelMeta.ModelPropertyMeta> properties)
        {
            StringBuilder builder = new StringBuilder();
           
            foreach (var item in properties)
            {
                //根據showtype獲取表單構造器
                IDynamicFormFieldGenerator fieldGenerator = _fieldGeneratorProvider.Get(item.ShowType);
                builder.Append(fieldGenerator.Generate(obj,item));
            }
            return builder.ToString();
        }
    }

  

  到此表單就可以呈現到界面上了。

二、如何接收表單數據並綁定到動態模型上

在mvc中提供了數據綁定機制,可以快速的把表單數據綁定到對象上,但是現在我們的對象是動態的,那又該如何應對?

在表單操作中,當前對應的模型我們肯定知道,所以可以借助前面介紹的內容,我們先得到一個動態模型對象,具體操作如下:

//根據模型id獲取到type 
Type modelType = _runtimeModelProvider.GetType(modelid);
//實例化
object obj = Activator.CreateInstance(modelType);

  

 我們現在需要解決的是,如何把動態表單提交的數據綁定到obj的屬性上。方法也很簡單,在mvc中給我們提供了很好的支持,方法就是TryUpdateModelAsync,借助這個方法,就可以很方便的把數據綁定到obj上,具體調用實例

  TryUpdateModelAsync(obj, modelType, "")

  obj就是上面實例化的對象,modelType就是動態模型對應的Type信息,有了數據后,后面就是通過ef完成數據庫同步的事了,比如增加

ShopDbContext.Add(obj);
ShopDbContext.SaveChanges();

  

所有代碼還是需要大家自己完善補充,如果有不足之處,歡迎大家能夠批評指正。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM