厨师长(第二部分)NET Core MVC与Fluent NHibernate和AngularJS

在Maser Chef第1部分中,我介绍了如何集成ASP。NET Core MVC与Fluent NHibernate和Angular JS。在这篇文章中,我将讨论如何使用ASP。NET Core MVC, Fluent NHibernate和Angular JS来实现一个CRUD SPA(单页应用)。 在存储库中使用泛型 创建、读取、更新和删除(CRUD的首字母缩写)是持久性存储的四个基本功能。 我们需要首先在我们的repository类的数据库级别上实现CRUD。我希望在查询、添加、更新、删除方法中使用泛型,以避免冗余编码。为什么使用泛型?简短的回答是,类型安全、编译时检查、更快并且适用于具有相同底层行为的许多类型。 在以前的数据模型类中,所有成员都具有与数据库字段相同的名称。实际上,数据模型类成员不必与数据库字段相同。例如,Recipe类的Id不必是RecipeId,它可以是任何名称,比如Id。我们需要做的是在映射过程中告诉Fluent NHibernate,如下所示。 隐藏,复制Code

Id(x => x.Id, "RecipeId");

通过这种方式,Fluent NHibernate知道它在映射“Id”到“RecipeId”。 因为我们不必使用相同的名称作为数据库字段,现在我们有机会改变不同的数据模型类,以拥有一些共同的成员。 我们创建了一个基类实体。 隐藏,复制Code

    public class Entity
    {
        public virtual Guid Id { get; set; }
        public virtual Guid? ParentId { get; set; }
        public virtual Type ParentType => null;
}

然后将Recipe, RecipeStep和RecipeItem派生Entity,将Recipe的RecipeId替换为Id,将RecipeStep的RecipeStepId替换为Id,将RecipeItem的ItemId替换为Id,将RecipeStep的RecipeId替换为ParentId,将RecipeItem的RecipeStepId替换为ParentId。 隐藏,复制Code

 public class Recipe : Entity
    {
        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 : Entity
    {
        public virtual int StepNo { get; set; }
        public virtual string Instructions { get; set; }
        public virtual IList<RecipeItem> RecipeItems { get; set; }
        public override Type ParentType => typeof(Recipe);
    }
public class RecipeItem : Entity
    {
        public virtual string Name { get; set; }
        public virtual decimal Quantity { get; set; }
        public virtual string MeasurementUnit { get; set; }
        public override Type ParentType => typeof(RecipeStep);
    }

现在我们还需要更改映射类。请注意不同名称的映射。 隐藏,收缩,复制Code

public class RecipeMap : ClassMap<Recipe>
    {
        public RecipeMap()
        {
            Id(x => x.Id, "RecipeId");
            Map(x => x.Name);
            Map(x => x.Comments);
            Map(x => x.ModifyDate);
            HasMany(x => x.Steps).KeyColumn("RecipeId").Inverse().Cascade.DeleteOrphan().OrderBy("StepNo Asc");
            Table("Recipes");
        }
}
public class RecipeStepMap : ClassMap<RecipeStep>
    {
        public RecipeStepMap()
        {
            Id(x => x.Id, "RecipeStepId");
            Map(x => x.ParentId, "RecipeId");
            Map(x => x.StepNo);
            Map(x => x.Instructions);
            HasMany(x => x.RecipeItems).KeyColumn("RecipeStepId").Inverse().Cascade.DeleteOrphan();
            Table("RecipeSteps");
        }
    }
public class RecipeItemMap : ClassMap<RecipeItem>
    {
        public RecipeItemMap()
        {
            Id(x => x.Id, "ItemId");
            Map(x => x.Name);
            Map(x => x.Quantity);
            Map(x => x.MeasurementUnit);
            Map(x => x.ParentId, "RecipeStepId");
            Table("RecipeItems");
        }
    }

“Cascade.DeleteOrphan”是什么?此选项在删除父对象时删除子对象。对于我们的示例,删除一个配方将删除该配方的所有配方步骤和配方项,删除一个步骤将删除该步骤的所有项。 然后将Repository的方法改为泛型方法,并放入泛型约束,即T必须是Entity的子类。 隐藏,收缩,复制Code

public T GetEntity<T>(Guid id) where T : Entity
        {
            try
            {
                return _session.Get<T>(id);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public T AddEntity<T>(T entity) where T : Entity
        {
            T newOne = null;
            using (var transaction = _session.BeginTransaction())
            {
                try
                {
                    _session.SaveOrUpdate(entity);
                    Commit(transaction, entity);
                    RefreshParentObject(entity);
                    newOne = _session.Get<T>(entity.Id) as T;
                }
                catch (Exception ex)
                {
                    throw ex;
                }

                return newOne;
            }
        }

        public void UpdateEntity<T>(T entity) where T : Entity
        {
            using (var transaction = _session.BeginTransaction())
            {
                try
                {
                    _session.Update(entity);
                    Commit(transaction, entity);
                    RefreshParentObject(entity);
                }
                catch (Exception ex)
                {
                    throw ex;
                }

            }
        }

        public void DeleteEntity<T>(Guid id) where T : Entity
        {
            using (var transaction = _session.BeginTransaction())
            {
                var entity = _session.Get<T>(id);
                if (entity != null)
                {
                    try
                    {
                        _session.Delete(entity);
                        Commit(transaction, entity);
                        RefreshParentObject(entity);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }
        }

对于添加、更新和删除方法,所有调用RefreshParentObject()。这是什么意思?当我们改变RecipeStep或RecipeItem时,它的父对象缓存并不知道这个改变。我们需要刷新父对象缓存。 隐藏,复制Code

void RefreshParentObject(Entity entity)
    {
        if (!entity.ParentId.HasValue)
            return;
        var parentObj = _session.Get(entity.ParentType, entity.ParentId.Value);
        if (parentObj != null)
            _session.Refresh(parentObj);
    }

现在我们更新web API控制器。 隐藏,收缩,复制Code

[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
    var recipe = _repository.GetEntity<Recipe>(id);
    if (recipe != null)
        return new ObjectResult(recipe);
    else
        return new NotFoundResult();

}
[HttpPost]
public IActionResult Post([FromBody]Recipe recipe)
{
    if (recipe.Id == Guid.Empty)
    {
        recipe.ModifyDate = DateTime.Now;
        return new ObjectResult(_repository.AddEntity<Recipe>(recipe));
    }
    else
    {
        var existingOne = _repository.GetEntity<Recipe>(recipe.Id);
        existingOne.Name = recipe.Name;
        existingOne.Comments = recipe.Comments;
        existingOne.ModifyDate = DateTime.Now;
        _repository.UpdateEntity<Recipe>(existingOne);
        return new ObjectResult(existingOne);
    }
}
[HttpPut("{id}")]
public IActionResult Put(Guid id, [FromBody]Recipe recipe)
{
    var existingOne = _repository.GetEntity<Recipe>(recipe.Id);
    existingOne.Name = recipe.Name;
    existingOne.Comments = recipe.Comments;
    _repository.UpdateEntity<Recipe>(recipe);
    return new ObjectResult(existingOne);
}

[HttpDelete("{id}")]
public IActionResult Delete(Guid id)
{
    _repository.DeleteEntity<Recipe>(id);
    return new StatusCodeResult(200);
}

角端路由 现在,我们需要在Master Chef应用程序中设置客户机路由,以便根据客户机提供的URL替换动态视图。我们可以从角度路由模块中获取角度路由特征。 使用ngRoute模块,您可以在单个页面应用程序中导航到不同的页面,而无需重新加载页面。$route用于将url深链接到控制器和视图(HTML部分)。它监视$location.url()并尝试将该路径映射到现有的路由定义。 $route中有两个依赖项,即$location和$routeParams。 1)注入ngRoute 打开app.js,在masterChefApp模块中注入ngroute。 隐藏,复制Code

(function () {
    'use strict';

    angular.module('masterChefApp', [
        // Angular modules 
        'ngRoute',

        // Custom modules 
        'recipesService'
        // 3rd Party Modules
        
    ]);
})();

2)配置Angular路由 为我们的Angular app模块定义一个配置函数——masterChefApp。并且,在该配置函数中,使用来自ngRoute模块的路由提供程序服务来定义客户端路由 隐藏,复制Code

angular.module('masterChefApp').config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
        $routeProvider
        .when('/', {
            templateUrl: 'partials/recipes.html',
            controller: 'recipesController'
        })
        .when('/recipes/add', {
            templateUrl: 'partials/add.html',
            controller: 'recipesAddController'
        })
        .when('/recipes/edit/:id', {
            templateUrl: 'partials/edit.html',
            controller: 'recipesEditController'
        })
        .when('/recipes/delete/:id', {
            templateUrl: 'partials/delete.html',
            controller: 'recipesDeleteController'
        });

        $locationProvider.html5Mode(true);

}]);

