ASP.NET Core OData 实践——Lesson8增删改查单值类型Property(C#)

单值属性(Single-Valued Property)指的是实体中类型为复杂对象但非集合的属性,比如客户的账单地址(BillingAddress)、企业客户的注册地址等。
单值属性的操作涵盖了查询、替换、部分更新等多种场景。本文将结合代码示例,讲解如何在 OData 控制器中优雅地处理单值属性,并分析其在数据建模和接口设计中的重要性。

支持的接口

Request MethodRoute Template说明
GET~/{entityset}/{key}/{property}查询基类类型Entity的基础类型属性值
GET~/{entityset}/{key}/{cast}/{property}查询派生类型Entity的基础类型属性值
GET~/{entityset}/{key}/{property}/{cast}查询基类类型Entity的派生类型属性值
GET~/{entityset}/{key}/{cast}/{property}/{cast}查询派生类型Entity的派生类型属性值
GET~/{singleton}/{property}查询基类类型单例的基础类型属性值
GET~/{singleton}/{cast}/{property}查询派生类型单例的基础类型属性值
GET~/{singleton}/{property}/{cast}查询基类类型单例的派生类型属性值
GET~/{singleton}/{cast}/{property}/{cast}查询派生类型单例的派生类型属性值
PATCH~/{entityset}/{key}/{singlevaluedproperty}局部更新基类类型Entity的单值基础类型属性
PATCH~/{entityset}/{key}/{cast}/{singlevaluedproperty}局部更新派生类型Entity的单值基础类型属性
PATCH~/{entityset}/{key}/{singlevaluedproperty}/{cast}局部更新基类类型Entity的单值派生类型属性
PATCH~/{entityset}/{key}/{cast}/{singlevaluedproperty}/{cast}局部更新派生类型Entity的单值派生类型属性
PATCH~/{singleton}/{singlevaluedproperty}局部更新基类类型Entity的单值基础类型属性
PATCH~/{singleton}/{cast}/{singlevaluedproperty}局部更新派生类型Entity的单值基础类型属性
PATCH~/{singleton}/{singlevaluedproperty}/{cast}局部更新基类类型Entity的单值派生类型属性
PATCH~/{singleton}/{cast}/{singlevaluedproperty}/{cast}局部更新派生类型Entity的单值派生类型属性
DELETE~/{entityset}/{key}/{nullableproperty}删除基类类型Entity的非空属性
DELETE~/{entityset}/{key}/{cast}/{nullableproperty}删除派生类型Entity的非空属性
DELETE~/{singleton}/{nullableproperty}删除基类类型单例的非空属性
DELETE~/{singleton}/{cast}/{nullableproperty}删除派生类型单例的非空属性

主要模型设计

在项目下新增Models文件夹,并添加Address、PostalAddress 、Customer和EnterpriseCustomer类。

namespace Lesson8.Models
{
    public class Address
    {
        public required string Street { get; set; }
    }
}
namespace Lesson8.Models
{
    public class PostalAddress : Address
    {
        public required string PostalCode { get; set; }
    }
}
using System.Net;

namespace Lesson8.Models
{
    using System.Collections.Generic;
    public class Customer
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public Address? BillingAddress { get; set; }
        public List<string> ContactPhones { get; set; } = [];
    }
}
using System.Net;

namespace Lesson8.Models
{
    using System.Collections.Generic;
    public class EnterpriseCustomer : Customer
    {
        public decimal? CreditLimit { get; set; }
        public Address? RegisteredAddress { get; set; }
        public List<Address> ShippingAddresses { get; set; } = new List<Address>();
    }
}

在这里插入图片描述

控制器设计

