• 厨师长(第二部分)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

  • 相关阅读:
    杭州电acm理工大舞台版
    String.Split()功能
    android layout物业介绍
    讨厌OpenSSL
    Android Bundle类别
    多项式回归
    如何直接串行电缆以及空调制解调器串行电缆之间的区别?
    为什么写科技博客是情侣如此重要?
    SSH框架总结(帧分析+环境结构+示例源代码下载)
    RapidXml用法
  • 原文地址:https://www.cnblogs.com/Dincat/p/13494085.html
Copyright © 2020-2023  润新知