第一个只是一个默认的路由-正斜杠。第二个是/recipes/add。第三个是/recipes/edit/,并传递:id作为路由参数,它允许我们采用一个动态id,我可以匹配其中一个菜谱。最后一个route /recipes/delete/:id也需要采用动态id参数。这个默认路由只会列出所有的菜谱。“添加”路由处理添加,“编辑”路由处理编辑或更新,“删除”路由处理删除或删除。CRUD函数由这四个客户端路由表示。对于每个路由,我们需要定义一个模板URL(它表示一些应该为此路由呈现的HTML)和一个单独的控制器(它将处理此路由)。 在最底部,使用$locationProvider,它的html5Mode函数,设置为true,以确保我可以使用友好和自然的url,避免使用hash bangs进行客户端路由。 Angular JS客户端控制器 我们已经配置了默认路由、添加路由、编辑路由和删除路由。然后需要相应的控制器,recipesController、recipesAddController、recipesEditController和recipesDeleteController。我们在recipesController.js中定义了所有这些控制器。 1)注入“添加”、“编辑”和“删除”控制器 隐藏,复制Code

angular
        .module('masterChefApp')
        .controller('recipesController', recipesController)
        .controller('recipesAddController', recipesAddController)
        .controller('recipesEditController', recipesEditController)
        .controller('recipesDeleteController', recipesDeleteController);

2)实现食谱添加控制器 隐藏,复制Code

recipesAddController.$inject = ['$scope', 'Recipe', '$location'];
    function recipesAddController($scope, Recipe, $location) {
        $scope.recipe = new Recipe();
        $scope.addRecipe = function () {
            $scope.recipe.$save(function () {
                $location.path('/');
            });
        }
    }