在项目中新增Controller文件夹,然后添加CompanyController类。该类注册于ODataController,以便拥有如下能力:

  1. OData 路由支持
    继承 ODataController 后,控制器自动支持 OData 路由(如 /odata/Shapes(1)),可以直接响应 OData 标准的 URL 路径和操作。
  2. OData 查询参数支持
    可以使用 [EnableQuery] 特性,自动支持 $filter、$select、$orderby、$expand 等 OData 查询参数,无需手动解析。
  3. OData 响应格式
    返回的数据会自动序列化为 OData 标准格式(如 JSON OData),方便前端或其他系统消费。
  4. OData Delta 支持
    支持 Delta<T>、DeltaSet<T> 等类型,便于实现 PATCH、批量 PATCH 等 OData 特有的部分更新操作。
  5. 更丰富的 OData 语义
    继承后可方便实现实体集、实体、导航属性、复杂类型等 OData 语义,提升 API 的表达能力。
using Lesson8.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Deltas;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;

namespace Lesson8.Controllers
{
    public class CustomersController: ODataController
    {
    }
}

下面我们在该类中填充逻辑。

数据源

        private static List<Customer> customers = new List<Customer>
        {
            new Customer
            {
                Id = 1,
                Name = "Customer 1",
                ContactPhones = new List<string> { "761-116-1865" },
                BillingAddress = new Address { Street = "Street 1A" }
            },
            new Customer
            {
                Id = 2,
                Name = "Customer 2",
                ContactPhones = new List<string> { "835-791-8257" },
                BillingAddress = new PostalAddress { Street = "2A", PostalCode = "14030" }
            },
            new EnterpriseCustomer
            {
                Id = 3,
                Name = "Customer 3",
                ContactPhones = new List<string> { "157-575-6005" },
                BillingAddress = new Address { Street = "Street 3A" },
                CreditLimit = 4200,
                RegisteredAddress = new Address { Street = "Street 3B" },
                ShippingAddresses = new List<Address>
                {
                    new Address { Street = "Street 3C" }
                }
            },
            new EnterpriseCustomer
            {
                Id = 4,
                Name = "Customer 4",
                ContactPhones = new List<string> { "724-096-6719" },
                BillingAddress = new Address { Street = "Street 4A" },
                CreditLimit = 3700,
                RegisteredAddress = new PostalAddress { Street = "Street 4B", PostalCode = "22109" },
                ShippingAddresses = new List<Address>
                {
                    new Address { Street = "Street 4C" }
                }
            }
        };

查询(GET)

Request MethodRoute Template说明
GET~/{entityset}/{key}/{property}查询基类类型Entity的基础类型属性值
GET~/{entityset}/{key}/{cast}/{property}查询派生类型Entity的基础类型属性值
GET~/{entityset}/{key}/{property}/{cast}查询基类类型Entity的派生类型属性值
GET~/{entityset}/{key}/{cast}/{property}/{cast}查询派生类型Entity的派生类型属性值
GET~/{singleton}/{property}查询基类类型单例的基础类型属性值
GET~/{singleton}/{cast}/{property}查询派生类型单例的基础类型属性值
GET~/{singleton}/{property}/{cast}查询基类类型单例的派生类型属性值
GET~/{singleton}/{cast}/{property}/{cast}查询派生类型单例的派生类型属性值

查询基类类型Entity的基础类型属性的值

Request MethodRoute Template说明
GET~/{entityset}/{key}/{primitiveproperty}/$value查询基类类型Entity的基础类型属性的值
GET~/{singleton}/{property}查询基类类型单例的基础类型属性值
        public ActionResult<Address> GetBillingAddress([FromRoute] int key)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (customer == null || customer.BillingAddress == null)
            {
                return NotFound();
            }

            return customer.BillingAddress;
        }
  • Request
curl --location 'http://localhost:5119/odata/Customers(2)/BillingAddress'
  • Response
{
    "@odata.context": "http://localhost:5119/odata/$metadata#Customers(2)/BillingAddress",
    "Street": "2A",
    "PostalCode": "14030"
}

查询派生类型Entity的基础类型属性值

