In this page, we introduce the Function/Action parameter in untyped scenario. For CLR typed scenarios, please refer to Function page and Action page.

Build Edm Model

Let’s build the Edm Model from scratch:

private static IEdmModel GetEdmModel()
{
    EdmModel model = new EdmModel();

    // Enum type "Color"
    EdmEnumType colorEnum = new EdmEnumType("NS", "Color");
    colorEnum.AddMember(new EdmEnumMember(colorEnum, "Red", new EdmIntegerConstant(0)));
    colorEnum.AddMember(new EdmEnumMember(colorEnum, "Blue", new EdmIntegerConstant(1)));
    colorEnum.AddMember(new EdmEnumMember(colorEnum, "Green", new EdmIntegerConstant(2)));
    model.AddElement(colorEnum);

    // complex type "Address"
    EdmComplexType address = new EdmComplexType("NS", "Address");
    address.AddStructuralProperty("Street", EdmPrimitiveTypeKind.String);
    address.AddStructuralProperty("City", EdmPrimitiveTypeKind.String);
    model.AddElement(address);

    // derived complex type "SubAddress"
    EdmComplexType subAddress = new EdmComplexType("NS", "SubAddress", address);
    subAddress.AddStructuralProperty("Code", EdmPrimitiveTypeKind.Double);
    model.AddElement(subAddress);

    // entity type "Customer"
    EdmEntityType customer = new EdmEntityType("NS", "Customer");
    customer.AddKeys(customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32));
    customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String);
    model.AddElement(customer);

    // derived entity type special customer
    EdmEntityType subCustomer = new EdmEntityType("NS", "SubCustomer", customer);
    subCustomer.AddStructuralProperty("Price", EdmPrimitiveTypeKind.Double);
    model.AddElement(subCustomer);

    // entity sets
    EdmEntityContainer container = new EdmEntityContainer("NS", "Default");
    model.AddElement(container);
    container.AddEntitySet("Customers", customer);

    IEdmTypeReference intType = EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Int32, isNullable: true);
    EdmEnumTypeReference enumType = new EdmEnumTypeReference(colorEnum, isNullable: true);
    EdmComplexTypeReference complexType = new EdmComplexTypeReference(address, isNullable: true);
    EdmEntityTypeReference entityType = new EdmEntityTypeReference(customer, isNullable: true);

    // functions
    BuildFunction(model, "PrimitiveFunction", entityType, "param", intType);
    BuildFunction(model, "EnumFunction", entityType, "color", enumType);
    BuildFunction(model, "ComplexFunction", entityType, "address", complexType);
    BuildFunction(model, "EntityFunction", entityType, "customer", entityType);
    
    // actions
    BuildAction(model, "PrimitiveAction", entityType, "param", intType);
    BuildAction(model, "EnumAction", entityType, "color", enumType);
    BuildAction(model, "ComplexAction", entityType, "address", complexType);
    BuildAction(model, "EntityAction", entityType, "customer", entityType);
    return model;
}

private static void BuildFunction(EdmModel model, string funcName, IEdmEntityTypeReference bindingType, string paramName, IEdmTypeReference edmType)
{
    IEdmTypeReference returnType = EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Boolean, isNullable: false);

    EdmFunction boundFunction = new EdmFunction("NS", funcName, returnType, isBound: true, entitySetPathExpression: null, isComposable: false);
    boundFunction.AddParameter("entity", bindingType);
    boundFunction.AddParameter(paramName, edmType);
    boundFunction.AddParameter(paramName + "List", new EdmCollectionTypeReference(new EdmCollectionType(edmType)));
    model.AddElement(boundFunction);
}

private static void BuildAction(EdmModel model, string actName, IEdmEntityTypeReference bindingType, string paramName, IEdmTypeReference edmType)
{
    IEdmTypeReference returnType = EdmCoreModel.Instance.GetPrimitive(EdmPrimitiveTypeKind.Boolean, isNullable: false);

    EdmAction boundAction = new EdmAction("NS", actName, returnType, isBound: true, entitySetPathExpression: null);
    boundAction.AddParameter("entity", bindingType);
    boundAction.AddParameter(paramName, edmType);
    boundAction.AddParameter(paramName + "List", new EdmCollectionTypeReference(new EdmCollectionType(edmType)));
    model.AddElement(boundAction);
}

Here’s the metadata document for this Edm Model:

