ASP.NET Web API教程:创建域模型的方法详细介绍

1.
在ASP.NET Web API开发中,创建域模型是构建应用程序的核心步骤之一,域模型不仅定义了数据的结构和行为,还决定了应用程序与数据存储之间的交互方式,本文将详细介绍三种主要的Entity Framework(EF)集成策略,并重点讲解代码先行(Code-first)方法,包括其优势、实现步骤和注意事项。
2. Entity Framework集成策略
2.1 数据库先行(Database-first)
数据库先行是一种以现有数据库为基础的开发方法,开发者首先创建或提供一个数据库结构,然后使用Entity Framework根据这个数据库结构自动生成相应的实体类和访问层代码,这种方法适用于已有成熟数据库的项目迁移,或者需要强依赖于现有数据模型的情况。
优点
利用现有数据库结构,减少开发工作量。
适合对现有数据库进行扩展或维护。
缺点
灵活性较差,难以适应需求变化。
不利于团队协作和版本控制。
2.2 模型先行(Model-first)
模型先行模式下,开发者先设计一个可视化的模型,如使用Visual Studio的EDM Designer(Entity Data Model Designer),在此基础上,EF会同时生成数据库结构和访问代码,这种方式更注重概念模型的建立,适合在设计阶段就明确数据结构和业务逻辑。
优点
直观的设计方式,便于理解和维护。
同时生成数据库和代码,提高开发效率。
缺点
需要额外的工具支持,如EDM Designer。
模型变更后,可能需要手动调整数据库结构。
3 代码先行(Code-first)
代码先行方法是从代码层面开始,先定义域对象(Plain Old CLR Objects, POCOs),这些对象是简单的.NET对象,无需继承特定的框架类,使用代码先行,开发者可以更好地控制数据访问逻辑,灵活地定义属性的验证规则,以及通过数据注解来定制数据库表结构,POCO对象的设计简洁,易于理解和维护。
优点
灵活性高,适应各种复杂需求。
更好的代码组织和可维护性。
易于与自动化测试和持续集成工具集成。
缺点
初始开发工作量较大,需要编写更多的代码。
对开发者的要求较高,需要深入理解EF和数据库设计。
3. 代码先行方法详解
1 定义POCO类
在代码先行方法中,首先需要定义POCO类,以下是一个简单的示例,展示了如何定义一个Product类和一个Order类。
namespace ProductStore.Models
{
using System.ComponentModel.DataAnnotations;
public class Product
{
[ScaffoldColumn(false)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public decimal Price { get; set; }
public decimal ActualCost { get; set; }
}
}
namespace ProductStore.Models
{
public class Order
{
[ScaffoldColumn(false)]
public int Id { get; set; }
public int ProductId { get; set; }
public decimal TotalAmount { get; set; }
public virtual Product Product { get; set; }
}
}
关键点解释

[ScaffoldColumn(false)]:告诉ASP.NET MVC在生成编辑器表单时跳过该属性。
[Required]:用于验证模型,指定属性不能为空。
virtual关键字:启用延迟加载,允许在需要时才加载关联的实体。
2 配置数据上下文
定义好POCO类后,接下来需要配置数据上下文,数据上下文负责与数据库进行交互,并管理POCO类与数据库表之间的映射关系。
using System.Data.Entity;
namespace ProductStore.Models
{
public class ProductStoreContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 在这里可以进行更详细的配置,如关系映射等
}
}
}
关键点解释
DbContext:EF的核心类,表示数据库上下文。
DbSet<T>:表示一个泛型***,对应数据库中的一张表。
OnModelCreating方法:用于配置模型创建时的额外设置,如外键约束、索引等。
3 使用数据注解定制数据库表结构
通过数据注解,可以在POCO类上直接指定数据库表的结构,如列名、数据类型、长度限制等,这有助于确保数据库表结构与业务需求一致。
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ProductStore.Models
{
[Table("Product")]
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Column(TypeName = "decimal(10, 2)")]
public decimal Price { get; set; }
public decimal ActualCost { get; set; }
}
}
关键点解释
[Table("Product")]:指定对应的数据库表名。
[Key]:指定主键字段。
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]:指定主键自增。
[StringLength(100)]:限制字符串最大长度。
[Column(TypeName = "decimal(10, 2)")]:指定列的数据类型和精度。
4 迁移数据库
在使用代码先行方法时,通常需要使用EF的迁移功能来同步数据库结构,迁移功能允许开发者通过代码来管理数据库的版本和变更。
Enable-Migrations Add-Migration Init Update-Database
关键点解释
Enable-Migrations:启用迁移功能。
Add-Migration Init:添加初始迁移。
Update-Database:应用迁移到数据库。
5 实现数据访问层和服务层
为了更好地组织代码,通常会将数据访问逻辑封装在数据访问层(Repository)和服务层(Service)中,这样可以使业务逻辑与数据访问逻辑分离,增强代码的可维护性和可测试性。
namespace ProductStore.Repositories
{
public class ProductRepository : IDisposable, IRepository<Product>
{
private readonly ProductStoreContext _context;
public ProductRepository(ProductStoreContext context)
{
_context = context;
}
public IQueryable<Product> GetAll() => _context.Products;
public Product GetById(int id) => _context.Products.Find(id);
public void Add(Product product) => _context.Products.Add(product);
public void Remove(Product product) => _context.Products.Remove(product);
public void SaveChanges() => _context.SaveChanges();
public void Dispose() => _context.Dispose();
}
}
namespace ProductStore.Services
{
public class ProductService
{
private readonly IRepository<Product> _repository;
public ProductService(IRepository<Product> repository)
{
_repository = repository;
}
public IEnumerable<Product> GetAllProducts() => _repository.GetAll();
public Product GetProductById(int id) => _repository.GetById(id);
public void AddOrUpdateProduct(Product product)
{
if (product.Id == 0)
_repository.Add(product);
else
_repository.Remove(_repository.GetById(product.Id)) && _repository.Add(product);
_repository.SaveChanges();
}
public void DeleteProduct(int id)
{
var product = _repository.GetById(id);
_repository.Remove(product);
_repository.SaveChanges();
}
}
}
关键点解释
IRepository<T>接口:定义常用的数据访问操作,如获取所有记录、根据ID获取记录、添加记录、移除记录和保存更改。
ProductRepository类:实现IRepository<Product>接口,提供具体的数据访问逻辑。
ProductService类:使用IRepository<Product>接口来实现业务逻辑,如添加、更新、删除产品等操作。
IDisposable接口:确保资源被正确释放。
6 控制器和服务层的集成
在ASP.NET Web API控制器中调用服务层的方法,处理HTTP请求并返回响应,以下是一个示例控制器,展示了如何使用ProductService来处理CRUD操作。
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ProductStore.Models;
using ProductStore.Services;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ProductStore.Controllers
{
public class ProductsController : ApiController
{
private readonly ProductService _productService;
public ProductsController(ProductService productService)
{
_productService = productService;
}
// GET api/Products
public IHttpActionResult GetAllProducts() => Ok(_productService.GetAllProducts());
// GET api/Products/5
public IHttpActionResult GetProductById(int id) => Ok(_productService.GetProductById(id));
// POST api/Products
[HttpPost]
public IHttpActionResult AddOrUpdateProduct([FromBody] Product product)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
_productService.AddOrUpdateProduct(product);
return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
}
// DELETE api/Products/5
[HttpDelete]
public IHttpActionResult DeleteProduct(int id)
{
_productService.DeleteProduct(id);
return Ok();
}
}
}
关键点解释
ApiController:ASP.NET Web API的核心基类,提供基本的API功能。
[HttpGet],[HttpPost],[HttpDelete]等特性:标记HTTP动词,指定方法处理的请求类型。

