EntityUI 自动 UI 生成器

jopen 11年前

介绍

EntityUI是一个能够通过在ASP.NET中的快速代码生成方式,创建用户界面和数据库存储,即可立即生成你所需要的应用。作为一个开发,去创建应用的时候,你只需要写域和视图模式来调整这些类即可,无需再写太多的代码了。

背景

我好几年前就开始使用ASP.net的EntityUI,也写了许多简单的代码来反射绘制UI图。这是我原来写的文章。但是,从我开始使用EntityUI到现在,它也发生了很多改变。MVC和Knockout已经流行了一段时间,而且他们都是生成MVVM模式很好的工具。

所以,现在EntityUI现在是一个使用MVC和Knockout自动从模型生成UI的工程。它主要的目标是成为一个应用程序的快速开发工具,不仅仅是生成通用的增删改查的页面,而是尝试去处理更高级别的方案。

先决条件

这是一个Microsoft MVC的工程。你首先要熟悉MVVM模式、域模型、视图模型,以及掌握knockout并能够使用它的一些高级功能。你也需要了解Repository模式,以及深入了解Microsoft Entity Framework Code First是怎么工作的。我们在nuget下载EntityUI包,并使用bootstrap 设计漂亮的布局。

使用代码

首先,我会用一个全新的项目来做展示。开始一个空的MVC工程,然后添加两个类库工程到解决(Solution)中,一个是给域模型(Domain Model),一个是给仓库(Repository)。然后从nuget中添加EntityUI到所有工程去。

安装EntityUI,执行以下命令:包的管理控制台包的管理控制台

PM> Install-Package EntityUi

EntityUI包含了一下几个核心的概念:

  • DomainModelBase - 基础类,域模型对象都需要继承它。

  • ViewModelBase - 基础类,视图模型对象都需要继承它。

  • ControllerBase - 控制器的基础类,它提供了增删改查的方法。

  • RepositoryBase - 仓库的基础类,它提供了基于Entity框架的增删改查方法。

接下来,我们需要写域模型(Domain Model)和仓库(Repository)方法。

在这个例子中,我们会用“Department”这个类作为例子。 这是从 MSDN Entity Framework Code First Fluent API example 中获取的例子。

