ASP.NET WebApi 开放数据
开放式数据协议(OData) 是用于在 web 数据访问协议。它提供统一的方式来构造数据、 查询的数据和操纵数据集通过 CRUD 操作。它支持 AtomPub (XML) 和 JSON 格式。它还定义的方式来公开元数据有关的数据。客户端可以使用元数据来发现的类型信息和数据集的关系。
ASP.NET Web API 容易地创建一个数据集的 OData 终结点。您可以控制到底哪些 OData 操作终结点支持。你可以承载多个 OData 端点,除了非 OData 端点。你有你的数据模型后, 端业务逻辑和数据层的完全控制。
在Models文件夹下,添加Product.cs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
namespace
ApiDemo02.Models { /// <summary>产品实体类</summary> public
class Product { /// <summary>产品ID</summary> public
int ID { get ; set ; } /// <summary>产品名称</summary> public
string Name { get ; set ; } /// <summary>产品类别</summary> public
string Genre { get ; set ; } /// <summary>产品价格</summary> public
decimal Price { get ; set ; } } } |
注:先生成一下项目!
在 Controllers文件夹下,添加控制器:
点击“添加”,则出现下面错误:
原因:前面我提示过,创建新实体后,先生成一下项目。这里我还是忘记了,生成项目后,再照上面步骤添加控制器,ok!
支架自动创建了两个文件:
打开App_Start文件下的WebApiConfig.cs,修改代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 |
using
ApiDemo02.Models; using
System.Web.Http; using
System.Web.Http.OData.Builder; namespace
ApiDemo02 { public
static class WebApiConfig { public
static void Register(HttpConfiguration config) { // Web API configuration and services //创建 OData 端点的实体数据模型 (EDM) ODataConventionModelBuilder builder = new
ODataConventionModelBuilder(); //添加设置为 EDM 实体 builder.EntitySet<Product>( "Products" ); //添加终结点的路由 config.Routes.MapODataRoute( "odata" , "odata" , builder.GetEdmModel()); // Web API routes //config.MapHttpAttributeRoutes(); //config.Routes.MapHttpRoute( // name: "DefaultApi", // routeTemplate: "api/{controller}/{id}", // defaults: new { id = RouteParameter.Optional } //); } } } |
EDM 是一种抽象的数据模型。EDM 用于创建元数据文件和定义服务的 Uri。ODataConventionModelBuilder通过使用一组默认的命名约定 EDM 创建 EDM,这种方法要求最少代码。如果你想对 EDM 的更多控制,你可以使用ODataModelBuilder类来显式地创建 EDM 中的通过添加属性、 键和导航属性。
EntitySet<Product>("Product")中"Product"的字符串定义是控制器的名称。终结点可以有多个实体集。EntitySet < T >对应一个实体集,然后再定义一个对应的控制器。
MapODataRoute方法第一个参数是路线的友好名称。您服务的客户端看不到此名称。第二个参数是为终结点的 URI 前缀。鉴于此代码,为产品实体集的 URI 是 http:// /odata/Product。您的应用程序可以有多个 OData 端点。对于每个终结点,调用MapODataRoute ,并提供一个独特的路由名称和唯一的 URI 前缀。
在修改一下根目录下的Web.config中的<connectionStrings>数据库连接字符串节点:
1
2
3
4
5
6 |
<connectionStrings> <add name= "ProductContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=ProductDB; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|ProductDB.mdf" providerName= "System.Data.SqlClient"
/> </connectionStrings> |
注:这一小节EDM有点难理解,我也得好好琢磨一下!
打开VS“工具”:
查看迁移帮助:
启用迁移:
这时,会多出Migrations文件夹及其下Configuration.cs文件:
修改Migrations文件夹下Configuration.cs文件来添加种子数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 |
using
ApiDemo02.Models; using
System.Data.Entity.Migrations; namespace
ApiDemo02.Migrations { internal
sealed class Configuration : DbMigrationsConfiguration<ApiDemo02.Models.ProductContext> { public
Configuration() { AutomaticMigrationsEnabled = false ; } protected
override void Seed(ApiDemo02.Models.ProductContext context) { context.Products.AddOrUpdate( new
Product[] { new
Product() { ID = 1, Name = "Hat" , Price = 15, Genre = "Apparel"
}, new
Product() { ID = 2, Name = "Socks" , Price = 5, Genre = "Apparel"
}, new
Product() { ID = 3, Name = "Scarf" , Price = 12, Genre = "Apparel"
}, new
Product() { ID = 4, Name = "Yo-yo" , Price = 4.95M, Genre = "Toys"
}, new
Product() { ID = 5, Name = "Puzzle" , Price = 8, Genre = "Toys"
}, }); } } } |
添加迁移:
更新数据库:
检查数据库文件:
至此,数据库创建OK,也有了模拟数据!
这里使用HTTP工具来模拟请求,所以先去下载(http://www.telerik.com/fiddler)安装Fiddler4。
运行项目,看一下端口:
注:这里Chrome浏览器以xml方式读取,如果使用IE浏览器,它默认是json方式,会提示下载!
打开fiddler,点Composer:
点Execute后,并查看xml结果:
响应json请求:
查看结果:
获取元数据,不论是否json请求,它返回xml格式:
查看结果:
读取Product数据:
查看数据:
由于截图太多了,我就不演示POST等其它请求了!
回顾一下Cntrollers下ProductController.cs的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 |
using
System.Data.Entity; using
System.Data.Entity.Infrastructure; using
System.Linq; using
System.Net; using
System.Web.Http; using
System.Web.Http.ModelBinding; using
System.Web.Http.OData; using
ApiDemo02.Models; namespace
ApiDemo02.Controllers { /* 若要为此控制器添加路由,请将这些语句合并到 WebApiConfig 类的 Register 方法中。请注意 OData URL 区分大小写。 using System.Web.Http.OData.Builder; using ApiDemo02.Models; ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); builder.EntitySet<Product>("Product"); config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel()); */ public
class ProductController : ODataController { private
ProductContext db = new
ProductContext(); // GET odata/Product [Queryable] public
IQueryable<Product> GetProduct() { return
db.Products; } // GET odata/Product(5) [Queryable] public
SingleResult<Product> GetProduct([FromODataUri] int
key) { return
SingleResult.Create(db.Products.Where(product => product.ID == key)); } // PUT odata/Product(5) public
IHttpActionResult Put([FromODataUri] int
key, Product product) { if
(!ModelState.IsValid) { return
BadRequest(ModelState); } if
(key != product.ID) { return
BadRequest(); } db.Entry(product).State = EntityState.Modified; try { db.SaveChanges(); } catch
(DbUpdateConcurrencyException) { if
(!ProductExists(key)) { return
NotFound(); } else { throw ; } } return
Updated(product); } // POST odata/Product public
IHttpActionResult Post(Product product) { if
(!ModelState.IsValid) { return
BadRequest(ModelState); } db.Products.Add(product); db.SaveChanges(); return
Created(product); } // PATCH odata/Product(5) [AcceptVerbs( "PATCH" , "MERGE" )] public
IHttpActionResult Patch([FromODataUri] int
key, Delta<Product> patch) { if
(!ModelState.IsValid) { return
BadRequest(ModelState); } Product product = db.Products.Find(key); if
(product == null ) { return
NotFound(); } patch.Patch(product); try { db.SaveChanges(); } catch
(DbUpdateConcurrencyException) { if
(!ProductExists(key)) { return
NotFound(); } else { throw ; } } return
Updated(product); } // DELETE odata/Product(5) public
IHttpActionResult Delete([FromODataUri] int
key) { Product product = db.Products.Find(key); if
(product == null ) { return
NotFound(); } db.Products.Remove(product); db.SaveChanges(); return
StatusCode(HttpStatusCode.NoContent); } protected
override void Dispose( bool
disposing) { if
(disposing) { db.Dispose(); } base .Dispose(disposing); } private
bool ProductExists( int
key) { return
db.Products.Count(e => e.ID == key) > 0; } } } |
真的很神奇地采用数据库上下文帮我们封装好数据库操作!疑问:还有必要再封装成仓储库方式吗? 再有WebApi如何路由的和JSON序列化/格式化,有空再介绍。
(注:截图过多,看起来花花绿绿地,抱歉了!)