大纲
在现代的 Web 开发中,OData(开放数据协议)已经成为了一种非常流行的标准,用于构建和使用 RESTful API。ASP.NET Core 提供了强大的 OData 支持,使得开发者可以轻松地创建符合 OData 规范的 API。
本文主要实现了一个ASP.NET Core 应用程序,通过构建一个复杂的 OData 模型来支持 OData API 的开发。代码中定义了枚举类型、复杂类型、实体类型、导航属性、函数、动作等 OData 模型元素,并将它们组合成一个完整的 EdmModel。
关键概念和方法
枚举类型(EnumType)
- 概念:枚举类型用于定义一组命名的常量值。在 OData 中,枚举类型可以作为实体属性的类型,使得数据具有更好的可读性和可维护性。
- 应用场景:当某个属性的取值是有限的且固定的一组值时,使用枚举类型非常合适。例如,颜色、性别、状态等。
- 特点:枚举类型的成员具有唯一的名称和对应的数值,方便在代码中进行比较和使用。
- 与其他类型的区别:与复杂类型和实体类型不同,枚举类型不包含结构属性,只是一组简单的常量值。
static void addEnumType(EdmModel model)
{
EdmEnumType color = new("Lesson10", "Color");
color.AddMember(new EdmEnumMember(color, "Red", new EdmEnumMemberValue(0)));
color.AddMember(new EdmEnumMember(color, "Blue", new EdmEnumMemberValue(1)));
color.AddMember(new EdmEnumMember(color, "Green", new EdmEnumMemberValue(2)));
model.AddElement(color);
}
Model Meta中的表现
<EnumType Name="Color">
<Member Name="Red" Value="0" />
<Member Name="Blue" Value="1" />
<Member Name="Green" Value="2" />
</EnumType>
复杂类型(ComplexType)
- 概念:复杂类型是一种包含多个结构属性的类型,但没有唯一标识符。它可以用于组合多个相关的属性,形成一个逻辑上的整体。
- 应用场景:当需要将多个相关的属性组合在一起,并且这些属性不需要唯一标识时,可以使用复杂类型。例如,地址信息、联系人信息等。
- 特点:复杂类型可以嵌套,即一个复杂类型的属性可以是另一个复杂类型。它通常用于,即一个复杂类型的属性可以是另一个复杂类型。它通常用于表示实体的一部分,而不是独立的实体。
- 与其他类型的区别:与实体类型不同,复杂类型没有唯一标识符,不能独立存在,必须作为其他实体或复杂类型的一部分使用。与枚举类型不同,复杂类型包含多个结构属性,而不是简单的常量值。
static void addNonAbstractComplexType(EdmModel model)
{
EdmComplexType address = new("Lesson10", "Address");
address.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String);
address.AddStructuralProperty("City", EdmPrimitiveTypeKind.String);
address.AddStructuralProperty("State", EdmPrimitiveTypeKind.String);
address.AddStructuralProperty("Country", EdmPrimitiveTypeKind.String);
model.AddElement(address);
}
Model Meta中的表现
<ComplexType Name="Address">
<Property Name="Street" Type="Edm.String" />
<Property Name="City" Type="Edm.String" />
<Property Name="State" Type="Edm.String" />
<Property Name="Country" Type="Edm.String" />
</ComplexType>
抽象
Abstract Complex Type(抽象复杂类型) 是一种特殊的复杂类型,其核心作用是定义一组派生类型的公共结构和行为,同时禁止直接实例化。
// 定义抽象复杂类型
static void addAbstractComplexType(EdmModel model)
{
EdmComplexType addressAbstract = new("Lesson10", "AddressAbstract", baseType: null, isAbstract: true);
model.AddElement(addressAbstract);
}
Model Meta中的表现
<ComplexType Name="AddressAbstract" Abstract="true" />
继承与多态
Non-Abstract Complex Type可以继承于Abstract Complex Type或者Non-Abstract Complex Type。
Abstract Complex Type 也可以继承于Abstract Complex Type或者Non-Abstract Complex Type。
Non-Abstract Complex Type继承于Abstract Complex Type
static void addDerivedNonAbstractComplexTypeFromAbstractComplexType(EdmModel model)
{
EdmComplexType derivedAddress = new("Lesson10", "DerivedAddressFromAbstract", baseType: model.FindType("Lesson10.AddressAbstract") as EdmComplexType, isAbstract: false);
derivedAddress.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String);
derivedAddress.AddStructuralProperty("City", EdmPrimitiveTypeKind.String);
model.AddElement(derivedAddress);
}
Model Meta中的表现
<ComplexType Name="DerivedAddressFromAbstract" BaseType="Lesson10.AddressAbstract">
<Property Name="Street" Type="Edm.String" />
<Property Name="City" Type="Edm.String" />
</ComplexType>
Non-Abstract Complex Type继承于Non-Abstract Complex Type
static void addDerivedNonAbstractComplexTypeFromNonAbstract(EdmModel model)
{
EdmComplexType derivedAddress = new("Lesson10", "PostalAddress", baseType : model.FindType("Lesson10.Address") as EdmComplexType);
derivedAddress.AddStructuralProperty("PostalCode", EdmPrimitiveTypeKind.String);
model.AddElement(derivedAddress);
}
Model Meta中的表现
<ComplexType Name="PostalAddress" BaseType="Lesson10.Address">
<Property Name="PostalCode" Type="Edm.String" />
</ComplexType>
Abstract Complex Type 可以继承于Abstract Complex Type
static void addDerivedAbstractComplexTypeFromAbstractComplexType(EdmModel model)
{
EdmComplexType derivedAddress = new("Lesson10", "DerivedAddress", baseType: model.FindType("Lesson10.AddressAbstract") as EdmComplexType, isAbstract: false);
derivedAddress.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String);
derivedAddress.AddStructuralProperty("City", EdmPrimitiveTypeKind.String);
model.AddElement(derivedAddress);
}
Model Meta中的表现
<ComplexType Name="DerivedAddress" BaseType="Lesson10.AddressAbstract">
<Property Name="Street" Type="Edm.String" />
<Property Name="City" Type="Edm.String" />
</ComplexType>
Abstract Complex Type 可以Non-Abstract Complex Type
static void addDerivedAbstractComplexTypeFromNonAbstractComplexType(EdmModel model)
{
EdmComplexType derivedAddress = new("Lesson10", "DerivedAddressFromNonAbstract", baseType: model.FindType("Lesson10.Address") as EdmComplexType, isAbstract: false);
derivedAddress.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String);
derivedAddress.AddStructuralProperty("City", EdmPrimitiveTypeKind.String);
model.AddElement(derivedAddress);
}
Model Meta中的表现
<ComplexType Name="DerivedAddressFromNonAbstract" BaseType="Lesson10.Address">
<Property Name="Street" Type="Edm.String" />
<Property Name="City" Type="Edm.String" />
</ComplexType>
实体类型(EntityType)
- 概念:实体类型是 OData 模型中最重要的概念之一,它表示具有唯一标识符的实体。实体类型可以包含多个结构属性和导航属性,用于描述实体的特征和关系。
- 应用场景:当需要表示具有独立存在意义的对象时,使用实体类型。例如,客户、订单、产品等。
- 特点:实体类型必须有一个或多个键属性,用于唯一标识实体。它可以与其他实体类型建立关联关系,通过导航属性进行访问。
- 与其他类型的区别:与复杂类型不同,实体类型有唯一标识符,可以独立存在。与枚举类型不同,实体类型包含多个结构属性和导航属性,而不是简单的常量值。
static void addNonAbstractEntityType(EdmModel model)
{
EdmEntityType customer = new("Lesson10", "Customer");
customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String);
var postalAddressType = model.FindType("Lesson10.PostalAddress") as IEdmComplexType;
if (postalAddressType != null)
{
customer.AddStructuralProperty("PostalAddress", new EdmComplexTypeReference(postalAddressType, true));
}
model.AddElement(customer);
}
Model Meta中的表现
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" />
<Property Name="Name" Type="Edm.String" />
<Property Name="PostalAddress" Type="Lesson10.PostalAddress" />
<NavigationProperty Name="Orders" Type="Collection(Lesson10.Order)" />
</EntityType>
抽象
抽象实体类型(Abstract EntityType) 是一种特殊的实体类型,其核心作用是定义一组派生实体的公共结构和行为,同时禁止直接实例化。
static void addAbstractEntityType(EdmModel model)
{
EdmEntityType orderAbstract = new("Lesson10", "OrderAbstract", baseType: null, isAbstract: true, isOpen: false);
orderAbstract.AddKeys(orderAbstract.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
model.AddElement(orderAbstract);
}
Model Meta中的表现
<EntityType Name="OrderAbstract" Abstract="true">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" />
</EntityType>
继承
Non-Abstract Entity Type可以继承于Abstract Entity Type或者Non-Abstract Entity Type。
Abstract Entity Type 也可以继承于Abstract Entity Type或者Non-Abstract Entity Type。
Non-Abstract Entity Type继承于Abstract Entity Type
static void addDerivedNonAbstractEntityTypeFromAbstractComplexType(EdmModel model)
{
EdmEntityType order = new("Lesson10", "OrderFromAbstract", baseType: model.FindType("Lesson10.OrderAbstract") as IEdmEntityType, isAbstract: false, isOpen: false);
order.AddStructuralProperty("Amount", EdmPrimitiveTypeKind.Decimal);
model.AddElement(order);
}
Model Meta中的表现
<EntityType Name="OrderFromAbstract" BaseType="Lesson10.OrderAbstract">
<Property Name="Amount" Type="Edm.Decimal" Scale="variable" />
</EntityType>
Non-Abstract Entity Type继承于Non-Abstract Entity Type
static void addDerivedNonAbstractEntityTypeFromNonAbstractEntityType(EdmModel model)
{
EdmEntityType vipCustomer = new("Lesson10", "VipCustomer", baseType: model.FindType("Lesson10.Customer") as IEdmEntityType);
vipCustomer.AddStructuralProperty("VipLevel", EdmPrimitiveTypeKind.String);
model.AddElement(vipCustomer);
}
Model Meta中的表现
<EntityType Name="VipCustomer" BaseType="Lesson10.Customer">
<Property Name="VipLevel" Type="Edm.String" />
</EntityType>
Abstract Entity Type 可以继承于Abstract Entity Type
static void addDerivedAbstractEntityTypeFromAbastractEntityType(EdmModel model)
{
EdmEntityType order = new("Lesson10", "Order", baseType: model.FindType("Lesson10.OrderAbstract") as IEdmEntityType, isAbstract: true, isOpen: false);
order.AddStructuralProperty("Amount", EdmPrimitiveTypeKind.Decimal);
model.AddElement(order);
}
Model Meta中的表现
<EntityType Name="Order" BaseType="Lesson10.OrderAbstract" Abstract="true">
<Property Name="Amount" Type="Edm.Decimal" Scale="variable" />
<NavigationProperty Name="Customer" Type="Lesson10.Customer" Nullable="false" />
</EntityType>
Abstract Entity Type继承于Non-Abstract Entity Type
static void addDerivedAbstractEntityTypeFromNonAbstractEntityType(EdmModel model)
{
EdmEntityType order = new("Lesson10", "OrderFromNonAbstract", baseType: model.FindType("Lesson10.Customer") as IEdmEntityType, isAbstract: false, isOpen: false);
order.AddStructuralProperty("Amount", EdmPrimitiveTypeKind.Decimal);
model.AddElement(order);
}
Model Meta中的表现
<EntityType Name="OrderFromNonAbstract" BaseType="Lesson10.Customer">
<Property Name="Amount" Type="Edm.Decimal" Scale="variable" />
</EntityType>
实体集合(Entity Set)
- 概念:同一实体类型的集合(类似数据库表),支持 CRUD 操作。
- 应用场景:多实例资源(用户列表、订单集合)。
- 特点:需键值访问单个实体,支持集合查询($filter)。
- 与其他类型的区别:与 EntityType 是 “集合 - 元素” 关系。
var container = model.EntityContainer as EdmEntityContainer;
var customerType = model.FindType("Lesson10.Customer") as IEdmEntityType;
if (customerType != null)
{
container.AddEntitySet("Customers", customerType);
}
Model Meta中的表现
<EntitySet Name="Customers" EntityType="Lesson10.Customer" />
单例(Singleton)
- 概念:全局唯一的单个实体,无需键值定位。
- 应用场景:唯一资源(系统配置、租户信息)。
- 特点:直接通过名称访问,不支持集合操作(如批量删除)。
- 与其他类型的区别:与 EntityType 是 “实例 - 类型” 关系。
var container = model.EntityContainer as EdmEntityContainer;
var singleCustomerType = model.FindType("Lesson10.SingleCustomer") as IEdmEntityType;
if (singleCustomerType != null)
{
container.AddSingleton("SingleCustomer", singleCustomerType);
}
Model Meta中的表现
<Singleton Name="SingleCustomer" Type="Lesson10.SingleCustomer" />
导航属性(NavigationProperty)
- 概念:导航属性用于表示实体之间的关联关系。通过导航属性,客户端可以在查询实体时同时获取相关联的实体数据。
- 应用场景:当实体之间存在关联关系时,使用导航属性可以方便地进行数据的查询和访问。例如,客户和订单之间的关系,一个客户可以有多个订单,一个订单只属于一个客户。
- 特点:导航属性可以是单向的或双向的,根据关联关系的性质而定。它可以指定目标实体的多重性,如一对一、一对多、多对多等。
- 与其他类型的区别:导航属性不是实体的结构属性,而是用于表示实体之间的关系。它不包含具体的数据,而是指向相关联的实体。
static void addNavigationProperty(EdmModel model)
{
EdmEntityType? customer = model.FindType("Lesson10.Customer") as EdmEntityType;
EdmEntityType? order = model.FindType("Lesson10.Order") as EdmEntityType;
if (customer != null && order != null)
{
customer.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo
{
Name = "Orders",
TargetMultiplicity = EdmMultiplicity.Many,
Target = order,
ContainsTarget = false
});
order.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo
{
Name = "Customer",
TargetMultiplicity = EdmMultiplicity.One,
Target = customer,
ContainsTarget = false
});
}
}
Model Meta中的表现
<EntityType Name="Customer">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" />
<Property Name="Name" Type="Edm.String" />
<Property Name="PostalAddress" Type="Lesson10.PostalAddress" />
<NavigationProperty Name="Orders" Type="Collection(Lesson10.Order)" />
</EntityType>
<EntityType Name="Order" BaseType="Lesson10.OrderAbstract" Abstract="true">
<Property Name="Amount" Type="Edm.Decimal" Scale="variable" />
<NavigationProperty Name="Customer" Type="Lesson10.Customer" Nullable="false" />
</EntityType>
函数(Function)
- 概念:函数是用于执行特定操作的 OData 元素,通常用于查询数据,返回一个结果;
- 应用场景:函数适用于需要根据特定条件查询数据的场景,如根据客户 ID 获取客户信息。
- 特点:函数可以是无绑定的或绑定的,无绑定函数可以直接调用,绑定函数需要绑定到某个实体或实体集上。
- 与其他类型的区别:函数与实体类型、复杂类型等不同,它不是用于表示数据结构,而是用于执行操作。Function(函数)主要用于执行查询操作并返回数据(如根据条件获取实体),而 Action(动作)主要用于执行命令式操作(如创建、更新、删除实体或触发业务逻辑),通常不直接返回查询结果但可能有执行状态反馈。
Unbound Function(无绑定函数)
- 定义:独立的服务操作,不绑定到特定实体或实体集。
- 调用方式:直接通过容器路径访问。
- 应用场景:全局查询、与实体无关的计算。
static void addUnboundFunction(EdmModel model)
{
EdmFunction function = new("Lesson10", "GetCustomerById", new EdmEntityTypeReference(model.FindType("Lesson10.Customer") as IEdmEntityType, true), isBound: false, entitySetPathExpression: null, isComposable: false);
function.AddParameter("key", EdmCoreModel.Instance.GetInt32(false));
model.AddElement(function);
}
Model Meta中的表现
<Function Name="GetCustomerById">
<Parameter Name="key" Type="Edm.Int32" Nullable="false" />
<ReturnType Type="Lesson10.Customer" />
</Function>
Bound Function(绑定函数)
- 定义:绑定到特定实体类型或实体集的函数。
- 调用方式:通过实体实例或实体集路径访问。
- 应用场景:基于特定实体的计算(如 Customer.GetOrdersByYear)。
static void addBoundFunction(EdmModel model)
{
EdmFunction function = new("Lesson10", "GetCustomerName", EdmCoreModel.Instance.GetString(false), isBound: true, entitySetPathExpression: null, isComposable: false);
function.AddParameter("customer", new EdmEntityTypeReference(model.FindType("Lesson10.Customer") as IEdmEntityType, false));
model.AddElement(function);
}
Model Meta中的表现
<Function Name="GetCustomerName" IsBound="true">
<Parameter Name="customer" Type="Lesson10.Customer" Nullable="false" />
<ReturnType Type="Edm.String" Nullable="false" />
</Function>
动作(Action)
- 概念:动作用于执行特定操作的 OData 元素,通常用于执行一些副作用操作,如更新、删除等。
- 应用场景:动作适用于需要执行一些业务逻辑的场景,如更新客户信息、删除订单等。
- 特点:动作可以是无绑定的或绑定的,并且可以有返回值或没有返回值。
- 与其他类型的区别:动作与实体类型、复杂类型等不同,它不是用于表示数据结构,而是用于执行操作。Function(函数)主要用于执行查询操作并返回数据(如根据条件获取实体),而 Action(动作)主要用于执行命令式操作(如创建、更新、删除实体或触发业务逻辑),通常不直接返回查询结果但可能有执行状态反馈。
Unbound Action(无绑定动作)
- 定义:独立的服务操作,不绑定到特定实体。
- 调用方式:直接通过容器路径访问。
- 应用场景:全局命令(如重置缓存、发送通知)。
static void addUnboundAction(EdmModel model)
{
EdmAction action = new("Lesson10", "UpdateCustomer", EdmCoreModel.Instance.GetString(false), isBound: false, entitySetPathExpression: null);
action.AddParameter("customer", new EdmEntityTypeReference(model.FindType("Lesson10.Customer") as IEdmEntityType, false));
model.AddElement(action);
}
Model Meta中的表现
<Action Name="UpdateCustomer">
<Parameter Name="customer" Type="Lesson10.Customer" Nullable="false" />
<ReturnType Type="Edm.String" Nullable="false" />
</Action>
Bound Action(绑定动作)
- 定义:绑定到特定实体类型或实体集的动作。
- 调用方式:通过实体实例路径访问。
- 应用场景:对特定实体执行操作(如 Customer.PlaceOrder)。
static void addBoundAction(EdmModel model)
{
EdmAction action = new("Lesson10", "UpdateCustomerName", EdmCoreModel.Instance.GetString(false), isBound: true, entitySetPathExpression: null);
action.AddParameter("customer", new EdmEntityTypeReference(model.FindType("Lesson10.Customer") as IEdmEntityType, false));
action.AddParameter("name", EdmCoreModel.Instance.GetString(false));
model.AddElement(action);
}
Model Meta中的表现
<Action Name="UpdateCustomerName" IsBound="true">
<Parameter Name="customer" Type="Lesson10.Customer" Nullable="false" />
<Parameter Name="name" Type="Edm.String" Nullable="false" />
<ReturnType Type="Edm.String" Nullable="false" />
</Action>
函数导入(FunctionImport)和动作导入(ActionImport)
- 概念:函数导入和动作导入是将函数和动作暴露给客户端的一种方式。通过函数导入和动作导入,客户端可以通过 URL 调用函数和动作。
- 应用场景:当需要将函数和动作提供给客户端使用时,使用函数导入和动作导入。例如,客户端可以通过调用函数导入获取特定客户的信息,通过调用动作导入更新客户信息。
- 特点:函数导入和动作导入需要添加到实体容器中,以便客户端可以通过 URL 访问。它们可以有参数和返回值,客户端可以在 URL 中传递参数。
- 与其他类型的区别:函数导入和动作导入是用于将函数和动作暴露给客户端的机制,与函数和动作本身不同,它们是在实体容器层面进行管理的。
static void addFunctionImport(EdmModel model)
{
var container = model.EntityContainer as EdmEntityContainer;
if (container != null)
{
var function = model.FindDeclaredOperations("Lesson10.GetCustomerById").FirstOrDefault() as IEdmFunction;
if (function != null)
{
container.AddFunctionImport("GetCustomerById", function);
}
}
}
static void addActionImport(EdmModel model)
{
var container = model.EntityContainer as EdmEntityContainer;
if (container != null)
{
var action = model.FindDeclaredOperations("Lesson10.UpdateCustomer").FirstOrDefault() as IEdmAction;
if (action != null)
{
container.AddActionImport("UpdateCustomer", action);
}
}
}
Model Meta中的表现
<FunctionImport Name="GetCustomerById" Function="Lesson10.GetCustomerById" />
<ActionImport Name="UpdateCustomer" Action="Lesson10.UpdateCustomer" />
实体容器(Container)
- 概念:在 OData 模型中,实体容器(Entity Container) 是模型的核心容器,用于封装实体集(Entity Sets)、函数导入(Function Imports)、动作导入(Action Imports)等运行时可访问的资源。当模型未显式指定容器名称时,OData 会自动创建一个默认容器。
- 应用场景:所有 OData 模型必须至少包含一个实体容器,默认容器是最常用的方式。
- 特点:集中管理可访问的实体集、函数、动作,确保客户端通过统一入口访问资源。
static void addDefaultContainer(EdmModel model)
{
EdmEntityContainer container = new("Lesson10", "DefaultContainer");
model.AddElement(container);
}
Model Meta中的表现
<FunctionImport Name="GetCustomerById" Function="Lesson10.GetCustomerById" />
<ActionImport Name="UpdateCustomer" Action="Lesson10.UpdateCustomer" />
<EntitySet Name="Customers" EntityType="Lesson10.Customer" />
<Singleton Name="SingleCustomer" Type="Lesson10.SingleCustomer" />
代码地址
https://github.com/f304646673/odata/tree/main/csharp/Lesson/Lesson10
1189

被折叠的 条评论
为什么被折叠?



