zoukankan      html  css  js  c++  java
  • Web API 之CRUD

    注:例子来自微软Web API官网(https://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-1),也有加入自己的一点体会。

    一、创建一个MVC项目,选择Web API类型的MVC,命名为BookService:

    二、创建Mode和Controller:

    在Models文件夹下面创建两个Model,分别是Author.cs和Book.cs;

      1)、其中Author的定义如下:

        

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace BookService.Models
    {
        public class Author
        {
            public int Id { get; set; }
            [Required]
            public string Name { get; set; }
        }
    }

      2)、Book的定义如下:

      

    using System.ComponentModel.DataAnnotations;
    
    namespace BookService.Models
    {
        public class Book
        {
            public int Id { get; set; }
            [Required]
            public string Title { get; set; }
            public int Year { get; set; }
            public decimal Price { get; set; }
            public string Genre { get; set; }
    
            // Foreign Key
            public int AuthorId { get; set; }
            // Navigation property
            public Author Author { get; set; }
        }
    }

    3)、插入小知识点----POCO:

      (1)、概念:所谓的POCO就是 pure old C# object 的缩写,翻译过来就是“纯洁老式的C#对象”,不过太拗口了,所以现在更多的称它为“简单C#对象”。

          含义:POCO的内在含义是指一个类不曾继承或者实现任何类和接口,也没有被其它框架侵入的C#对象。就是最原生态的C#对象,我的理解是它是一个“光棍”。

      (2)、意义:简单、灵活、易扩展。我们定义的POCO在后面使用的用于逻辑层的DTO(Data Transfer Object)以及用于UI显示的VO(View Object)都是以此为基础的。

    三、添加API Controller,在控制器的选择当中选择 “包含操作的Web API(使用EF)”,这样子它会帮我们生成增删改查的操作。

    选择之后会蹦出如下弹出框要求对控制器进行设置:

     

     设置的话就按照上面要求的来做就行。下面进行解释:

    A、模型类的选择:下拉选择,是为哪一个Model创建控制器就选择哪一个;

    B、数据上下文,这个是用来创建Context文件的,这个文件是Model与数据库交互的一个门户,是对数据操作的一个集合,所有的Model都在这个类里面进行管理,我理解成是中介。添加的方式就是点击右侧的“+”然后再弹出框中点击确定就OK了。就是选择默认的名字就好了。

    C、 选择异步的控制器操作,异步操作方法通常用于长时间运行的、非 CPU 绑定的请求操作方法,在这个小例子当中实际上并不是必须的。(我对这个其实还不懂,今后会好好去搞明白,索性这个异步在目前对于例子也不重要,所以可以先不用管)

    D、控制器名称,这个建议是使用默认的,这个命名是很规范的,使用了REST API的设计原则,对于资源命名使用的是名词,而且是复数形式Books而不是单数形式Book,很规范。

    点击 “添加” 之后会生成两个文件BooksController.cs控制器(在controller文件夹中)和BookServiceContext.cs上下文文件(在Models文件夹中)。

    注:如果是跟着上面的步骤做的话其实是无法创建控制器的,因为EF是ORM框架,是根据反射进行创建的,也就是说它是需要程序集的,因此在创建使用EF的控制器之前需要进行编译,之后才可以创建成功。在这里才进行说明是个人觉得犯错才记得牢(*_*).

     BooksController.cs文件主要包含对Book.cs实体类的CRUD操作,而BookServiceContext.cs包含了对Book.cs实体的注册。打开的话就可以看到。

    接下来该做的就是按照上面的步骤创建Author.cs实体的控制器。

    三、创建数据库,完成EF的映射:

    1、添加Migration:生成一下-->在VS中选择工具-->NuGet包管理器-->Nuget程序管理控制台--->输入命令:Enable-Migrations

      效果: 在项目之下创建一个文件夹Migration,里面有一个配置文件Configuration.cs。

    2.打开Configuration.cs文件,添加代码(用途是初始化数据库当中的数据),代码如下:

    protected override void Seed(BookService.Models.BookServiceContext context)
    {
        context.Authors.AddOrUpdate(x => x.Id,
            new Author() { Id = 1, Name = "Jane Austen" },
            new Author() { Id = 2, Name = "Charles Dickens" },
            new Author() { Id = 3, Name = "Miguel de Cervantes" }
            );
    
        context.Books.AddOrUpdate(x => x.Id,
            new Book() { Id = 1, Title = "Pride and Prejudice", Year = 1813, AuthorId = 1, 
                Price = 9.99M, Genre = "Comedy of manners" },
            new Book() { Id = 2, Title = "Northanger Abbey", Year = 1817, AuthorId = 1, 
                Price = 12.95M, Genre = "Gothic parody" },
            new Book() { Id = 3, Title = "David Copperfield", Year = 1850, AuthorId = 2, 
                Price = 15, Genre = "Bildungsroman" },
            new Book() { Id = 4, Title = "Don Quixote", Year = 1617, AuthorId = 3, 
                Price = 8.95M, Genre = "Picaresque" }
            );
    }

    seed方法是用于为数据库设置初始值的,一般在开发项目的时候我们不使用这个方法,这个例子只是为了为数据库添加数据才使用的。

    3、继续在Nuget程序包控制台输入指令:

    Add-Migration Initial
    Update-Database
    分析:
    Add-Migration Initial:生成文件

    Update-Database:这个方法会创建数据库,当Model改动的时候,需要再次运行上面两个指令进行数据库的更新操作(可以想见,这个就是使用了Migration的好处:不需要再
    重新创
    建EF,不会丢失数据)。

    注:在这一步的时候我出了一个错误:根本无法创建数据库成功,后来找到原因,因为我使用的是VS自带的LocalDB,生成的实例名是Data Source=(localdb)MSSQLLocalDB
    (在web.config中),这个实例名是SQL Express2014的,而我安装的是SQl Express 2012,这样子的话就会出现问题,需要把实例名改成Data Source=(localdb)v11.0,
    那么问题就解决了。个人感觉这是一个问题,微软的VS无法检测到我们安装的是哪一个版本的SQL Exepress.

    让我们来看看数据库:

    4、创建DTO(Data Transfer Object):
    先来说说DTO的优点:
    1)、克服对数据库的重复调用;
    2)、隐藏不想让用户知道的数据库表规则,即隐藏一些字段,防止数据库规则泄露;
    3)、减少一些属性(字段)的调用降低服务器负荷;
    4)、减少数据库中表的对象嵌套,即方便用户使用数据;
    5)、避免over posting(不懂这个怎么翻译呀);
    6)、解耦合(数据层和业务逻辑层之间解耦)。

    四、修改自动生成的BooksController中的GetBooks()、GetBook(int id)和PostBook()方法。
    // GET api/Books
    public IQueryable<BookDTO> GetBooks()
    {
        var books = from b in db.Books
                    select new BookDTO()
                    {
                        Id = b.Id,
                        Title = b.Title,
                        AuthorName = b.Author.Name
                    };
    
        return books;
    }
    
    // GET api/Books/5
    [ResponseType(typeof(BookDetailDTO))]
    public async Task<IHttpActionResult> GetBook(int id)
    {
        var book = await db.Books.Include(b => b.Author).Select(b =>
            new BookDetailDTO()
            {
                Id = b.Id,
                Title = b.Title,
                Year = b.Year,
                Price = b.Price,
                AuthorName = b.Author.Name,
                Genre = b.Genre
            }).SingleOrDefaultAsync(b => b.Id == id);
        if (book == null)
        {
            return NotFound();
        }
    
        return Ok(book);
    } 
    
    [ResponseType(typeof(Book))]
    public async Task<IHttpActionResult> PostBook(Book book)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        db.Books.Add(book);
        await db.SaveChangesAsync();
    
        // New code:
        // Load author name
        db.Entry(book).Reference(x => x.Author).Load();
    
        var dto = new BookDTO()
        {
            Id = book.Id,
            Title = book.Title,
            AuthorName = book.Author.Name
        };
    
        return CreatedAtRoute("DefaultApi", new { id = book.Id }, dto);
    }
    
    

    五、创建UI和写Script脚本,显示数据:

    注:这一步和官网的是不一样的,如果看官想看的话可以自行到官网查看:https://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-6

    官网使用的是Knockout.js。公司这边已经不使用Knockout.js,所以我是使用angular.js来写的。

    1、先上HTML代码,把View文件夹下的子文件夹下面的Home中的index替换成如下代码(要自己引用angular.js):

    <style type="text/css">
        h1,h2,h3,html,div{
            margin:0;
            padding:0;
    
        }
        #books {
            border: 2px solid #000000;
            padding: 20px;
            width: 350px;
            font-size: 18px;
            margin:10px;
            float:left;
        }
        #addBlock{
            border: 2px solid #000000;
            padding: 20px;
            width: 350px;
            font-size: 18px;
            margin:10px;
            float:left;
        }
    
        #details{
            border: 2px solid #000000;
            padding: 20px;
            width: 350px;
            font-size: 18px;
            margin:10px;
            float:left;
        }
        #edit{
            border: 2px solid #000000;
            padding: 20px;
            width: 350px;
            font-size: 18px;
            margin:10px;
            clear:both;
        }
        /*div{float:left;
            margin:20px;
        }*/
    </style>
    
    <div ng-app="First" ng-controller="firstController">
    
        <!--base message-->
        <div id="books">
            <h1>Books</h1>
            <p ng-repeat="x in baseMessage"> <label ng-bind="x.Title+': '+x.AuthorName"></label>
                <a href="#" ng-click="detail(x.Id)">Details</a>
                <a href="#" ng-click="delete(x.Id)">Delete</a>
            </p>
        </div>
    
        <!--detail message-->
        <div id="details">
            <h1>Details</h1>
            <label>Author</label> <label>{{": "+Author}}</label><br />
            <label>Title</label> <label>{{": "+Title}}</label><br />
            <label>Year</label> <label>{{": "+Year}}</label><br />
            <label>Genre</label> <label>{{": "+Genre}}</label><br />
            <label>Price</label> <label>{{": "+Price}}</label><br />
        </div>
        <!--add block-->
        <div id="addBlock">
            <h1>Add Book</h1>
            <label>Author</label><select id="Author" ng-options="item as item.AuthorName for item in baseMessage" ng-model="selectedAuthor"></select><br />
            @*@Html.DropDownList("Author", new List<SelectListItem>(), new { ng_options = "item as item.AuthorName for item in baseMessage", ng_model = "selected" });*@
            <!--Why???????-->
            <label>Title</label><input type="text" id="Title" /> <br />
            <label>Year</label> <input type="text" id="Year" /><br />
            <label>Genre</label> <input type="text" id="Genre" /><br />
            <label>Price</label> <input type="text" id="Price" /><br />
            <input type="button" value="Add" ng-click="AddBook(selectedAuthor)"/>
        </div>
        <hr />
        <!--Edit block-->
        <div id="edit">
            <h1>Modify Books</h1>
            <label>Book:</label><select id="Author" ng-options="item as item.Title for item in baseMessage" ng-model="selectedBook"></select><br />
            <label>Title:</label><input type="text" id="titleModify" /> <br />
            <label>Year:</label> <input type="text" id="yearModify" /><br />
            <label>Genre:</label> <input type="text" id="genreModify" /><br />
            <label>Price:</label> <input type="text" id="priceModify" /><br />
            <input type="button" value="Modify" ng-click="ModifyBook(selectedBook)" />
        </div>
    </div>

    2、新建一个js文件,引用到index,js文件写的是angular代码,如下:

    var app = angular.module("First", []);
    app.controller("firstController", function ($scope, $http) {
        var message = new Array();
        var selectAuthor = new Array();
        $http({
            method: "GET",
            url: "/api/Books/"
        }).then(function (data) {
            angular.forEach(data.data, function (value, key) {
                message.push(value);
                selectAuthor.push(value.AuthorName);
            })
            $scope.baseMessage = message;
            $scope.selectAuthors = selectAuthor;
            $scope.selected = $scope.baseMessage[0];
        })
    
        $scope.detail = function detail(id) {
            $http.get("/api/Books/" + id).then(function (data) {
                $scope.Author = data.data.AuthorName;
                $scope.Title = data.data.Title;
                $scope.Year = data.data.Year;
                $scope.Genre = data.data.Genre;
                $scope.Price = data.data.Price;
            });
        }
    
        $scope.delete = function (id) {
            $http.delete("api/Books/" + id).then(function () {
                alert("OK,You have already delete it!");
            })
        }
    
        $scope.AddBook = function (selectedAuthor) {
            //alert(selected.Id);
            var Title = $("#Title").val();
            var Year = $("#Year").val();
            var Genre = $("#Genre").val();
            var Price = $("#Price").val();
            var book = {
                AuthorId: parseInt(selectedAuthor.Id),
                Title: Title,
                Year: parseInt(Year),
                Genre: Genre,
                Price: Price
            }
            $http.post(
                "/api/Books/", book).success(function () {
                    alert("Add Sucess!");
                })
        }
    
        $scope.ModifyBook = function (selectedModify) {
            var id = selectedModify.Id;
            var AuthorId = 1;
            authorId(id);
            function authorId(id) {
                $http.get("/api/Books/" + id).then(function (data) {
                    AuthorId = data.data.AuthorId;
                 
                    var Title = $("#titleModify").val();
                    var Year = $("#yearModify").val();
                    var Genre = $("#genreModify").val();
                    var Price = $("#priceModify").val();
                    var book = {
                        AuthorId: AuthorId,
                        Id: parseInt(selectedModify.Id),
                        Title: Title,
                        Year: parseInt(Year),
                        Genre: Genre,
                        Price: Price
                    }
                    //$http.put("/api/Books/" + selectedModify.Id, book,function () {
                    //    alert("OK,You have already modify it (*_*) ~");
                    //});
                    $http({ method: "PUT", url: "/api/Books/" + selectedModify.Id, data: { "id": selectedModify.Id, "book": book } }).then(function () {
                        alert("OK,You have already modify it (*_*) ~");
                    });
    
                });
            }
        }
    });

    3、生成解决方案然后运行就会有结果了。

    注:博主有强迫症,一个博文不喜欢写太长,所以对于angular代码的解析就放在其他博文里面http://www.cnblogs.com/heisehenbai/p/Angular.html

     





  • 相关阅读:
    Log4J输出日志到WEB工程目录的实现方法
    MyEclipse 10 中增加svn插件
    Web 项目添加log4j
    ruby on rails 之旅 第一章 ubuntu11.10安装
    ruby on rails 之旅 第一章 ubuntu12.04安装
    centos 6.3 server 安装mysql5
    技术实践第四期|解读移动开发者日常性能监控平台应用
    电脑一族预防颈椎劳损八法
    方便的使用单击和双击更新DataGrid中的数据的例子(转载)
    不重复随机数列生成算法
  • 原文地址:https://www.cnblogs.com/heisehenbai/p/WebAPIANDEF.html
Copyright © 2011-2022 走看看