目录
在Visual Studio 2015更新3中创建MasterChef应用程序
本文向您展示如何集成ASP.NET Core MVC,Fluent NHibernate和AngularJS来构建简单的Web应用程序。它带您逐步了解构建单页应用程序所涉及的元素。它涵盖Fluent NHibernate,它的安装、添加数据模型类、映射类和存储库类,添加和配置Grunt,安装Angular JS,添加index.html,Angular JS应用模块,服务工厂和控制器。
介绍
ASP.NET 5已死,并被重命名为ASP.NET Core 1.0。但是,尽管命名了新名称,但完全从头编写的ASP.NET框架“ASP.NET 5”并不是一个好主意,原因有一个:5使ASP.NET 5看起来更大、更好,并且替代了ASP.NET 4.6。不是这样。因此,ASP.NET 5现在是ASP.NET Core 1.0,而NET Core 5现在是.NET Core 1.0。为什么是1.0?因为这些是新的。整个.NET Core概念是新的。.NET Core 1.0 CLI非常新。不仅如此,.NET Core还不如完整的.NET Framework 4.6完整。
跨平台兼容性是使用ASP.NET Core的一大新功能。从此版本开始,我们既可以在Windows上开发和运行ASP.NET Core,并且一直在这种情况下运行,但也可以在Mac OS X和Linux操作系统上运行。
ASP.NET Core和MVC是服务器端,但是正如我们前面提到的,在单页应用程序中,还存在一个客户端组件。Angular实际上是最受欢迎的框架之一。它建立在HTML,CSS和JavaScript等网络技术的基础之上,并遵循模型视图的任何模式,这基本上使我们能够创建在表示和业务逻辑之间具有脱钩关系的应用程序。
NHibernate是用于Microsoft .NET平台的对象关系映射(ORM)解决方案。它提供了一个框架,用于将面向对象的域模型映射到传统的关系数据库。Fluent NHibernate提供了NHibernate标准XML映射文件的替代方法。Fluent NHibernate 无需编写XML文档(.hbm.xml文件),而是允许您使用强类型的C#代码编写映射。这样可以方便地进行重构,提高可读性并简化代码。
在本文中,我想展示如何使用ASP.NET Core MVC,Fluent Hibernate和Angular JS构建单页应用程序——MasterChef。
Master Chef Recipe数据模型UML
在本地SQL Express中,只需创建一个数据库“MasterChef”。然后在sql文件夹下运行schema.sql。
在Visual Studio 2015更新3中创建MasterChef应用程序
为了使用ASP.NET Core,您需要更新ASP.NET Web工具。最新版本为2.0.2,可从此链接下载。
从Visual C#/Web中,选择ASP.NET Core Web应用程序(.NET Framework)。
ASP.NET Core具有两种应用程序:
- ASP.NET Core .NET Framework应用程序是使用.NET Framework在Windows上运行的应用程序。
- ASP.NET Core .NET Core应用程序是使用.NET Core在Windows,Linux和OS X上运行的跨平台应用程序。
选择“Empty ”模板,然后取消选中“Host in cloud ”。
看一下ASP.NET Core Web解决方案的结构。它创建一个“src”文件夹,实际项目在“src”文件夹下。在此src文件夹中,有一个特殊文件夹——wwwroot,该文件夹将容纳我们所有的实时Web文件。因此,任何HTML,我们最终的angularapp.js,任何其他压缩的脚本,图像资产或将在实时网站上提供的类似内容都位于此处。但是,我们所有的源代码(实际上就是运行此ASP.NET 5 MVC 6 Angular应用程序的代码)都不会在wwwroot文件夹中保存。
添加Fluent NHibernate数据模型
1)安装Fluent NHibernate
从Nuget软件包管理器添加Fluent NHibernate软件包。
打开project.json并添加“FluentNHibernate”:“2.0.3”
2)添加数据模型类
在我们的解决方案中,创建“Models”文件夹。添加Recipe,RecipeStep和RecipeItem模型类模型文件夹中。
public class Recipe
{
public virtual Guid RecipeId { get; set; }
public virtual string Name { get; set; }
public virtual string Comments { get; set; }
public virtual DateTime ModifyDate { get; set; }
public virtual IList<RecipeStep> Steps{ get; set; }
}
public class RecipeStep
{
public virtual Guid RecipeStepId { get; set; }
public virtual int StepNo { get; set; }
public virtual string Instructions { get; set; }
public virtual IList<RecipeStep> RecipeItems { get; set; }
}
public class RecipeItem
{
public virtual Guid ItemId { get; set; }
public virtual string Name { get; set; }
public virtual float Quantity { get; set; }
public virtual string MeasurementUnit { get; set; }
}
3)添加Fluent NHibernate映射类
如前所述,NHibernate映射XML(.hbm)被Fluent Hibernate中的映射类替代。因此,我们需要为每个数据模型类提供映射类。
public class RecipeMap : ClassMap<Recipe>
{
public RecipeMap()
{
Id(x => x.RecipeId);
Map(x => x.Name);
Map(x => x.Comments);
Map(x => x.ModifyDate);
HasMany(x => x.Steps).KeyColumn("RecipeId").Inverse().OrderBy("StepNo Asc");
Table("Recipes");
}
}
如何映射列表?在RecipeMap类中,使用HasMany(x=>x.Steps). KeyCoumn("RecipeId")是RecipeSteps表中的引用键。此外,recipe step需要由StepNo进行排序,其使用OrderBy。相同的映射在RecipeStepMap类中发生。
public class RecipeStepMap : ClassMap<RecipeStep>
{
public RecipeStepMap()
{
Id(x => x.RecipeStepId);
Map(x => x.StepNo);
Map(x => x.Instructions);
HasMany(x => x.RecipeItems).KeyColumn("RecipeStepId").Inverse();
Table("RecipeSteps");
}
}
public class RecipeItemMap : ClassMap<RecipeItem>
{
public RecipeItemMap()
{
Id(x => x.ItemId);
Map(x => x.Name);
Map(x => x.Quantity);
Map(x => x.MeasurementUnit);
Table("RecipeItems");
}
}
4)添加存储库(Repository)类
我们使用存储库模式来分离检索数据并将其从作用在模型上的业务逻辑映射到实体模型的逻辑。业务逻辑应该与构成数据源层的数据类型无关。
存储库在应用程序的数据源层和业务层之间进行中介。它查询数据源中的数据,将数据从数据源映射到业务实体,并将业务实体中的更改持久保存到数据源。存储库将业务逻辑与基础数据源的交互分开。
在Repository类中,我们需要配置Fluent NHibernate会话。
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012
.ConnectionString("Server=.\\sqlexpress;
Database=MasterChef; Integrated Security=SSPI;"))
.Mappings(m => m
.FluentMappings.AddFromAssemblyOf<Repository>())
.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
您可以通过.FluentMappings.Add(…)添加列表映射类。另外,您可以通过以下.FluentMappings.AddFromAssembly(…)方式在程序集中添加所有映射类:
添加Web API控制器
1)安装ASP.NetCore.Mvc
我们在Nuget包管理器project.json上添加了Asp.NetCore.Mvc包。
2)添加API RecipesController
创建一个“api ”文件夹,然后右键单击该API文件夹以添加一个新项目。在ASP.NET中,选择“Web API控制器类”模板。我们将类命名为RecipesController.cs。
在RecipesController类中,我们设置了一些函数来处理基本的CRUD请求。我们在这里收到要求所有食谱的GET请求。我们在这里还有另一个函数Get,该函数带有一个ID,以便用户可以请求我们返回的特定食谱。我们这里还有更多函数,例如POST允许用户创建新配方。还有PUT,我们可以在其中更新现有配方。最后是DELETE,可以删除特定配方。因此,所有这些都是来自Web API控制器类的样板文件,我们将对其进行添加以创建实际的应用程序。
在RecipesController类上,我们添加_repository成员来处理数据库内容。
HttpGet 获得所有食谱。
[HttpGet]
public IEnumerable<Recipe> Get()
{
return _repository.GetAllRecipes();
}
HttpGet(id) 获得特定的食谱。
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var recipe = _repository.GetRecipe(id);
if (recipe != null)
return new ObjectResult(recipe);
else
return new NotFoundResult();
}
HttpPost(Recipe)添加或更新配方。如果输入的食谱ID为空,则添加一个新的食谱;否则,我们将更新现有食谱。
[HttpPost]
public IActionResult Post([FromBody]Recipe recipe)
{
if (recipe.RecipeId == Guid.Empty)
{
return new ObjectResult(_repository.AddRecipe(recipe));
}
else
{
var existingOne = _repository.GetRecipe(recipe.RecipeId);
existingOne.Name = recipe.Name;
existingOne.Comments = recipe.Comments;
_repository.UpdateRecipe(existingOne);
return new ObjectResult(existingOne);
}
}
HttpPut(id, recipe) 更新食谱。
[HttpPut("{id}")]
public IActionResult Put(Guid id, [FromBody]Recipe recipe)
{
var existingOne = _repository.GetRecipe(recipe.RecipeId);
existingOne.Name = recipe.Name;
existingOne.Comments = recipe.Comments;
_repository.UpdateRecipe(recipe);
return new ObjectResult(existingOne);
}
HTTPDelete 删除食谱。
[HttpDelete("{id}")]
public IActionResult Delete(Guid id)
{
_repository.DeleteRecipe(id);
return new StatusCodeResult(200);
}
3)配置MVC
添加食谱控制器后,我们假设 http://localhost/api/recipes返回所有食谱。咱们试试吧。
没用 为什么?那是因为我们还没有配置MVC。
转到Startup.cs。
在ConfigureServices(…)中添加services.AddMvc()的,并在Configure(…)中添加app.UseMvc。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime.
// Use this method to configure the HTTP request pipeline.
public void Configure
(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
添加完这些后,让我们再试一次,在菜单栏中单击“IIS Express”。
现在,URL正常了,并且返回了JSON结果。
Grunt
1)添加Grunt
添加一个新文件夹“scripts”,它将保存我们所有的脚本文件。如前所述,所有运行中的脚本和html都位于wwwroot文件夹下。因此,现在,我们需要安装Grunt来帮助我们将“scripts”文件夹下的所有脚本自动安装到wwwroot文件夹。最终,我们希望Grunt帮助我们观察该文件夹,并合并和压缩其中的所有脚本,然后再将结果移到我们的实时wwwroot文件夹中。
要安装Grunt,我们将使用对ASP.NET Core带来的NPM软件包管理器的内置支持。这是对Bower软件包管理器和NuGet软件包管理器的支持的补充。
右键单击我的项目以添加一个新项。我要选择“客户端”并在此处滚动,直到看到NPM配置文件选项,package.json是一个适当的名称。
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"grunt": "1.0.1",
"grunt-contrib-uglify": "2.0.0",
"grunt-contrib-watch": "1.0.0",
"bower": "1.7.9"
}
}
2)配置 Grunt
接下来实际上是配置Grunt。右键单击该项目,转到Add - New Item,然后从客户端部分选择Grunt Configuration文件。将名称保留为gruntfile.js并添加该文件。
我们分别配置uglify插件和watch插件。对于uglify插件(它将帮助我们将脚本文件夹中的所有JavaScript文件压缩为另一个文件– app.js),该插件将位于wwwroot文件夹中。第二种配置是设置为监视脚本文件夹。而且,只要该文件夹中的所有JavaScript文件发生更改,它都会自动运行uglify,以确保wwwroot文件夹中的app.js中具有最新版本的脚本。这个Grunt配置文件中的最后一件事是注册要运行的任务。
打开Gruntfile.js:
module.exports = function (grunt) {
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.initConfig({
uglify: {
my_target: {
files: {
'wwwroot/app.js': ['scripts/app.js', 'scripts/**/*.js']
}
}
},
watch: {
scripts: {
files: ['scripts/**/*.js'],
tasks: ['uglify']
}
}
});
grunt.registerTask('default', ['uglify', 'watch']);
};
现在,我们应该能够实际运行该文件了。转到查看菜单——Other Windows。选择将在底部显示的Task Runner Explorer。刷新以查看已保存的任务。因此,请运行默认任务。现在您可以看到输出。而现在,结果是什么也没有写到我们的wwwroot/app.js中。这就是我们所期望的,因为我们的脚本文件中没有任何脚本可以缩小,然后从中创建app.js。
Angular JS
1)安装Angular JS
我们使用Bower包管理器来获取客户端Angular JavaScript文件。因此,要做的第一件事是右键单击我们的项目以添加一个新项目。在客户端上选择一个Bower配置文件“bower.json ”。
完成后,bower.json不会显示在我们的解决方案中。发生了什么?我不知道。这应该是Visual Studio的错误,需要Microsoft修复。目前,我们必须从文件系统中打开bower.json。
在bower.json中,在dependencies部分中添加“jquery”,“bootstrap”,“angular”,“angular-route”和“angular-resource” 。
{
"name": "asp.net",
"private": true,
"dependencies": {
"jquery": "3.1.0",
"bootstrap": "3.1.0",
"angular": "1.5.8",
"angular-route": "1.5.8",
"angular-resource": "1.5.8"
}
保存后,Visual Studio开始自动还原所有这些程序包。
还原完成后,Visual Studio将这些软件包安装在wwwroot\lib文件夹下。
2)添加index.html
现在,我们需要在wwwroot文件夹下添加index.html 作为主页。
我们只是创建一个非常简单的index.html,确保它被ASP.NET Core使用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>Master Chef Recipes</h1>
</body>
</html>
单击“IIS Express”启动我们的Web应用程序。
它似乎不起作用。那是因为我们没有告诉ASP.NET Core使用静态文件。
转到startup.cs。
在Configure(…)方法中添加以下两行:
app.UseDefaultFiles();
app.UseStaticFiles();
然后再次启动我们的Web应用程序。
现在可以了。
3)添加Angular JS App模块
现在我们要创建的是一个Angular模块,它将成为我们的应用程序。记住,我们在“scripts”文件夹下创建所有JavaScript 。因此,右键单击我们项目中的“scripts ”文件夹。添加一个新项目。在“客户端模板”部分中,选择“AngularJs模块”。app.js的默认名称。
在app.js中,将应用程序名称更改为“masterChefApp”。
(function () {
'use strict';
angular.module('masterChefApp', [
// Angular modules
//'ngRoute'
// Custom modules
// 3rd Party Modules
]);
})();
4)添加服务工厂
从Angular JS中,我们需要以某种方式调用服务器Web API。这可以通过服务来完成。因此,我们创建了一个服务工厂来进行http调用。
首先,我们在scripts下创建一个“service”文件夹。然后右键单击“service”以添加新项目。在客户端中,选择AngularJs Factory模板。将名称更改为recipesFactory.js。
在recipesFactory.cs中,将app更改为masterChefApp,然后在getData()中调用$http.get(‘api/recipes/’)。
(function () {
'use strict';
angular
.module('masterChefApp')
.factory('recipesFactory', recipesFactory);
recipesFactory.$inject = ['$http'];
function recipesFactory($http) {
var service = {
getData: getData
};
return service;
function getData() {
return $http.get('/api/recipes');
}
}
})();
5)添加Angular JS控制器
现在,我们要创建一个可以在浏览器中显示配方的客户端控制器。
首先,我们在脚本下创建一个控制器文件夹。然后,右键单击控制器,添加一个新项目。在“客户端”中,选择“使用$scope模板的AngularJs控制器” 。将名称更改为recipesController.js。
在控制器功能体内,设置一个调用配方的作用域变量。我们通过使用recipesFactory及其getData函数对其进行设置。
function () {
'use strict';
angular
.module('masterChefApp')
.controller('recipesController', recipesController);
recipesController.$inject = ['$scope', 'recipesFactory'];
function recipesController($scope, recipesFactory) {
$scope.recipes = [];
recipesFactory.getData().success(function (data) {
$scope.recipes = data;
}).error(function (error) {
//log errors
});
}
})();
保存后,您可以查看wwwroot文件夹,该文件夹已自动创建一个压缩的app.js,其中包括app.js,recipesFactory.js和recipesController.js中的所有脚本。
6)更改index.html以使用Angular模块
第一件事是使用ng-app指令来激活我们的Angular应用程序。因此,当使用ng-app指令时,必须确保使用在app.js中定义的相同名称,即masterChefApp。另外,我们需要导入一些所需的脚本文件。最后,引用app.js ——您可以看到的所有JavaScript文件都在我的wwwroot文件夹中。继续,在我的<body>标签上,使用另一个指令。这就是ng-cloak指令。这将使主体保持隐藏状态,直到Angular完全加载了数据并渲染了我的模板为止。在<body>内部,定义一个div。使用ng-controller指令来指示此div。
<!DOCTYPE html>
<html ng-app="masterChefApp">
<head>
<base href="/">
<meta charset="utf-8" />
<title>Master Chef Recipes</title>
<script src="lib/angular/angular.min.js"></script>
<script src="lib/angular-resource/angular-resource.min.js"></script>
<script src="lib/angular-route/angular-route.min.js"></script>
<script src="app.js"></script>
<link href="lib/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet" media="screen">
</head>
<body ng-cloak>
<div ng-controller="recipesController">
<h1>Master Chef Recipes</h1>
<ul>
<li ng-repeat="recipe in recipes">
<p> {{recipe.name}} - {{recipe.comments}}</p>
<ul>
<li ng-repeat="step in recipe.steps">
<p> step {{step.stepNo}} : {{step.instructions}}</p>
<ul>
<li ng-repeat="item in step.recipeItems">
<p> {{item.name}}
{{item.quantity}} {{item.measurementUnit}}</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</body>
</html>
好。现在,通过单击IIS Express来运行它。
浏览器上显示两个主厨师食谱。一种是蜂蜜鸡,另一种是蒙古羊肉。就像Angular JS一样,非常容易学习。
Angular 资源使Master Chef更好
在recipesFactory中,我们显式调用$http.get。工作正常。但是我们可以通过ngResource让它变得更简单更好。该ngResource模块通过$resource服务提供与RESTful服务的交互支持。在$resource服务中,所有http get,post,put和delete操作均已内置。您只需要传递Url,而无需显式调用它们。
首先在app.js中,我们注册一个自定义模块recipesService。
(function () {
'use strict';
angular.module('masterChefApp', [
// Angular modules
//'ngRoute'
// Custom modules
'recipesService'
// 3rd Party Modules
]);
})();
然后添加另一个AngularJS Factory类,我们将其命名为recipesService.cs。
在recipesService.cs中,我们注入ngresource并传递“/api/recipes/:id”。
(function () {
'use strict';
var recipesService = angular.module('recipesService', ['ngResource']);
recipesService.factory('Recipe', ['$resource', function ($resource) {
return $resource('/api/recipes/:id');
}]);
})();
在recipesController.cs中,我们可以直接使用Recipe.query()。
(function () {
'use strict';
angular
.module('masterChefApp')
.controller('recipesController', recipesController);
recipesController.$inject = ['$scope', 'Recipe'];
function recipesController($scope, Recipe) {
$scope.recipes = Recipe.query();
}
})();
现在我们不再需要recipesFactory.cs。我们将其删除。然后再次运行我们的master chef Web应用程序。
它像一种魅力。
结论
在本文中,我将向您展示如何集成ASP.NET Core MVC,Fluent NHibernate和AngularJS来构建简单的Web应用程序。在Master Chef Part 2中,我将讨论Angular路由,并使用Angular路由构建SPA CRUD(列表,添加,编辑,删除)应用程序。另外,我将向您展示如何使用引导样式来使您的Web应用程序看起来更好。