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

 





原文地址:https://www.cnblogs.com/heisehenbai/p/WebAPIANDEF.html