Request MethodRoute Template说明
GET~/{entityset}/{key}/{cast}/{property}查询派生类型Entity的基础类型属性值
GET~/{singleton}/{cast}/{property}查询派生类型单例的基础类型属性值
        public ActionResult<Address> GetRegisteredAddressFromEnterpriseCustomer([FromRoute] int key)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (enterpriseCustomer == null || enterpriseCustomer.RegisteredAddress == null)
            {
                return NotFound();
            }

            return enterpriseCustomer.RegisteredAddress;
        }
  • Request
curl --location 'http://localhost:5119/odata/Customers(4)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress'
  • Response
{
    "@odata.context": "http://localhost:5119/odata/$metadata#Customers(4)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress",
    "Street": "Street 4B",
    "PostalCode": "22109"
}

查询基类类型Entity的派生类型属性值

Request MethodRoute Template说明
GET~/{entityset}/{key}/{property}/{cast}查询基类类型Entity的派生类型属性值
GET~/{singleton}/{property}/{cast}查询基类类型单例的派生类型属性值
        public ActionResult<PostalAddress> GetBillingAddressOfPostalAddress([FromRoute] int key)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (!(customer?.BillingAddress is PostalAddress billingAddress))
            {
                return NotFound();
            }

            return billingAddress;
        }
  • Request
curl --location 'http://localhost:5119/odata/Customers(2)/BillingAddress/Lesson8.Models.PostalAddress'
  • Response
{
    "@odata.context": "http://localhost:5119/odata/$metadata#Customers(2)/BillingAddress/Lesson8.Models.PostalAddress",
    "Street": "2A",
    "PostalCode": "14030"
}

查询派生类型Entity的派生类型属性值

Request MethodRoute Template说明
GET~/{entityset}/{key}/{cast}/{property}/{cast}查询派生类型Entity的派生类型属性值
GET~/{singleton}/{cast}/{property}/{cast}查询派生类型单例的派生类型属性值
        public ActionResult<PostalAddress> GetRegisteredAddressOfPostalAddressFromEnterpriseCustomer([FromRoute] int key)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (!(enterpriseCustomer?.RegisteredAddress is PostalAddress registeredAddress))
            {
                return NotFound();
            }

            return registeredAddress;
        }
  • Request
curl --location 'http://localhost:5119/odata/Customers(4)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress/Lesson8.Models.PostalAddress'
  • Response
{
    "@odata.context": "http://localhost:5119/odata/$metadata#Customers(4)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress/Lesson8.Models.PostalAddress",
    "Street": "Street 4B",
    "PostalCode": "22109"
}

完整更新(PUT)

Request MethodRoute Template说明
PUT~/{entityset}/{key}/{property}完整更新基类类型Entity的基础类型属性值
PUT~/{entityset}/{key}/{cast}/{property}
PUT~/{entityset}/{key}/{property}/{cast}
PUT~/{entityset}/{key}/{cast}/{property}/{cast}
PUT~/{singleton}/{property}
PUT~/{singleton}/{cast}/{property}
PUT~/{singleton}/{property}/{cast}
PUT~/{singleton}/{cast}/{property}/{cast}

完整更新基类类型Entity的基础类型属性值

Request MethodRoute Template说明
PUT~/{entityset}/{key}/{property}完整更新基类类型Entity的基础类型属性值
PUT~/{singleton}/{property}
        public ActionResult PutToBillingAddress([FromRoute] int key, [FromBody] Address address)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (customer == null)
            {
                return NotFound();
            }

            customer.BillingAddress = address;

            return Ok();
        }
  • Request
curl --location --request PUT 'http://localhost:5119/odata/Customers(1)/BillingAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "One Microsoft Way"
}'

完整更新派生类型Entity的基础类型属性值

Request MethodRoute Template说明
PUT~/{entityset}/{key}/{cast}/{property}
PUT~/{singleton}/{cast}/{property}
        public ActionResult PutToRegisteredAddressFromEnterpriseCustomer([FromRoute] int key, [FromBody] Address address)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (enterpriseCustomer == null)
            {
                return NotFound();
            }

            enterpriseCustomer.RegisteredAddress = address;

            return Ok();
        }
  • Request
