1.新建WEB API项目
2.在Models文件夹中新建2个类
Author.cs
using System.ComponentModel.DataAnnotations;
namespace UsingWebAPIWithEF6.Models
{
public class Author
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
}
Book.cs
using System.ComponentModel.DataAnnotations;
namespace UsingWebAPIWithEF6.Models
{
public class Book
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
public int Year { get; set; }
public int Price { get; set; }
public int Genre { get; set; }
//Foreign Key
public int AuthorId { get; set; }
public Author Author { get; set; }
}
}
EF会遍历模型并且生成对应的数据表,属性字段ID将会设置被设置为表的主键
3.添加WEB API控制器
选择Controllers 右键 添加控制器,然后选择 添加
勾选异步控制器,增加并发使用量。
可以看到生成的代码使用了新的语法特性
// GET: api/Authors/5
[ResponseType(typeof(Author))]
public async Task<IHttpActionResult> GetAuthor(int id)
{
Author author = await db.Authors.FindAsync(id);
if (author == null)
{
return NotFound();
}
return Ok(author);
}
4,使用Code First创建数据库
VS中选择 工具 包管理器 程序包管理控制台
执行以下命令
Enable-Migrations
命令执行完以后生成了以下文件和文件夹
打开该文件,重写Seed方法,增加一些模拟数据(需要引入Models命名空间)
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" }
);
}
接着在包管理器控制台中执行以下命令
Add-Migration Initial
Update-Database
F5运行,因为项目中存在WEB API Help Page 所以会有一个API导航可以查看到所有API,这里也可以自己另外接入其他API测试框架。(例如Swagger-UI 比较美观 )
5.查看数据库
VS选择视图 SQLSERVER对象资源管理器(以下为数据库表结构)
6.跟踪EF的数据执行过程,查看生成的SQL语句
打开DbContext文件,在base方法中添加以下代码。
public UsingWebAPIWithEF6Context() : base("name=UsingWebAPIWithEF6Context")
{
//New Code
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}
请求接口
可以看到Author=null,这是因为相关的实体没有被加载到。
以下是生成的SQL语句
可以看到此处没有引用Author表,只是做了Book表的信息查询。
7.了解EF的三种数据加载方式,解决以上问题。
EF的三种数据加载模式分别是:
eager loading, lazy loading, and explicit loading(此处我只使用 eager loading)
Eager Loading
EF加载相关实体作为初始数据库查询的一部分,要使用Eager Loading需要使用System.Data.Entity.Include扩展方法
如下
public IQueryable<Book> GetBooks()
{
return db.Books.Include(a => a.Author);
}
Include可以告诉EF包含Author数据进来,重新运行程序访问接口,可以看到返回的JSON如下
以下为生成的SQL语句
至此WEB API已经可以正常返回数据,但是返回的JSON结果直接映射了我们的数据库表结构,多数情况下这并不一个很好的方式,有时候我们需要删除不必要显示的某些字段这时候需要我们重新定义相应的数据传输类。
创建数据传输对象(DTO)
新建类库 并且增加2个DTO类
如下
namespace DTO
{
public class BootDto
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
}
}
namespace DTO
{
public class BookDetailDto
{
public int Id { get; set; }
public string Title { get; set; }
public int Year { get; set; }
public decimal Price { get; set; }
public string AuthorName { get; set; }
public string Genre { get; set; }
}
}
下来修改Book控制器中的get方法
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;
}
此处,我增加了一个TDO类进行数据的传输
namespace DTO
{
public class BookDto
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
}
}
因为使用了DTO所以,在此对查询方法进行了修改,如下
public async Task<IHttpActionResult> GetBooks()
{
var books = await db.Books.Include(b=> b.Author).ToListAsync();
//添加映射关系
Mapper.Initialize(i => i.CreateMap<Book,BookDto>());
var bookMap =Mapper.Map<List<Book>, List<BookDto>>(books);
if (bookMap == null)
{
return NotFound();
}
return Ok(bookMap);
}
// 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);
}
接口测试结果
图书列表
图书详细信息
提交方法
// POST: api/Books
[ResponseType(typeof(Book))]
public async Task<IHttpActionResult> PostBook(Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Books.Add(book);
await db.SaveChangesAsync();
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);
}
这里我们是手动进行DTO转换的,所以使用AutoMapper来进行自动映射。
在包管理控制台中执行安装
Install-Package AutoMapper
安装完成以后,就可以修改替代代码了,如下
// GET: api/Books/5
[ResponseType(typeof(BookDetailDto))]
public async Task<IHttpActionResult> GetBook(int id)
{
var book =await db.Books.Include(b => b.Author).SingleOrDefaultAsync(b=> b.Id==id);
Mapper.Initialize(i => i.CreateMap<Book, BookDto>());
Mapper.Map<BookDto>(book);
if (book == null)
{
return NotFound();
}
return Ok(book);
}
测试如下
8.设计界面进行数据展示(稍后更新 时间关系)