因此,recipesAddController需要一个$作用域和食谱服务,它还需要$location服务。recipesAddController创建或提供了允许用户向应用程序添加菜谱的功能。为此,使用菜谱服务创建一个新的$scope变量recipe。它还在这里创建了一个$scope函数——addRecipe,该函数将使用recipe services保存方法向服务器提交菜谱。在提交食谱后的回调中,我们将把应用程序重定向到它的主页。 3)实现菜谱编辑控制器 隐藏,复制Code

recipesEditController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesEditController($scope, Recipe, $location, $routeParams) {
        $scope.recipe = Recipe.get({ id: $routeParams.id });
        $scope.editRecipe = function () {
            $scope.recipe.$save(function () {
                $location.path('/');
           });
        }
}

recipesEditController需要一个$作用域和菜谱服务$location服务。它还需要$routeParameter来传递id. recipesEditController创建或提供允许某人向应用程序更新菜谱的功能。我们将使用routeParams服务来更新菜谱。通过从route参数获取菜谱的ID。然后,我们将进入服务器,通过调用菜谱服务get函数获取适当的菜谱——这次是提供ID的get方法。该ID将被提供给前端。用户可以做出任何。 最后,我们将更新后的食谱记录提交给服务器。 4)实现菜谱删除控制器 隐藏,复制Code

recipesDeleteController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesDeleteController($scope, Recipe, $location, $routeParams) {
        $scope.recipe = Recipe.get({ id: $routeParams.id });
        $scope.deleteRecipe = function () {
            $scope.recipe.$remove({ id: $scope.recipe.id }, function () {
                $location.path('/');
            });
        };
}

recipesDeleteController使用$routeParams获取ID并检索特定的菜谱。然后提供这个函数deleteRecipe,在这个函数中我们可以使用菜谱服务的$remove方法告诉服务器我们想要删除一个特定的菜谱。 局部视图模板 1)修改Index.html使用ng-view 修改index.html以使用部分视图。首先添加一个“base”标签和它的href属性到/。这对于$locationProvider能够正常工作是必要的,因为它需要一个基础。现在转到正文内容。摆脱所有这些,只需使用ng-view指令。 隐藏,复制Code

<!DOCTYPEhtml>
<htmlng-app="masterChefApp">
<head>
    <basehref="/">
    <metacharset="utf-8"/>
    <title>Master Chef Recipes</title>
    <scriptsrc="lib/angular/angular.min.js"></script>
    <scriptsrc="lib/angular-resource/angular-resource.min.js"></script>
    <scriptsrc="lib/angular-route/angular-route.min.js"></script>
    <scriptsrc="app.js"></script>
    </head>
<bodyng-cloak>
    <div>
        <ng-view></ng-view>
    </div>
</body>
</html>

基于这个ng-view指令和我们已经设置的路由的使用,ng-view将能够交付正确的部分视图和正确的控制器,以在客户端路由上使用$routeProvider来为视图提供电源。 我们在app.js文件中指定了四个控制器。这些控制器给我们CRUD操作。route URL /将从服务器检索所有菜谱。/recipes/add将创建一个新菜谱。使用变量id的recipes/edit将更新现有的菜谱,而/recipes/delete也使用变量id将从服务器删除或删除特定的菜谱。 现在我们在wwwroot文件夹下创建“partials”文件夹。然后可以逐个添加模板。 2)检索模板—Recipes.html 右键点击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们给它起名叫“recipes.html”。 html,它将检索并显示菜谱列表。 隐藏,收缩,复制Code

<div>
    <h2>Master Chief Recipes</h2>
    <ul>
        <ling-repeat="recipe in recipes">
            <div>
               <h5>{{recipe.name}} - {{recipe.comments}}</h5>
            </div>
            <div>
                <ahref="recipes/edit/{{recipe.id}}">edit</a>
            </div>
            <div>
                <ahref="recipes/delete/{{recipe.id}}">delete</a>
            </div>
            <ul>
                <ling-repeat="step in recipe.steps">
                    <p> step {{step.stepNo}} : {{step.instructions}}</p>
                    <ul>
                        <ling-repeat="item in step.recipeItems">
                            <p> {{item.name}}  {{item.quantity}} {{item.measurementUnit}}</p>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
    <p><ahref="recipes/add"> Add a new recipe </a></p>
</div>

请注意,这不是完整的html。我们只是定义了一个部分视图它将在AngularJS应用中被替换。 现在如果我们运行它,我们应该看到所有的食谱。 3)引导风格 虽然它工作,但它是一个完全普通的html。所以我们需要应用一些CSS样式。 Bootstrap是一个非常流行的前端框架,它包括基于HTML和CSS的排版设计模板,表单,按钮,表格,导航,模板,图像旋转木马和许多其他的,以及可选的JavaScript插件。应用bootstrap样式可以使我们的master chef web应用程序更漂亮。 我们已经在bower配置中添加了引导包。 隐藏,复制Code

 {
	"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"
  }
}

所以bootstrap已经安装在wwwrootlib文件夹中。现在我们将它包含在index.html中。 隐藏,复制Code

<link href="lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" media="screen">