curl --location --request PUT 'http://localhost:5119/odata/Customers(3)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "One Microsoft Way"
}'

完整更新基类类型Entity的派生类型属性值

Request MethodRoute Template说明
PUT~/{entityset}/{key}/{property}/{cast}
PUT~/{singleton}/{property}/{cast}
        public ActionResult PutToBillingAddressOfPostalAddress([FromRoute] int key, [FromBody] PostalAddress billingAddress)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (!(customer?.BillingAddress is PostalAddress billingAddressInstance))
            {
                return NotFound();
            }

            billingAddressInstance.Street = billingAddress.Street;
            billingAddressInstance.PostalCode = billingAddress.PostalCode;

            return Ok();
        }
  • Request
curl --location --request PUT 'http://localhost:5119/odata/Customers(2)/BillingAddress/Lesson8.Models.PostalAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "2A",
    "PostalCode": "14030"
}'

完整更新派生类型Entity的派生类型属性值

Request MethodRoute Template说明
PUT~/{entityset}/{key}/{cast}/{property}/{cast}
PUT~/{singleton}/{cast}/{property}/{cast}
        public ActionResult PutToRegisteredAddressOfPostalAddressFromEnterpriseCustomer([FromRoute] int key, [FromBody] PostalAddress registeredAddress)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (enterpriseCustomer == null)
            {
                return NotFound();
            }

            enterpriseCustomer.RegisteredAddress = registeredAddress;

            return Ok();
        }
  • Request
curl --location --request PUT 'http://localhost:5119/odata/Customers(4)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress/Lesson8.Models.PostalAddress' \
--header 'Content-Type: text/plain' \
--data '{
    "Street": "One Microsoft Way",
    "PostalCode": "98052"
}'

新增(POST)

单值属性(如 BillingAddress、RegisteredAddress)只是实体的一个字段,不是集合,不能单独“新增”,所以不支持POST指令。

局部更新(PATCH)

Request MethodRoute Template说明
PATCH~/{entityset}/{key}/{singlevaluedproperty}局部更新基类类型Entity的单值基础类型属性
PATCH~/{entityset}/{key}/{cast}/{singlevaluedproperty}局部更新派生类型Entity的单值基础类型属性
PATCH~/{entityset}/{key}/{singlevaluedproperty}/{cast}局部更新基类类型Entity的单值派生类型属性
PATCH~/{entityset}/{key}/{cast}/{singlevaluedproperty}/{cast}局部更新派生类型Entity的单值派生类型属性
PATCH~/{singleton}/{singlevaluedproperty}局部更新基类类型Entity的单值基础类型属性
PATCH~/{singleton}/{cast}/{singlevaluedproperty}局部更新派生类型Entity的单值基础类型属性
PATCH~/{singleton}/{singlevaluedproperty}/{cast}局部更新基类类型Entity的单值派生类型属性
PATCH~/{singleton}/{cast}/{singlevaluedproperty}/{cast}局部更新派生类型Entity的单值派生类型属性

局部更新基类类型Entity的单值基础类型属性

Request MethodRoute Template说明
PATCH~/{entityset}/{key}/{singlevaluedproperty}局部更新基类类型Entity的单值基础类型属性
PATCH~/{singleton}/{singlevaluedproperty}局部更新基类类型Entity的单值基础类型属性
        public ActionResult PatchToBillingAddress([FromRoute] int key, [FromBody] Delta<Address> delta)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (customer == null)
            {
                return NotFound();
            }

            if (customer.BillingAddress == null)
            {
                return BadRequest("BillingAddress cannot be null.");
            }

            if (delta == null)
            {
                return BadRequest("Invalid request body.");
            }

            delta.Patch(customer.BillingAddress);

            return Ok();
        }
  • Request
