• 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();
    

      

    所有代码还是需要大家自己完善补充,如果有不足之处,欢迎大家能够批评指正。

      

  • 相关阅读:
    Leetcode167-Two Sum II Input array is sorted-Easy
    Leetcode122-Best Time to Buy and Sell Stock II-Easy
    Leetcode121-Best Time to Buy and Sell Stock I
    Leetcode118_Pascal's Triangle_Easy
    Leetcode88_Merge Sorted Array_Easy
    Leetcode66-Plus One-Eassy
    Open DS
    python, general, for quick review
    [python] CSV read and write using module xlrd and xlwt
    [python] xlrd
  • 原文地址:https://www.cnblogs.com/dxp909/p/6483618.html
Copyright © 2020-2023  润新知