我们将应用下面的引导样式。 我们在index.html中应用主div与.container-fluid(全宽度)来进行适当的对齐和填充。 我们为ui应用所有的.list-group,为recipes.html中的li应用.list-group-item。我们还为添加链接应用“btn-primary”,为编辑链接应用“btn-default”,为删除链接应用“btn-delete”。我还想把recipe显示为一个徽章,所以也要应用。badge样式。 再跑一次大厨,看看现在是什么样子。 Bootstrap包括一个强大的移动优先网格系统,用于构建各种形状和大小的布局。它基于12列布局,有多个层次,每个媒体查询范围一个。有三个主要组件——容器、行和列。容器-。固定宽度的容器或。容器-流体的全宽度-中心你的网站内容,并帮助对齐你的网格内容。行是列的水平分组,确保列被正确地排列。列类表示您希望在每行可能的12列中使用的列数。如果你想要三个等宽的列,你可以用。cole -xs-4。 我们使用bootstrap网格系统中的主厨模板。 4)使用Angular JS实现展开/折叠 我知道有很多方法可以用jQuery展开/折叠来改变DOM。记住,我们使用的是MVVM模式。所以我热衷于通过改变控制器(视图模型)中的模型来实现展开/折叠。 在recipesController中添加expand()函数。在expand()函数中,我们设置了recipe对象的show属性。 隐藏,复制Code

recipesController.$inject = ['$scope', 'Recipe'];

    function recipesController($scope, Recipe) {
        $scope.recipes = Recipe.query();
        $scope.expand = function (recipe) {
            recipe.show = !recipe.show;
        }
}

我们在recipesController中添加了一个ng-click来调用expand()函数。 隐藏,复制Code

 <divclass="btn-group">
               <buttonclass="btn badge pull-left"ng-click="expand(recipe)"><h5>{{recipe.name}} - {{recipe.comments}}</h5></button>
</div>

然后我们使用ng-show来控制是否显示菜谱的详细信息。 隐藏,复制Code

<ul class="list-group" ng-show="recipe.show">
                <li ng-repeat="step in recipe.steps" class="list-group-item">

只需单击recipe badge扩展您想要查看的内容。 5)创建模板- add.html 右键单击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们将其命名为“add.html”。 在add.html中,使用ng-submit将数据发送到服务器。我们将通过ng-model指令将用户输入到输入字段中的信息绑定到一个范围变量菜谱。当用户按下Save按钮使用表单提交时,我们会调用作用域函数addRecipe它会在控制器中后台将recipe对象提交给服务器。 隐藏,复制Code

<h1>Add a new recipe</h1>
<divclass="container-fluid">
    <formng-submit="addRecipe()">
        <divclass="row">
            <divclass="form-group col-xs-4">
                <labelfor="name">Name</label>
                <inputng-model="recipe.name"name="name"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <divclass="form-group col-md-4 col-xs-8">
                <labelfor="comments">Comments</label>
                <inputng-model="recipe.comments"name="comments"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <buttontype="submit"class="btn btn-primary">Save</button>
            <ahref="/"class="btn btn-default">Cancel</a>
        </div>
    </form>
</div>

编辑模板- Edit .html 右键单击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们提供一个名称“edit.html”。 现在我们要更新一个食谱。我们将在edit.html部分模板中处理这个问题。edit.html看起来像add.html,因为我们需要为最终用户提供所有必要的字段,以实际更新现有菜谱。我们有recipe.name和recipe.comments的输入。它们通过ng-model指令被绑定到一个范围变量——一个对象配方。此外,在编辑控制器上有一个作用域函数——editRecipe。当用户在编辑中按下Save按钮时。html中,该函数将被调用,而将更新的菜谱信息提交到服务器进行持久存储是该函数的工作。 隐藏,复制Code

<h1>Edit recipe</h1>
<divclass="container-fluid">
    <formng-submit="editRecipe()">
        <divclass="row">
            <divclass="form-group col-xs-4">
                <labelfor="name">Name</label>
                <inputng-model="recipe.name"name="name"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <divclass="form-group col-md-4 col-xs-8">
                <labelfor="comments">Comments</label>
                <inputng-model="recipe.comments"name="comments"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <buttontype="submit"class="btn btn-primary">Save</button>
            <ahref="/"class="btn btn-default">Cancel</a>
        </div>
    </form>
</div>

7)删除模板 右键点击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们将其命名为“delete.html”。 在删除。html,我们会提供一个段落来进行确认。那么你真的想删除这个食谱吗?我们将绑定到有问题的食谱信息也就是要删除的食谱。我们将提供一个按钮,它调用一个作用域函数- deleteRecipe。它将向服务器提交一个请求,以删除特定的菜谱。 隐藏,复制Code

<div class="alert alert-warning">
    <p>Do you really want to delete this recipe?</p>
    <p> {{recipe.name}} - {{recipe.comments}}</p>
</div>
<button ng-click="deleteRecipe()" class="btn btn-danger">Yes</button>
<a href="/" class="btn btn-default">No</a>