curl --location --request PATCH 'http://localhost:5119/odata/Customers(1)/BillingAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "One Microsoft Way"
}'

局部更新派生类型Entity的单值基础类型属性

Request MethodRoute Template说明
PATCH~/{entityset}/{key}/{cast}/{singlevaluedproperty}局部更新派生类型Entity的单值基础类型属性
PATCH~/{singleton}/{cast}/{singlevaluedproperty}局部更新派生类型Entity的单值基础类型属性
        public ActionResult PatchToRegisteredAddressFromEnterpriseCustomer([FromRoute] int key, [FromBody] Delta<Address> delta)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (enterpriseCustomer == null)
            {
                return NotFound();
            }

            if (enterpriseCustomer.RegisteredAddress == null)
            {
                return BadRequest("RegisteredAddress cannot be null.");
            }

            if (delta == null)
            {
                return BadRequest("Invalid request body.");
            }

            delta.Patch(enterpriseCustomer.RegisteredAddress);

            return Ok();
        }
  • Request
curl --location --request PATCH 'http://localhost:5119/odata/Customers(3)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "One Microsoft Way"
}'

局部更新基类类型Entity的单值派生类型属性

Request MethodRoute Template说明
PATCH~/{entityset}/{key}/{singlevaluedproperty}/{cast}局部更新基类类型Entity的单值派生类型属性
PATCH~/{singleton}/{singlevaluedproperty}/{cast}局部更新基类类型Entity的单值派生类型属性
        public ActionResult PatchToBillingAddressOfPostalAddress([FromRoute] int key, [FromBody] Delta<PostalAddress> delta)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (!(customer?.BillingAddress is PostalAddress billingAddress))
            {
                return NotFound();
            }

            delta.Patch(billingAddress);

            return Ok();
        }
  • Request
curl --location --request PATCH 'http://localhost:5119/odata/Customers(2)/BillingAddress/Lesson8.Models.PostalAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "2A",
    "PostalCode": "14030"
}'

局部更新派生类型Entity的单值派生类型属性

Request MethodRoute Template说明
PATCH~/{entityset}/{key}/{cast}/{singlevaluedproperty}/{cast}局部更新派生类型Entity的单值派生类型属性
PATCH~/{singleton}/{cast}/{singlevaluedproperty}/{cast}局部更新派生类型Entity的单值派生类型属性
        public ActionResult PatchToRegisteredAddressOfPostalAddressFromEnterpriseCustomer([FromRoute] int key, [FromBody] Delta<PostalAddress> delta)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (!(enterpriseCustomer?.RegisteredAddress is PostalAddress registeredAddress))
            {
                return NotFound();
            }

            delta.Patch(registeredAddress);

            return Ok();
        }
  • Request
curl --location --request PATCH 'http://localhost:5119/odata/Customers(4)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress/Lesson8.Models.PostalAddress' \
--header 'Content-Type: application/json' \
--data '{
    "Street": "One Microsoft Way",
    "PostalCode": "98052"
}'

删除(DELETE)

Request MethodRoute Template说明
DELETE~/{entityset}/{key}/{nullableproperty}删除基类类型Entity的非空属性
DELETE~/{entityset}/{key}/{cast}/{nullableproperty}删除派生类型Entity的非空属性
DELETE~/{singleton}/{nullableproperty}删除基类类型单例的非空属性
DELETE~/{singleton}/{cast}/{nullableproperty}删除派生类型单例的非空属性

删除操作只能支持可以为null的属性,所以我们在模型定义时需要将要操作的属性(如本例中的Customer.BillingAddress和EnterpriseCustomer.RegisteredAddress )设置为nullable,否则请求无法路由到相关函数。

using System.Net;

namespace Lesson8.Models
{
    using System.Collections.Generic;
    public class Customer
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public Address? BillingAddress { get; set; }
        public List<string> ContactPhones { get; set; } = [];
    }
}
using System.Net;

