欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET Core开发日志——OData

程序员文章站 2023-09-20 17:31:43
简述 OData,即Open Data Protocol,是由微软在2007年推出的一款开放协议,旨在通过简单、标准的方式创建和使用查询式及交互式RESTful API。 类库 在.NET Core中想要使用OData功能的话需要添加 包。 准备模型类 创建Edm模型 OData使用EDM,即Ent ......

简述

odata,即open data protocol,是由微软在2007年推出的一款开放协议,旨在通过简单、标准的方式创建和使用查询式及交互式restful api。

类库

在.net core中想要使用odata功能的话需要添加microsoft.aspnetcore.odata包。

dotnet add package microsoft.aspnetcore.odata

准备模型类

public class address
{
    public string city { get; set; }
    public string street { get; set; }
}
public enum category
{
    book,
    magazine,
    ebook
}
public class press
{
    public int id { get; set; }
    public string name { get; set; }
    public string email { get; set; }
    public category category { get; set; }
}
public class book
{
    public int id { get; set; }
    public string isbn { get; set; }
    public string title { get; set; }
    public string author { get; set; }
    public decimal price { get; set; }
    public address address { get; set; }
    public press press { get; set; }
}

创建edm模型

odata使用edm,即entity data model来描述数据的结构。在startup文件中添加创建方法。

private static iedmmodel getedmmodel()
{
    var builder = new odataconventionmodelbuilder();
    builder.entityset<book>("books");
    builder.entityset<press>("presses");
    return builder.getedmmodel();
}

注册odata服务

在startup文件的configureservices方法里注册odata服务。

services.addodata();
services.addmvc(options =>
    {
        options.enableendpointrouting = false;
    }).setcompatibilityversion(compatibilityversion.version_2_2);

这里要注意的是在.net core 2.2里,默认已经有终结点,所以要使用odata的终结点的话需要将默认选项禁用掉。

注册odata终结点

同样在startup文件里,在其configure方法内将原来的注册路由内容改为注册odata的终结点。

app.usemvc(b =>
{
    b.mapodataserviceroute("odata", "odata", getedmmodel());
});

显示元数据

运行程序后访问https://localhost:5001/odata/$metadata地址,可以看到所有可用模型的元数据。

<edmx:edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" version="4.0">
    <edmx:dataservices>
        <schema xmlns="http://docs.oasis-open.org/odata/ns/edm" namespace="default">
            <entitytype name="book">
                <key>
                    <propertyref name="id"/>
                </key>
                <property name="id" type="edm.int32" nullable="false"/>
                <property name="isbn" type="edm.string"/>
                <property name="title" type="edm.string"/>
                <property name="author" type="edm.string"/>
                <property name="price" type="edm.decimal" nullable="false"/>
                <property name="address" type="default.address"/>
                <navigationproperty name="press" type="default.press"/>
            </entitytype>
            <entitytype name="press">
                <key>
                    <propertyref name="id"/>
                </key>
                <property name="id" type="edm.int32" nullable="false"/>
                <property name="name" type="edm.string"/>
                <property name="email" type="edm.string"/>
                <property name="category" type="default.category" nullable="false"/>
            </entitytype>
            <complextype name="address">
                <property name="city" type="edm.string"/>
                <property name="street" type="edm.string"/>
            </complextype>
            <enumtype name="category">
                <member name="book" value="0"/>
                <member name="magazine" value="1"/>
                <member name="ebook" value="2"/>
            </enumtype>
            <entitycontainer name="container">
                <entityset name="books" entitytype="default.book">
                    <navigationpropertybinding path="press" target="presses"/>
                </entityset>
                <entityset name="presses" entitytype="default.press"/>
            </entitycontainer>
        </schema>
    </edmx:dataservices>
</edmx:edmx>

创建controller

本文实例中不考虑数据库的操作,故而使用hard code方式构建必要的模型对象。

public class bookscontroller : odatacontroller
{
    private static ilist<book> books {get; set;}
    public bookscontroller()
    {
        books = new list<book>
        {
            new book
            {
                id = 1,
                isbn = "111-0-321-56789-1",
                title = "calculus",
                price = 66.6m,
                address = new address
                {
                    city = "shanghai",
                    street = "beijin xi road"
                },
                press = new press
                {
                    id = 1,
                    name = "shanghai tongji",
                    category = category.book
                }
            },
            new book
            {
                id = 2,
                isbn = "222-2-654-00000-2",
                title = "linear algebra",
                price = 53.2m,
                address = new address
                {
                    city = "shanghai",
                    street = "beijin dong road"
                },
                press = new press
                {
                    id = 2,
                    name = "shanghai fudan",
                    category = category.ebook
                }
            }            
        };   
    }

    [enablequery]
    public iactionresult get()
    {
        return ok(books);
    }

    [enablequery]
    public iactionresult get(int key)
    {
        return ok(books.firstordefault(b => b.id == key));
    }
}

enablequery特性在需要高级查询的场景时必须添加。

查询

加入controller之后,访问https://localhost:5001/odata/books地址,可得到所有book数据。

{
    "@odata.context": "https://localhost:5001/odata/$metadata#books",
    "value": [
        {
            "id": 1,
            "isbn": "111-0-321-56789-1",
            "title": "calculus",
            "author": null,
            "price": 66.6,
            "address": {
                "city": "shanghai",
                "street": "beijin xi road"
            }
        },
        {
            "id": 2,
            "isbn": "222-2-654-00000-2",
            "title": "linear algebra",
            "author": null,
            "price": 53.2,
            "address": {
                "city": "shanghai",
                "street": "beijin dong road"
            }
        }
    ]
}

访问https://localhost:5001/odata/books(1)地址,可得到key值为1的book数据。

{
    "@odata.context": "https://localhost:5001/odata/$metadata#books/$entity",
    "id": 1,
    "isbn": "111-0-321-56789-1",
    "title": "calculus",
    "author": null,
    "price": 66.6,
    "address": {
        "city": "shanghai",
        "street": "beijin xi road"
    }
}

高级查询

如果想要使用odata查询的高级功能,可以在注册终结点时额外加上相应的配置。

app.usemvc(b =>
{
    b.select().expand().filter().orderby().maxtop(100).count();
    b.mapodataserviceroute("odata", "odata", getedmmodel());
});

访问网址时加上所需的查询内容:
https://localhost:5001/odata/books?$select=id,title

{
    "@odata.context": "https://localhost:5001/odata/$metadata#books(id,title)",
    "value": [
        {
            "id": 1,
            "title": "calculus"
        },
        {
            "id": 2,
            "title": "linear algebra"
        }
    ]
}

如果想要按特定条件过滤数据内容的话也很容易:
https://localhost:5001/odata/books?$filter=price%20le%2060

{
    "@odata.context": "https://localhost:5001/odata/$metadata#books",
    "value": [
        {
            "id": 2,
            "isbn": "222-2-654-00000-2",
            "title": "linear algebra",
            "author": null,
            "price": 53.2,
            "address": {
                "city": "shanghai",
                "street": "beijin dong road"
            }
        }
    ]
}

总结

不难看出,odata的真正魅力在于其对那些高级查询功能的支持,所以在创建restful api时,不妨考虑使用odata,这样应该能减少许多不必要的代码工作。