多个URL映射到同一个Web API控制器 那食谱步骤和食谱项呢?一般来说,我们可以创建单独的API控制器来处理菜谱步骤和菜谱项。但是它太重了。我想把所有与菜谱相关的restful服务打包到RecipesController中。但是对于配方步骤操作和配方项操作,它肯定需要不同的url。幸运的是,ASP。NET Core Web API支持不同的路由。路由是Web API将URI匹配到操作的方式。Web API支持一种新的路由类型,称为属性路由。顾名思义,属性路由使用属性来定义路由。属性路由使您可以对web API中的uri进行更多的控制。例如,您可以轻松地创建描述资源层次结构的uri。 web控制器类的route属性是基URI。 隐藏,复制Code

[Route("api/[controller]")]
    public class RecipesController : Controller
{
….
}

对于RecipesController,基本URL是/api/recipes。 隐藏,复制Code

[HttpGet("{id}")]
        public IActionResult Get(Guid id)
        {
            var recipe = _repository.GetEntity<Recipe>(id);
            if (recipe != null)
                return new ObjectResult(recipe);
            else
                return new NotFoundResult();

        }

上面的方法没有route属性,这意味着这个方法被映射到/api/recipes/:id 但是对于get step方法和get item方法,我们需要不同的URL。我想获得步骤URL是/api/recipes/step/:id和项目URL是/api/recipes/item/:id。因此,我们为get step方法添加[Route("step/{id}")],为get item方法添加[Route("item/{id}")]。 隐藏,复制Code

[HttpGet]
        [Route("step/{id}")]
        public IActionResult GetStep(Guid id)
        {
            var recipeStep = _repository.GetEntity<RecipeStep>(id);
            if (recipeStep != null)
                return new ObjectResult(recipeStep);
            else
                return new NotFoundResult();

        }
[HttpGet]
        [Route("item/{id}")]
        public IActionResult GetItem(Guid id)
        {
            var recipeItem = _repository.GetEntity<RecipeItem>(id);
            if (recipeItem != null)
                return new ObjectResult(recipeItem);
            else
                return new NotFoundResult();

        }

让我们看看API路由是否可以工作。单击IIS Express启动我们的web应用程序。首先我们检查URL, api/recipes/step/AEE9602B-03EF-4A5F-A380-2962134ADB7E。 它像预期的那样工作。 然后我们检查api/recipes/item/862B91D5-FB60-4004-8179-0415AB900795 它也起作用了。 我们还需要为post和delete添加路由属性。 隐藏,收缩,复制Code

//GET api/recipes/step/:id
        [HttpGet]
        [Route("step/{id}")]
        public IActionResult GetStep(Guid id)
        {
            var recipeStep = _repository.GetEntity<RecipeStep>(id);
            if (recipeStep != null)
                return new ObjectResult(recipeStep);
            else
                return new NotFoundResult();

        }

        //POST api/recipes/step
        [HttpPost]
        [Route("step")]
        public IActionResult UpdateStep([FromBody]RecipeStep recipeStep)
        {
            if (recipeStep.Id == Guid.Empty)
            {
                return new ObjectResult(_repository.AddEntity<RecipeStep>(recipeStep));
            }
            else
            {
                var existingOne = _repository.GetEntity<RecipeStep>(recipeStep.Id);
                existingOne.StepNo = recipeStep.StepNo;
                existingOne.Instructions = recipeStep.Instructions;
                _repository.UpdateEntity<RecipeStep>(existingOne);
                return new ObjectResult(existingOne);
            }
        }

        //DELETE api/recipes/step/:id
        [HttpDelete]
        [Route("step/{id}")]
        public IActionResult DeleteStep(Guid id)
        {
            _repository.DeleteEntity<RecipeStep>(id);
            return new StatusCodeResult(200);
        }

        // GET api/recipes/item/:id
        [HttpGet]
        [Route("item/{id}")]
        public IActionResult GetItem(Guid id)
        {
            var recipeItem = _repository.GetEntity<RecipeItem>(id);
            if (recipeItem != null)
                return new ObjectResult(recipeItem);
            else
                return new NotFoundResult();

        }

        //POST api/recipes/item
        [HttpPost]
        [Route("item")]
        public IActionResult UpdateItem([FromBody]RecipeItem recipeItem)
        {
            if (recipeItem.Id == Guid.Empty)
            {
                if (recipeItem.MeasurementUnit == null)
                    recipeItem.MeasurementUnit = "";
                return new ObjectResult(_repository.AddEntity<RecipeItem>(recipeItem));
            }
            else
            {
                var existingOne = _repository.GetEntity<RecipeItem>(recipeItem.Id);
                existingOne.Name = recipeItem.Name;
                existingOne.Quantity = recipeItem.Quantity;
                existingOne.MeasurementUnit = recipeItem.MeasurementUnit;
                _repository.UpdateEntity<RecipeItem>(existingOne);
                return new ObjectResult(existingOne);
            }
        }

        //DELETE api/recipes/item/:id
        [HttpDelete]
        [Route("item/{id}")]
        public IActionResult DeleteItem(Guid id)
        {
            _repository.DeleteEntity<RecipeItem>(id);
            return new StatusCodeResult(200);
        }

单个Angular资源服务的多个路由url Angular资源服务也支持多个url。到目前为止,我们只使用默认动作。 隐藏,复制Code

{
  get: {method: 'GET'},
  save: {method: 'POST'},
  query: {method: 'GET', isArray: true},
  remove: {method: 'DELETE'},
  delete: {method: 'DELETE'}
}

