Method Event
Summary
Define Post and Undo post on the Order entity. Add the code that runs them in a Logic class. Core creates API endpoints. Client buttons use the server's labels and rules.
The examples use the C# type DocsOrder, displayed as Order.
See the complete Order example for all models, logic, and client code.
Defining Methods
[Method("Post", MethodType.Modify, SaveOnCompletion = true)]
public partial void Post();
[Method("Undo post", MethodType.Modify, SaveOnCompletion = true)]
public partial void UndoPost();
Add these methods to the partial entity class. SaveOnCompletion saves after all handlers finish.
MethodType
| Type | HTTP | Use |
|---|---|---|
Read |
GET | Return data without changing stored records. |
Modify |
POST | Change data. |
Parameter rules
Non-nullable parameters without defaults are required. Nullable parameters and parameters with defaults are optional.
Missing required parameters return 400 BadRequest.
Implementing Methods
Instance method
Register the Order handlers in its Logic class:
[Logic]
public class OrderBL(DocsOrder.Logic order)
{
[RegisterLogic]
public void Posting()
{
order.Method(o => o.Post)
.DisableIf(o => o.IsPosted)
.Do(o => o.IsPosted = true)
.OnSuccess("Order posted.");
order.Method(o => o.UndoPost)
.DisableIf(o => !o.IsPosted)
.Do(o => o.IsPosted = false);
}
}
Users can run Post on drafts and UndoPost on posted Orders.
The API does not allow direct edits to IsPosted. Clients must use these methods.
The complete example also makes Description and Amount read-only after posting.
Success messages
Put OnSuccess after Do. Core sends the message after the handlers and any SaveOnCompletion save succeed.
The client button displays it automatically.

