OData Actions in .NET Core
Tags: dotnet, webdev, api, csharp
Published: 2020-04-01 20:04:07

---
title: OData Actions in .NET Core
published: true
date: 2020-03-30 06:00:00 UTC
tags: dotnet, webdev, api, csharp
canonical_url: https://wellsb.com/csharp/aspnet/odata-actions-in-dotnet-core/
---
[Original Article](https://wellsb.com/csharp/aspnet/odata-actions-in-dotnet-core/)

In the previous tutorial, you learned how to [create a function in an OData API](https://wellsb.com/csharp/aspnet/odata-api-multiple-parameters/). Such a function could accept multiple parameters and return relevant data corresponding to those parameters. An OData action, on the other hand, is better suited for modifying a database entity. In this tutorial, you will learn how to create and configure an OData action.

## Create OData Action

Whereas an OData function is generally associated with an Http GET request, an Action can be associated with a POST request. For this example, you will build off the previous tutorial where you created a project to track your favorite open source contributors. In that tutorial, you created a standard GET method to return a specific project by its Project Id. Then you used a special OData function to add a GET endpoint `ByProjectByContributor` to filter and return only a project’s pull requests that were made by a specific contributor.

Recall, we are using the following model for the Project entity in this example.

```csharp
public class Project
{
	public int Id { get; set; }
	public string Title { get; set; }
	public List PullRequests {get; set; }
}
```

Now, suppose you want to change the Title of a given project. This is a trivial example, as you could simply `PUT` or `PATCH` a specific project with key `Id`. For example, `PUT: api/projects(5)`

Instead, we will create an OData action with its own endpoint that is designed to update the Title of a specific project. Let’s create an action with the following endpoint:

```
POST: api/projects(5)/SetTitle
```

Your controller action may look like the following

```csharp
// POST: api/projects(5)/SetTitle       
[HttpPost]
[ODataRoute("({id})/SetTitle")]
public async Task<IActionResult> SetTitle([FromODataUri] int id, ODataActionParameters parameters)
{
    if (!ModelState.IsValid)
    {
        return BadRequest();
    }

    string newtitle = (int)parameters["Title"];            
 
    var project = await _context.Projects
        .Where(p => p.Id == id)
        .FirstOrDefaultAsync();
 
    project.Title = newTitle;
	
	_context.Update(project);
	await _context.SaveChangesAsync();
}
```

Of course, you would want to enclose the `SaveChangesAsync()` command in a try-catch block in order to catch any `DbUpdateConcurrencyException` exceptions.

## Register OData Action

To use your new OData action, you must first register it in your EDM model builder, just like you did with the OData functions. If you are using [endpoint routing](https://wellsb.com/csharp/aspnet/odata-endpoint-routing-net-core/) and the `GetEdmModel()` method from [the previous tutorial](https://wellsb.com/csharp/aspnet/odata-api-multiple-parameters/), bind your action to the Project entity type and register the Title parameter.

```csharp
private IEdmModel GetEdmModel()
{
    var builder = new ODataConventionModelBuilder();
    builder.EntitySet<Project>("Projects");
	
	...
 
    builder.EntityType<Project>()
        .Action("SetTitle")
		.Parameter("Title").Required();
		
	...
 
    return builder.GetEdmModel();
}
```

## Make Call to OData Action

You might be wondering how to pass the `parameters` to the controller action. For OData actions, all parameters are passed in the payload of the POST request as Json. For example:

```
POST: api/projects(5)/SetTitle

{
   "Title": "Super Cool C# Project"
}
```

This can be made easier by [creating a service class](https://wellsb.com/csharp/aspnet/blazor-httpclientfactory-and-web-api/) with a method that handles the Json encoding for you. Such a method might look like:

```csharp
public class ApiService
{
	public HttpClient _httpClient;

	public ApiService(HttpClient client)
	{
		_httpClient = client;
	}
	
	...
	
	public async Task<HttpResponseMessage> SetTitle(int projectId, string newTitle)
	{
		TitleModel title = new TitleModel() { Title = newTitle };
		string json = JsonSerializer.Serialize(title);
		var stringContent = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

		return await _httpClient.PostAsync($"api/projects({projectId})/SetTitle", stringContent);            
	}
	
	...
}

```

Where the TitleModel object is derived from a class that contains any parameters you want to pass to the controller action.

```csharp
public class TitleModel
{
    public string Title { get; set; }
}
```

Once you register the service, you can inject it into a Blazor page, view, or even a controller as a dependency and invoke the `SetTitle()` method.

## Why Use OData Actions?

Again, this is a somewhat trivial example of how to use an OData controller action. In real-world usage, your action may include complex logic and calculations. It can even be used to modify multiple entities across different entity types. For example, suppose you want to become GDPR compliant and allow a user to delete all their data that is stored on your server? You could create an OData action to generate a list of entities they control and remove them by issuing one simple API call.

As you can see, OData actions are a powerful tool to add to your API building toolbelt. Let me know how you plan to use OData actions, and, as always, if you have any questions or thoughts, let me know in the comments below.

[Source](https://wellsb.com/csharp/aspnet/odata-actions-in-dotnet-core/)