以上操作都是在ng resource中构建的,所以我们可以直接使用它。 隐藏,复制Code

recipesService.factory('Recipe', ['$resource', function ($resource) {
      return $resource('/api/recipes/:id');
  }]);

但是我们现在需要定义自己的自定义操作,并使用默认URL为操作提供不同的URL。 隐藏,复制Code

recipesService.factory('Recipe', ['$resource', function ($resource) {
        return $resource('/api/recipes/:id', {}, {
            getRecipeStep: { method: 'GET', url: '/api/recipes/step/:id' },
            saveRecipeStep: { method: 'POST', url: '/api/recipes/step' },
            removeRecipeStep: { method: 'DELETE', url: '/api/recipes/step/:id' },
            getRecipeItem: { method: 'GET', url: '/api/recipes/item/:id' },
            saveRecipeItem: { method: 'POST', url: '/api/recipes/item' },
            removeRecipeItem: { method: 'DELETE', url: '/api/recipes/item/:id' }
        });
}]);

我们仍然使用recipe的默认操作,并添加新的自定义操作getRecipeStep、saveRecipeStep、removeRecipeStep、getRecipeItem、saveRecipeItem和removeRecipeItem。 所有url都匹配配方步骤和配方项的web API url。 为配方步骤和配方项添加新的角度路径 现在我们需要为app.js中的菜谱步骤创建、更新、删除和菜谱项创建、更新、删除模板和控制器添加新的客户端路由。 隐藏,收缩,复制Code

$routeProvider
  .when('/', {
      templateUrl: 'partials/recipes.html',
      controller: 'recipesController'
  })
  .when('/recipes/add', {
      templateUrl: 'partials/add.html',
      controller: 'recipesAddController'
  })
  .when('/recipes/edit/:id', {
      templateUrl: 'partials/edit.html',
      controller: 'recipesEditController'
  })
  .when('/recipes/delete/:id', {
      templateUrl: 'partials/delete.html',
      controller: 'recipesDeleteController'
  })
  .when('/recipes/addStep/:id', {
      templateUrl: 'partials/addStep.html',
      controller: 'recipesAddStepController'
  })
  .when('/recipes/editStep/:id', {
      templateUrl: 'partials/editStep.html',
      controller: 'recipesEditStepController'
  })
  .when('/recipes/deleteStep/:id', {
      templateUrl: 'partials/deleteStep.html',
      controller: 'recipesDeleteStepController'
  })
  .when('/recipes/addItem/:id', {
      templateUrl: 'partials/addItem.html',
      controller: 'recipesAddItemController'
  })
  .when('/recipes/editItem/:id', {
      templateUrl: 'partials/editItem.html',
      controller: 'recipesEditItemController'
  })
  .when('/recipes/deleteItem/:id', {
      templateUrl: 'partials/deleteItem.html',
      controller: 'recipesDeleteItemController'
  });

为配方步骤和配方项添加新的Angular控制器 在recipesController.js中注入step和item控制器。 隐藏,复制Code

angular
   .module('masterChefApp')
   .controller('recipesController', recipesController)
   .controller('recipesAddController', recipesAddController)
   .controller('recipesEditController', recipesEditController)
   .controller('recipesDeleteController', recipesDeleteController)
   .controller('recipesAddStepController', recipesAddStepController)
   .controller('recipesEditStepController', recipesEditStepController)
   .controller('recipesDeleteStepController', recipesDeleteStepController)
   .controller('recipesAddItemController', recipesAddItemController)
   .controller('recipesEditItemController', recipesEditItemController)
   .controller('recipesDeleteItemController', recipesDeleteItemController);

recipesAddStepController创建或提供了允许某人向应用程序添加菜谱步骤的功能。当我们添加配方步骤时,我们需要父配方Id。我们将通过使用routeParams服务获得要创建的配方步骤。通过从route参数获取菜谱的ID。 隐藏,复制Code

recipesAddStepController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesAddStepController($scope, Recipe, $location, $routeParams) {
        $scope.recipeStep = new Recipe();
        $scope.recipeStep.parentId = $routeParams.id;
        $scope.addRecipeStep = function () {
            $scope.recipeStep.$saveRecipeStep(function () {
                $location.path('/');
            });
        };
    }

recipesEditStepController创建或提供了允许某人将配方步骤更新到应用程序的功能。我们将使用routeParams服务来更新菜谱步骤。通过从route参数获取菜谱步骤的ID。 隐藏,复制Code

recipesEditStepController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
function recipesEditStepController($scope, Recipe, $location, $routeParams) {
    $scope.recipeStep = Recipe.getRecipeStep({ id: $routeParams.id });
    $scope.editRecipeStep = function () {
        $scope.recipeStep.$saveRecipeStep(function () {
            $location.path('/');
        });
    };
}

recipesDeleteStepController使用$routeParams获取ID并检索特定的菜谱步骤。然后将此函数的删除步骤提供给应用程序。 隐藏,复制Code

    recipesDeleteStepController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesDeleteStepController($scope, Recipe, $location, $routeParams) {
        $scope.recipeStep = Recipe.getRecipeStep({ id: $routeParams.id });
        $scope.deleteRecipeStep = function () {
            $scope.recipeStep.$removeRecipeStep({ id: $scope.recipeStep.id }, function () {
                $location.path('/');
            });
        };
}

