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

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

更多相关文章
  • Windows Socket I/O模型以及Linux Epoll模型的有关资料
    本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教. 一:select模型 二:WSAAsyncSelect模型 三:WSAEventSelect模型 四:Overlapped I/O 事件通知模型 五:Overlapped I/O 完成例程模型 ...
  • 用Ogre2.0 打造新3D引擎教程
    本文讲解了用Ogre2.0全新功能打造新3D引擎的步骤,然后再用Ogre2.1 结合OpenGL3+高效渲染,喜欢玩3D的朋友可以参考一下.从现在Ogre2.1的代码来看,大约总结下,更新包含去掉过多的设计模式,SoA的数据结构(用于SIMD,DOD),新的线程模式,新的渲染流程与场景更新,新的材质 ...
  • 局部加权回归、欠拟合、过拟合LocallyWeightedLinearRegression、Underfitting、Overfitting
         本文主要讲解局部加权(线性)回归.在讲解局部加权线性回归之前,先讲解两个概念:欠拟合.过拟合,由此引出局部加权线性回归算法. 欠拟合.过拟合     如下图中三个拟合模型.第一个是一个线性模型,对训练数据拟合不够好,损失函数取值较大.如图中第二个模型,如果我们在线性模型上加一个新特征项,拟 ...
  • ABP源码分析三十五:ABP中动态WebAPI原理解析
    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能,这应该算是对DRY的最佳诠释了. 如下图所示,一行代码就为所有实现了IApplicationServ ...
  • LVS负载均衡群集之NAT模型&&DR模型
    LVS 是linux virtual server 的简写,即linux的虚拟服务器,是一个虚拟的服务器集群系统.ip负载均衡技术是在负载调度器的实现技术中效率最高的,在已有的ip负载均衡技术中,主要通过网络地址转换(即VS/NAT 技术).由于使用nat技术会使分派器成为瓶颈,所以提出了通过ip隧 ...
  • LB群集--lvs-dr模型
    lvs简介: LVS是一个开源的软件,由毕业于国防科技大学的章文嵩博士于1998年5月创立,可以实现LINUX平台下的简单负载均衡.LVS是Linux Virtual Server的缩写,意思是Linux虚拟服务器.Lvs 的集群工作模式有3种:VS/NAT,vs/tun,vs/dr.Lvs的调度算 ...
  • 数据库精华知识点总结1—数据库的三层模式和二级映像,E-R实体联系图图,关系模型
    Data base: 长期存储在计算机内,有组织的,可共享的大量数据集合.基本特征:永久存储,可共享,有一定的物理和逻辑结构. Data base manage system(DBMS):用户和os之间的一层数据管理软件. 1.提供数据操纵语言DML对数据库增删改查 2.数据库的建立和维护 3.提供 ...
  • Lungo使用说明及心得中文api——模型部分
            最近在尝试着做webapp应用,考虑应用要实现的功能,通过比对现在流行的几套框架,最终确定尝试使用lungojs这个框架,经过几周的体验,感觉还不错 轻量小巧,迎合技术发展的脚步,完美的架构与html5+CSS3+JS,而且实现了扁平化,通过 quojs框架实现了各种设备的手势和动作 ...
一周排行