namespace Lesson8.Models
{
    using System.Collections.Generic;
    public class EnterpriseCustomer : Customer
    {
        public decimal? CreditLimit { get; set; }
        public Address? RegisteredAddress { get; set; }
        public List<Address> ShippingAddresses { get; set; } = new List<Address>();
    }
}

删除基类类型Entity的非空属性

        public ActionResult DeleteToBillingAddress([FromRoute] int key)
        {
            var customer = customers.SingleOrDefault(d => d.Id.Equals(key));

            if (customer == null)
            {
                return NotFound();
            }

            customer.BillingAddress = null!; // Use null-forgiving operator to suppress the warning

            return NoContent();
        }
  • Request
curl --location --request DELETE 'http://localhost:5119/odata/Customers(1)/BillingAddress'

删除派生类型Entity的非空属性

        public ActionResult DeleteToRegisteredAddressFromEnterpriseCustomer([FromRoute] int key)
        {
            var enterpriseCustomer = customers.OfType<EnterpriseCustomer>().SingleOrDefault(d => d.Id.Equals(key));

            if (enterpriseCustomer == null)
            {
                return NotFound();
            }

            enterpriseCustomer.RegisteredAddress = null!; // Use null-forgiving operator to suppress the warning

            return NoContent();
        }
  • Request
curl --location --request DELETE 'http://localhost:5119/odata/Customers(3)/Lesson8.Models.EnterpriseCustomer/RegisteredAddress'

主程序

using Lesson8.Models;
using Microsoft.AspNetCore.OData;
using Microsoft.OData.ModelBuilder;
using Microsoft.OData.Edm;

var builder = WebApplication.CreateBuilder(args);

static IEdmModel GetEdmModel()
{
    var modelBuilder = new ODataConventionModelBuilder();
    modelBuilder.EntitySet<Customer>("Customers");

    return modelBuilder.GetEdmModel();
}

builder.Services.AddControllers().AddOData(options =>
    options.Select()
           .Filter()
           .OrderBy()
           .Expand()
           .Count()
           .SetMaxTop(null)
           .AddRouteComponents("odata", GetEdmModel())
);

var app = builder.Build();

app.UseRouting();

app.MapControllers();

app.Run();

服务文档

  • Request
curl --location 'http://localhost:5119/odata'
  • Response
{
    "@odata.context": "http://localhost:5119/odata/$metadata",
    "value": [
        {
            "name": "Customers",
            "kind": "EntitySet",
            "url": "Customers"
        }
    ]
}

模型元文档

  • Request
curl --location 'http://localhost:5119/odata/$metadata'
  • Response
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:DataServices>
        <Schema Namespace="Lesson8.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityType Name="Customer">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.Int32" Nullable="false" />
                <Property Name="Name" Type="Edm.String" Nullable="false" />
                <Property Name="BillingAddress" Type="Lesson8.Models.Address" Nullable="false" />
                <Property Name="ContactPhones" Type="Collection(Edm.String)" />
            </EntityType>
            <ComplexType Name="Address">
                <Property Name="Street" Type="Edm.String" Nullable="false" />
            </ComplexType>
            <ComplexType Name="PostalAddress" BaseType="Lesson8.Models.Address">
                <Property Name="PostalCode" Type="Edm.String" Nullable="false" />
            </ComplexType>
            <EntityType Name="EnterpriseCustomer" BaseType="Lesson8.Models.Customer">
                <Property Name="CreditLimit" Type="Edm.Decimal" Nullable="false" Scale="variable" />
                <Property Name="RegisteredAddress" Type="Lesson8.Models.Address" Nullable="false" />
                <Property Name="ShippingAddresses" Type="Collection(Lesson8.Models.Address)" />
            </EntityType>
        </Schema>
        <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityContainer Name="Container">
                <EntitySet Name="Customers" EntityType="Lesson8.Models.Customer" />
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

代码地址

https://github.com/f304646673/odata/tree/main/csharp/Lesson/Lesson8

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

breaksoftware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值