<?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="NS" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="Color">
        <Member Name="Red" Value="0" />
        <Member Name="Blue" Value="1" />
        <Member Name="Green" Value="2" />
      </EnumType>
      <ComplexType Name="Address">
        <Property Name="Street" Type="Edm.String" />
        <Property Name="City" Type="Edm.String" />
      </ComplexType>
      <ComplexType Name="SubAddress" BaseType="NS.Address">
        <Property Name="Code" Type="Edm.Double" />
      </ComplexType>
      <EntityType Name="Customer">
        <Key>
          <PropertyRef Name="Id" />
        </Key>
        <Property Name="Id" Type="Edm.Int32" />
        <Property Name="Name" Type="Edm.String" />
      </EntityType>
      <EntityType Name="SubCustomer" BaseType="NS.Customer">
        <Property Name="Price" Type="Edm.Double" />
      </EntityType>
      <Function Name="PrimitiveFunction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="param" Type="Edm.Int32" />
        <Parameter Name="paramList" Type="Collection(Edm.Int32)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Function>
      <Function Name="EnumFunction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="color" Type="NS.Address" />
        <Parameter Name="colorList" Type="Collection(NS.Color)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Function>
      <Function Name="ComplexFunction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="address" Type="NS.Address" />
        <Parameter Name="addressList" Type="Collection(NS.Address)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Function>
      <Function Name="EntityFunction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="customer" Type="NS.Color" />
        <Parameter Name="customerList" Type="Collection(NS.Customer)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Function>
      <Action Name="PrimitiveAction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="param" Type="Edm.Int32" />
        <Parameter Name="paramList" Type="Collection(Edm.Int32)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Action>
      <Action Name="EnumAction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="color" Type="NS.Address" />
        <Parameter Name="colorList" Type="Collection(NS.Color)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Action>
      <Action Name="ComplexAction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="address" Type="NS.Address" />
        <Parameter Name="addressList" Type="Collection(NS.Address)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Action>
      <Action Name="EntityAction" IsBound="true">
        <Parameter Name="entity" Type="NS.Customer" />
        <Parameter Name="customer" Type="NS.Color" />
        <Parameter Name="customerList" Type="Collection(NS.Customer)" />
        <ReturnType Type="Edm.Boolean" Nullable="false" />
      </Action>
      <EntityContainer Name="Default">
        <EntitySet Name="Customers" EntityType="NS.Customer" />
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

Controller & Routing

Let’s add the following methods into CustomersController:

[HttpGet]
public IHttpActionResult PrimitiveFunction(int key, int? param, [FromODataUri]IList<int?> paramList)
{
    ......
}

[HttpPost]
public IHttpActionResult PrimitiveAction(int key, ODataActionParameters parameters)
{
    ......
}

/* // will support in V5.5 RTM
[HttpGet]
public IHttpActionResult EnumFunction(int key, [FromODataUri]EdmEnumObject color, [FromODataUri]EdmEnumObjectCollection colorList)
{
    ......
}

[HttpPost]
public IHttpActionResult EnumAction(int key, ODataActionParameters parameters)
{
    ......
}
*/

[HttpGet]
public IHttpActionResult ComplexFunction(int key, [FromODataUri]EdmComplexObject address, [FromODataUri]EdmComplexObjectCollection addressList)
{
    ......
}

[HttpPost]
public IHttpActionResult ComplexAction(int key, ODataActionParameters parameters)
{
    ......
}

[HttpGet]
public IHttpActionResult EntityFunction(int key, [FromODataUri]EdmEntityObject customer, [FromODataUri]EdmEntityObjectCollection customerList)
{
    ......
}

[HttpPost]
public IHttpActionResult EntityAction(int key, ODataActionParameters parameters)
{
    ......
}

Request Samples

Now, We can invoke the function with the entity and collection of entity parameter as:

~odata/Customers(1)/NS.EntityFunction(customer=@x,customerList=@y)?@x={\"@odata.type\":\"%23NS.Customer\",\"Id\":1,\"Name\":\"John\"}&@y={\"value\":[{\"@odata.type\":\"%23NS.Customer\",\"Id\":2, \"Name\":\"Mike\"},{\"@odata.type\":\"%23NS.SubCustomer\",\"Id\":3,\"Name\":\"Tony\", \"Price\":9.9}]}"

Also, We can invoke the action by issuing a Post on ~/odata/Customers(1)/NS.EntityAction with the following request body:

{
  "customer":{\"@odata.type\":\"#NS.Customer\",\"Id\":1,\"Name\":\"John\"},
  "customerList":[
    {\"@odata.type\":\"#NS.Customer\",\"Id\":2, \"Name\":\"Mike\"},
    {\"@odata.type\":\"#NS.SubCustomer\",\"Id\":3,\"Name\":\"Tony\", \"Price\":9.9}
  ]
}

For other request samples, please refer to Function page and Action page.

Unbound function/action

Unbound function and action are similiar with bound function and action in the request format. But only attribute routing can be used for unbound function/action routing.

Thanks.