Ok方法:返回HTTP 200状态码和结果数据。
BadRequest方法:返回HTTP 400状态码和错误信息。
CreatedAtRoute方法:返回HTTP 201状态码和新建资源的URL。
[FromBody]特性:指示参数应从请求体中反序列化。
ModelState.IsValid:检查模型验证是否通过。
routePrefix:路由前缀,用于生成URL。
RouteAttribute:自定义路由,指定HTTP方法和路径。
RouteName:路由名称,用于生成URL。
HttpStatusCodeResult:返回指定的HTTP状态码。
ContentResult结果。
ObjectResult:返回对象结果。
JsonConvert.SerializeObject:将对象序列化为JSON字符串。
JsonConvert.DeserializeObject<T>:将JSON字符串反序列化为对象。
JObject:表示JSON对象。
JArray:表示JSON数组。
JToken:表示JSON元素。
JValue:表示JSON值。
JNode:表示JSON节点。
JProperty:表示JSON属性。
JContainerContract:表示JSON容器契约。
JTokenType:表示JSON令牌类型。
JsonSerializerSettings:配置JSON序列化的设置。
JsonSerializer:用于序列化和反序列化JSON数据。
JsonTextWriter:用于写入JSON文本的写入器。
JsonReader:用于读取JSON数据的读取器。
JsonWriter:用于写入JSON数据的写入器。
JsonWriterFactory:用于创建JsonWriter的工厂类。
JsonTextWriter:用于写入JSON文本的写入器。
小伙伴们,上文介绍了“ASP.NET Web API教程 创建域模型的方法详细介绍”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。