recipesAddItemController创建或提供了允许用户向应用程序添加菜谱项的功能。当我们添加菜谱项时,我们需要父菜谱步骤Id。我们将通过使用routeParams服务获得要创建的菜谱项。通过从route参数获取菜谱步骤的ID。 隐藏,复制Code

recipesAddItemController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesAddItemController($scope, Recipe, $location, $routeParams) {
        $scope.recipeItem = new Recipe();
        $scope.recipeItem.parentId = $routeParams.id;
        $scope.addRecipeItem = function () {
            $scope.recipeItem.$saveRecipeItem(function () {
                $location.path('/');
            });
        };
}

recipesEditItemController创建或提供了允许用户将菜谱项更新到应用程序的功能。我们将使用routeParams服务来更新菜谱项。通过从route参数获取菜谱项的ID。 隐藏,复制Code

recipesEditItemController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
function recipesEditItemController($scope, Recipe, $location, $routeParams) {
    $scope.recipeItem = Recipe.getRecipeItem({ id: $routeParams.id });
    $scope.editRecipeItem = function () {
        $scope.recipeItem.$saveRecipeItem(function () {
            $location.path('/');
        });
    };
}

recipesDeleteItemController使用$routeParams获取ID并检索特定的菜谱项。然后提供此函数,将菜谱项删除到应用程序。 隐藏,复制Code

    recipesDeleteItemController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesDeleteItemController($scope, Recipe, $location, $routeParams) {
        $scope.recipeItem = Recipe.getRecipeItem({ id: $routeParams.id });
        $scope.deleteRecipeItem = function () {
            $scope.recipeItem.$removeRecipeItem({ id: $scope.recipeItem.id }, function () {
                $location.path('/');
            });
        };
}

添加配方步骤和配方项的所有模板 现在我们需要为配方步骤和配方项创建所有模板。创建“addStep。html”、“editStep。html”、“deleteStep.html”、“addItem。html”、“editItem。html”和“deleteItem。在partials文件夹中。 1)配方步骤模板 在addStep。html,使用ng-submit发送数据到服务器。当用户按下Save按钮时,调用一个作用域函数addRecipeStep,该函数在控制器的后台将向服务器提交这个配方步骤对象。 隐藏,复制Code

<h1>Add a new recipe step</h1>
<divclass="container-fluid">
    <formng-submit="addRecipeStep()">
        <divclass="row">
            <divclass="form-group col-xs-1">
                <labelfor="stepNo">Step No.</label>
                <inputng-model="recipeStep.stepNo"name="stepNo"type="text"class="form-control"/>
            </div>
        </div>

        <divclass="row">
            <divclass="form-group col-md-4 col-xs-8">
                <labelfor="instructions">Instructions</label>
                <inputng-model="recipeStep.instructions"name="instructions"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <buttontype="submit"class="btn btn-primary">Save</button>
            <ahref="/"class="btn btn-default">Cancel</a>
        </div>
    </form>
</div>

html更新现有的配方步骤。使用ng-model指令将输入字段绑定到一个范围变量——一个对象recipeStep。此外,在step编辑控制器上,有一个作用域函数- editRecipeStep。 隐藏,复制Code

<h1>Edit Recipe Step</h1>
<divclass="container-fluid">
    <formng-submit="editRecipeStep()">
        <divclass="row">
            <divclass="form-group col-xs-1">
                <labelfor="stepNo">Step No.</label>
                <inputng-model="recipeStep.stepNo"name="stepNo"type="text"class="form-control"/>
            </div>
        </div>

        <divclass="row">
            <divclass="form-group col-md-4 col-xs-8">
                <labelfor="instructions">Instructions</label>
                <inputng-model="recipeStep.instructions"name="instructions"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <buttontype="submit"class="btn btn-primary">Save</button>
            <ahref="/"class="btn btn-default">Cancel</a>
        </div>
    </form>
</div>

在deleteStep。html,我们会提供一个段落来进行确认。我们将提供一个按钮,它调用一个作用域函数- deleteRecipeStep。它将向服务器提交一个请求,以删除特定的配方步骤。 隐藏,复制Code

<div class="alert alert-warning">
    <p>Do you really want to delete this recipe step?</p>
    <p> {{recipeStep.stepNo}} - {{recipeStep.instructions}}</p>
</div>
<button ng-click="deleteRecipeStep()" class="btn btn-danger">Yes</button>
<a href="/" class="btn btn-default">No</a>

2)配方物品模板 在addItem。html,使用ng-submit发送数据到服务器。当用户按下Save按钮时,调用一个作用域函数addRecipeItem,该函数在控制器的后台将向服务器提交这个recipe item对象。 隐藏,收缩,复制Code

<h1>Add a new recipe item</h1>
<divclass="container-fluid">
    <formng-submit="addRecipeItem()">
        <divclass="row">
            <divclass="form-group col-xs-4">
                <labelfor="name">Name</label>
                <inputng-model="recipeItem.name"name="name"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <divclass="form-group col-md-4 col-xs-4">
                <labelfor="quantity">Quantity</label>
                <inputng-model="recipeItem.quantity"name="quantity"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <divclass="form-group col-md-4 col-xs-4">
                <labelfor="measurementUnit">Measurement Unit</label>
                <inputng-model="recipeItem.measurementUnit"name="measurementUnit"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <buttontype="submit"class="btn btn-primary">Save</button>
            <ahref="/"class="btn btn-default">Cancel</a>
        </div>
    </form>