To include the Order number, use a message function:
order.Method(o => o.Post)
.Do(o => o.IsPosted = true)
.OnSuccess(o => $"Order {o.DocNumber} posted.");
Use this instead of the fixed message above.
When success messages appear
Disabled, aborted, or failed methods and failed saves show no success message. Validation questions delay it. Accepting shows it; rejecting cancels it. The method does not run again.
OnSuccess supports instance Read and Modify methods, including void methods. Static methods are not supported.
Core does not add a default success message.
Inside a workflow, a method may succeed before the workflow is saved.
When does it fire?
A method runs when called from code or through its API endpoint.
The client can run it with <MethodButton Method="Post" /> inside a DataGraph.
See Method Buttons and Split Buttons.
Scenarios
Use these patterns as needed.
1. Method that changes data (POST)
The Order's Post is a Modify method. Call it on a saved Order:
POST /api/DocsOrder({orderId})/Post
Pass Modify parameters in a JSON body. For example, public partial void SetNote(string note) receives:
POST /api/DocsOrder({orderId})/SetNote
Content-Type: application/json
{ "note": "Deliver before noon" }
Its handler reads the typed parameter as args.note.
SetNote illustrates parameter syntax; the demo uses a Change note dialog.
2. Method that only reads data (GET)
A Read method can calculate a value. Add this optional method to DocsOrder:
[Method("Amount with tax", MethodType.Read)]
public partial decimal AmountWithTax(decimal taxRate);
Register it in a [RegisterLogic] method:
order.Method(o => o.AmountWithTax).Do((o, args) => o.Amount * (1 + args.taxRate));
GET /api/DocsOrder({orderId})/AmountWithTax()?taxRate=0.08
3. Static method to query open orders (GET)
A static method runs without a current record:
[Method("Open orders", MethodType.Read)]
public static partial IQueryable<DocsOrder> GetOpenOrders();
Static method
Register a static method without an entity parameter:
order.Method(_ => DocsOrder.GetOpenOrders)
.Do(args => args.GetEntities<DocsOrder>().Where(o => !o.IsPosted));
Entity collections support normal OData options such as $select, $expand, and $filter:
GET /api/DocsOrder/GetOpenOrders()?$select=Guid,DocNumber,Description,Amount
4. Parameterless method
Read routes require (), even with no inputs. Modify routes do not need parentheses.
The demo's ChangeNote Read method returns a virtual dialog entity:
GET /api/DocsOrder({orderId})/ChangeNote()
See Workflow Dialogs for its declaration and handler.
5. Computing a property when a method executes
For a stored IsPosted property without a setter, compute its value from the method that marked it dirty:
[ApiEntity]
public partial class Journal
{
[Property<DataTypes.Boolean>("Is posted")]
public partial bool IsPosted { get; }
[Method("Post", MethodType.Modify)]
public partial void Post();
[Method("Undo post", MethodType.Modify)]
public partial void UndoPost();
}
[Logic]
public class JournalBL(Journal.Logic journal)
{
[RegisterLogic]
public void Posting()
{
journal.Compute(j => j.IsPosted)
.From((j, args) => args.PropertyState.DirtiedBy switch
{
nameof(Journal.Post) => true,
nameof(Journal.UndoPost) => false,
_ => args.PropertyState.ValidValue,
})
.DirtyByMethod(j => j.Post)
.DirtyByMethod(j => j.UndoPost);
journal.Method(j => j.Post).Do(_ => { });
journal.Method(j => j.UndoPost).Do(_ => { });
}
}
Keep ValidValue when neither method caused the update. Empty handlers make the methods available; Compute changes the status.
See Dirty from a method for timing and notifications.
6. Disabling a method with DisableIf (Disabled UI button)
order.Method(o => o.Post)
.DisableIf(o => o.IsPosted)
.Do(o => o.IsPosted = true);
Use DisableIf to prevent users from clicking the method.
Any active true condition disables the method. Keep conditions fast; Core checks them on each client request.
Disabled names appear in RecordReadOnly. A MethodButton stays visible but disabled;
a SplitButton hides the unavailable action. See Read-only.
7. Aborting a method with AbortIf
Use AbortIf to stop a method and explain why when it is called.
For example, replace DisableIf with:
order.Method(o => o.Post)
.AbortIf(o => o.IsPosted)
.WithMessage(o => $"Order {o.DocNumber} is already posted.")
.Do(o => o.IsPosted = true);
Asking the user instead of aborting
Use AskUser to confirm before running the handler, such as posting an archived Order:
order.Method(o => o.Post)
.AbortIf(o => o.IsArchived)
.AskUser("Post this archived order?", acceptButton: "Post", rejectButton: "Cancel")
.Do(o => o.IsPosted = true);
Inside a workflow, Core keeps the arguments and waits for acceptance. Outside a workflow, it uses the default answer. See Questions.
8. Aborting during method execution
AbortIf and DisableIf run before the handlers.
If a handler changes data, use UndoOnAbort to say how Core should undo that change:
order.Method(o => o.Post).Do((o, args) =>
{
var previousStatus = o.IsPosted;
o.IsPosted = true;
args.UndoOnAbort(record => record.IsPosted = previousStatus);
});
A later handler can call args.AbortWithMessage(...) to stop the method.
Core skips the remaining handlers and runs undo handlers in reverse order.
Changes without UndoOnAbort stay pending. Put each undo beside the change it reverses.
9. Save on completion
Set SaveOnCompletion = true on [Method] to save after the handlers finish.
Do not also call args.Context.SaveChanges() in a handler.
Core checks errors and required properties, runs the enabled handlers, then saves once.
If a handler or save fails, Core runs the registered undo handlers.
10. Subscribers and features
Each handler is a subscriber. It and its rules run only when the tenant has that feature enabled. The handler can read that feature's properties and entities. The last enabled handler supplies the return value.
If all handlers belong to unavailable features, Core reports that the method is unavailable. If no handlers are registered, Core reports a configuration error.
Controlling execution
- DisableIf disables the button and prevents the method from running.
- AbortIf stops the method with an explanation.
- UndoOnAbort undoes registered changes if the method aborts or its final save fails.
OData API Routes
| Method type | HTTP | Route |
|---|---|---|
| Instance Read | GET | /api/{Entity}({id})/MethodName() |
| Instance Modify | POST | /api/{Entity}({id})/MethodName |
| Static Read | GET | /api/{Entity}/MethodName() |
| Static Modify | POST | /api/{Entity}/MethodName |
GET parameters use the query string. POST parameters use a JSON body.
Notes
argsexposes typed parameters,GetService<T>(), andGetEntities<T>().- Read methods should not change stored records.
- Core generates API endpoints from method declarations.
- For application notifications after instance methods finish, see Method listeners.