public class Department : DomainModelBase      {          public Department()          {              Courses = new HashSet<Course>();          }             public string Name { get; set; }          public decimal Budget { get; set; }          public DateTime StartDate { get; set; }          public int? Administrator { get; set; }             // Navigation property          public ICollection<Course> Courses { get; set; }      }

正如你所看到的,这个类是继承了DomainModelBase类。

然后,开始实现仓库。添加你的内容(Context)类,并继承实例框架(Entity Framework)的DbContext类,然后给Department添加一个简单的仓库(Repository)类"DepartmentRepository":

public class DataContext : DbContext      {          public DbSet<Department> Departments { get; set; }              protected override void OnModelCreating(DbModelBuilder modelBuilder)          {              Database.SetInitializer(new CreateDatabaseIfNotExists<DataContext>());          }       }      public class DepartmentRepository : RepositoryBase<Department, DataContext>      {          protected override DataContext GetContext()          {              return new DataContext();          }      }

现在,DataContext类是一个非常简单的,只有一个属性的Context类。

仓库(Repository)很好实现。这个类继承了DomainModelBase,除了GetContext就没有别的方法了。正如你所见,当继承了RepositoryBase,你就得给Base提供实体名和DataContext,让它知道是什么在工作。

接下来我们把视野转移到视图(View),现在仍是一个空白的MVC项目。我们准备用Bootstrap来实现视图。我使用的是Eric Hexter在Nuget上提供的包,并做了些改变。我添加并修改一些文件,使之能够运行成功,它现在已经完全是EntityUI的核心部分之一。我最终会把包添加到Nuget上,但现在,你们可以在文章中下载该压缩包。

在web工程中建立好一些文件之后,开始添加模型(Model)、视图(View)和控制器(Controller)的类。我一直提倡要有一个独立的域(Domain)和视图模型(View Model)类,尽管在一般情况下他们是几乎相同的。我们要用AutoMapper简单的在域和视图模型中进行转换。

以下是Controller类:

public class DepartmentController : ControllerBase<Department, DepartmentView, DataContext>      {          public DepartmentController()          {              Repository=new DepartmentRepository();          }      }

这个控制器和其他类很像,都是要继承ControllerBase。它需要了解域模型(Domain Model)、视图模型(View Model)和DataContext。

最后一步是在global.aspx中使用AutoMapper将独立的域(Domain)和视图模型(View Model)进行映射:

Mapper.CreateMap<Department, DepartmentView>();              Mapper.CreateMap<DepartmentView, Department>()            .ForMember(d => d.Id, o => o.Condition(s => s.Id > 0))                    .ForMember(d => d.Courses, o => o.Ignore());;

以上是使用AutoMapper对域视图和模型视图的简单配置.它的基本做法是通过反射机制将一个对象的属性值赋给另外一个.本质上这个工具是开告诉 EntityUI如何将 域(Domain ) 转换成 模型视图(Model View )对象的.

现在可以创建和运行你的程序了,当你浏览Department时,它将把Department的内容展示出来,包括完整的CRUD功能包括渲染页面,并将它保存到数据库.

这就是最简单最基础的使用EntityUi,现在可以做一些更有乐趣的事情.

使用下拉列表

接下来介绍如何只需要修改你的视图模型便能使用下拉列表。首先先添加Course,如下:

这个是域模型(Domain Model):

public class Course : DomainModelBase      {          public Course()          {              Instructors = new HashSet<Instructor>();          }            public string Title { get; set; }          public int Credits { get; set; }            // Foreign key          public int DepartmentID { get; set; }            // Navigation properties          public Department Department { get; set; }          public ICollection<Instructor> Instructors { get; set; }        }

正如你所见,部门(Department)作为一个Navigation Property在UI中展示,我们希望看到所有部门的下拉列表,并且用户能够选择部门。

下面是仓库(Repository)和控制器(Controller):

public class CourseRepository : RepositoryBase<Course, DataContext>      {          protected override DataContext GetContext()          {              return new DataContext();          }      }

他们都只是简单地从EntityUi的RepositoryBase和ControllerBase继承过来的。

现在是模型视图(Model View):

public class CourseView : ViewModelBase      {          public string Title { get; set; }          public int Credits { get; set; }            public DropDown Departments { get; set; }            public CourseView()          {              Departments = new DropDown              {                  Items = (new DepartmentRepository())                            .List()                            .ToList()                            .ConvertAll(x => new SelectListItem                            {                                Text = x.Name,                                Value = x.Id.ToString()                            })              };          }      }

这里有点不太一样。我们定义了Departments Property作为EntityUi中“DropDown”的助手类型,来给Department提供下拉列表。此外,我们从数据库中加载了部门的清单,放在构造函数里。我希望代码是一目了然的,不需要过多的解释。我们只需要调用在DepartmentRepository 中的“List()”方法(EntityUi提供的)去获取所有部门,然后用“ConvertAll()”方法(Linq提供的)来转换为SelectListItem。

另一个有趣的部分是映射模型视图(Model View)和域模型(Domain View)。下面的代码是需要添加到Global.asax:

Mapper.CreateMap<DepartmentView, Department>()                    .ForMember(d => d.Id, o => o.Condition(s => s.Id > 0))                    .ForMember(d => d.Courses, o => o.Ignore());       Mapper.CreateMap<Department, DepartmentView>();

这实际上是Auto Mapper的一个比较新鲜的用法,你可以看看AutoMapper的有关文档。

在第一个映射表中,分别把CourseView(视图模型)映射到Course(域模型)。有趣的部分是,把域(Domain)的部门ID映射到视图(View)的部门下拉列表中的SelectedId的属性。在第二次映射的时候,我们是从域到视图模型,而它也是完全相反的。

这些基本上是所需要的代码。如果你想现在运行,并导航到“Courses”,你就需要有添加、编辑、删除部门的下拉选项。



增加用户接口交互

到目前为止,我们已经看到了一些基本的 CRUD 功能。通过处理模型视图,不仅仅能实现 CRUD 功能。因此比如说,我们有这样的业务需要,只要预算(budget)被分配部门就应该申请预算,否则,预算输入框不应该显示出来。

关于它最好的实现将是视图中的 Javascript。处理这些场景最容易的方法是通过 knockout。EntityUi 允许你增加 knockout 绑定到使用 DataBind 数据标注的任意属性上。

所以,我们通过如下方式简单的修改 Department 视图,来满足这个需求:

public class DepartmentView : ViewModelBase   {      public string Name { get; set; }      public bool HasBuget { get; set; }      [DataBind("visible:HasBuget()")]      public decimal Budget { get; set; }      public DateTime StartDate { get; set; }   }

这样,我这里所作的是首先增加了一个 boolean 属性的 HasBudget。这将渲染成一个检查框。

之后,我增加了 DataBind 属性,提供了我通常会给视图增加的 knockout 绑定。EntityUi 会自动渲染它。你需要熟悉 knockout 绑定来充分利用这个特性。如果没有,请查看 www.knockoutjs.com 的文档。所有这里所做的是设置 HasBudget 属性的可见性。在幕后,EntityUi 使用 knockoutmvc 将每个模式视图设置为 knockout 视图模型,使得每个属性都可以被观察。

这处代码变化所做的事情是,当你在编辑模式查看页面时,如果勾选了 "HasBudget" 检查框,它会显示 Budget 输入字段和标签,如果没有勾选则会隐藏它们。

总结

因此希望通过上文提到的细节,你可以了解 EnitityUi 是如何通过利用工具处理代码编写的各个方面,从数据处理直到用户界面,来用于快速应用程序开发的。

作为开发者,主要的任务是书写 Domain Model 和 View Model。之后其它的大多数事情是增加属性,和 Fluent 的 API 配置,以及遵守约定,这样我们就会有个全功能的应用程序。

EntityUi 也非常灵活,为开发者提供了完全的控制。开发者可以对任何类进行重载或者增加方法,不管是 Repository,Controller 还是 View。你可以在同样的应用程序中,不使用 EntityUi 而是凭自己非常容易的书写出完整的页面或者类,或者利用 EntityUi 的框架将其增加到视图或者控制器中。

参考

我使用了许多不同的开源库实现了 EntityUi。下面是其中的一些主要开发库:

  • MVC Bootrap 包 推ter.bootstrap.mvc4,作者 Eric Hexter
    (https://www.nuget.org/packages/推ter.bootstrap.mvc4/)

  • AutoMapper,作者 Jimmy Bogard
    (https://www.nuget.org/packages/AutoMapper/3.0.0)

  • knockoutmvc (http://knockoutmvc.com/)