</div>

html更新现有的菜谱项。使用ng-model指令将输入字段绑定到一个范围变量——一个对象recipeItem。此外,在项目编辑控制器上,有一个作用域函数- editRecipeItem。 隐藏,收缩,复制Code

<h1>Edit Recipe Item</h1>
<divclass="container-fluid">
    <formng-submit="editRecipeItem()">
        <divclass="row">
            <divclass="form-group col-xs-4">
                <labelfor="name">Name</label>
                <inputng-model="recipeItem.name"name="name"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <divclass="form-group col-md-4 col-xs-4">
                <labelfor="quantity"></label>
                <inputng-model="recipeItem.quantity"name="quantity"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <divclass="form-group col-md-4 col-xs-4">
                <labelfor="measurementUnit"></label>
                <inputng-model="recipeItem.measurementUnit"name="measurementUnit"type="text"class="form-control"/>
            </div>
        </div>
        <divclass="row">
            <buttontype="submit"class="btn btn-primary">Save</button>
            <ahref="/"class="btn btn-default">Cancel</a>
        </div>
    </form>
</div>

在deleteItem。html,我们会提供一个段落来进行确认。我们将提供一个按钮,它调用作用域函数deleteRecipeItem。它将向服务器提交一个请求,以删除特定的菜谱项。 隐藏,复制Code

<div class="alert alert-warning">
    <p>Do you really want to delete this recipe item?</p>
    <p> {{recipeItem.name}}  {{recipeItem.quantity}} {{recipeItem.measurementUnit}}</p>
</div>
<button ng-click="deleteRecipeItem()" class="btn btn-danger">Yes</button>
<a href="/" class="btn btn-default">No</a>

一切都完成了。现在您可以创建、更新或删除菜谱了。你会成为一个真正的大厨。不仅仅是一个只遵循别人食谱的厨师。 IE缓存问题 最后,我想谈谈发生在IE上的一个缓存问题。如果我们把IIS Express改成IE,在我添加了一个新菜谱“roast Duck”之后,你不能马上看到我刚刚添加的新菜谱。它没有正确地插入吗?去数据库查一下,新食谱就在那里。看起来当返回到list时,AngularJS根本没有发送httpget请求到服务器,只是从缓存中获取结果。这就是为什么新的更新不会弹出。我们可以资源通过httpProvider解决这个问题。在AngularJS应用程序配置函数中注入httpProvider。然后将http默认缓存设置为false,并将http get请求头中的If-Modified-Since设置为0。 隐藏,收缩,复制Code

angular.module('masterChefApp').config(['$routeProvider', '$httpProvider', '$locationProvider', function ($routeProvider, $httpProvider, $locationProvider) {
        //disable http cache
        $httpProvider.defaults.cache = false;
        if (!$httpProvider.defaults.headers.get) {
            $httpProvider.defaults.headers.get = {};
        }

        $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
        //////////////////////////////////////////////////////////////////

        $routeProvider
        .when('/', {
            templateUrl: 'partials/recipes.html',
            controller: 'recipesController'
        })
        .when('/recipes/add', {
            templateUrl: 'partials/add.html',
            controller: 'recipesAddController'
        })
        .when('/recipes/edit/:id', {
            templateUrl: 'partials/edit.html',
            controller: 'recipesEditController'
        })
        .when('/recipes/delete/:id', {
            templateUrl: 'partials/delete.html',
            controller: 'recipesDeleteController'
        })
        .when('/recipes/addStep/:id', {
            templateUrl: 'partials/addStep.html',
            controller: 'recipesAddStepController'
        })
        .when('/recipes/editStep/:id', {
            templateUrl: 'partials/editStep.html',
            controller: 'recipesEditStepController'
        })
        .when('/recipes/deleteStep/:id', {
            templateUrl: 'partials/deleteStep.html',
            controller: 'recipesDeleteStepController'
        })
        .when('/recipes/addItem/:id', {
            templateUrl: 'partials/addItem.html',
            controller: 'recipesAddItemController'
        })
        .when('/recipes/editItem/:id', {
            templateUrl: 'partials/editItem.html',
            controller: 'recipesEditItemController'
        })
        .when('/recipes/deleteItem/:id', {
            templateUrl: 'partials/deleteItem.html',
            controller: 'recipesDeleteItemController'
        });

        $locationProvider.html5Mode(true);

    }]);

然后我们再试一次。它像一个魅力。虽然我没有这个缓存问题在谷歌Chrome,我们仍然需要修复这个问题在IE,因为web应用程序应该工作在所有的浏览器。 结论 在本文中,我介绍了如何使用angular route创建SPA CRUD应用程序。我们还讨论了如何在单个服务器端Web API控制器中映射多个url。以及相应的,如何在单个客户端angular资源服务中映射不同的路由。从Maser Chef part 3开始,我们将在Angular2和EntityFramework Core上开始一个新的冒险。 本文转载于:http://www.diyabc.com/frontweb/news17322.html

原文地址:https://www.cnblogs.com/Dincat/p/13494085.html