Skip to content

Commit

Permalink
readme
Browse files Browse the repository at this point in the history
  • Loading branch information
pimbrouwers committed Dec 11, 2024
1 parent 2b92095 commit ee9b83e
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 47 deletions.
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ Danom is a C# library that provides (monadic) structures to facilitate durable p

- Implementation of common monads: [Option](#option) and [Result](#result).
- Exhaustive matching to prevent null reference exceptions.
- Fluent API for chaining operations.
- Fluent API for chaining operations, including async support.
- Integrated with [ASP.NET Core](#aspnet-core-mvc-integration) and [Fluent Validation](#fluent-validation-integration).
- API for [parsing strings](#string-parsing) into .NET primitives and value types.
- Supports asynchronous operations.

## Design Goals

- Easy to use API for common monadic operations.
- Efficient implementation to minimize overhead.
- Seamless integration with existing C# code and libraries.
- Provide a safe and expressive way to handle nullable values.
- Prevent direct use of internal value, enforcing exhaustive matching.
- Efficient implementation to minimize overhead.
- Opionated monads to encourage consistent use.

## Getting Started

Expand Down Expand Up @@ -200,6 +199,8 @@ Result<int, string> resultOrElseWith =
Result<int, string>.Ok(99)); // useful if creating the value is expensive
```

### Result Errors

Since error messages are frequently represented as keyed string collections, the `ResultErrors` type is provided to simplify Result creation. The flexible constructor allows errors to be initialized with a single string, a collection of strings, or a key-value pair.

```csharp
Expand Down Expand Up @@ -315,7 +316,7 @@ Since Danom introduces types that are most commonly found in your model and busi

### Fluent Validation Integration

Danom is integrated with [Fluent Validation](https://fluentvalidation.net/) to provide a seamless way to validate your models and return a `Result` or `ResultOption` with the validation errors.
[Fluent Validation](https://fluentvalidation.net/) is an excellent library for building validation rules for your models. A first-class integration is available via [Danom.Validation](src/Danom.Validation/README.md) to provide a seamless way to validate your models and return a `Result` or `ResultOption` with the validation errors.

A quick example:

Expand Down Expand Up @@ -353,9 +354,7 @@ Documentation can be found [here](src/Danom.Validation/README.md).

### ASP.NET Core MVC Integration

Danom is integrated with [ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-5.0) to provide a set of utilities to help integrate the Danom library with common tasks in ASP.NET Core MVC applications.

Documentation can be found [here](src/Danom.Mvc/README.md).
Danom is integrated with ASP.NET Core via [Danom.Mvc](src/Danom.Mvc/README.md). This library provides a set of utilities to help integrate the core types with common tasks in ASP.NET Core MVC applications.

### ASP.NET Core Minimal API Integration

Expand Down
130 changes: 92 additions & 38 deletions src/Danom.Mvc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[![NuGet Version](https://img.shields.io/nuget/v/Danom.Mvc.svg)](https://www.nuget.org/packages/Danom.Mvc)
[![build](https://github.com/pimbrouwers/Danom/actions/workflows/build.yml/badge.svg)](https://github.com/pimbrouwers/Danom/actions/workflows/build.yml)

Danom.Mvc is a library that provides a set of rendering utilities to help integrate the [Danom](../../README.md) library with common tasks in ASP.NET Core MVC applications.
Danom.Mvc is a library that provides a set of utilities to help integrate the [Danom](../../README.md) library with common tasks in ASP.NET Core MVC applications.

## Getting Started

Expand All @@ -17,7 +17,20 @@ Or using the dotnet CLI
dotnet add package Danom.Mvc
```

## Working With `Option`
## Controller

The `DanomController` class extends the base controller class to provide a set of methods to help work with `Result`, `Option`, and `ResultOption` types in ASP.NET Core MVC applications.


### Option

The `ViewOption` method is used to render a view based on the presence of an `Option` value.

If the `Option` is `Some`, the view is rendered with the value. If the `Option` is `None`, the `noneAction` is invoked. By default, the `noneAction` returns a `NotFound` result.

A custom view name can be provided to render a view with a different name than the action.

Some examples demonstrating the use of `ViewOption` are shown below:

```csharp
using Danom.Mvc;
Expand All @@ -26,29 +39,32 @@ using Microsoft.AspNetCore.Mvc;
public sealed class OptionController
: DanomController
{
private readonly Option<string> _someOption = Option.Some("Hello world");
private readonly Option<string> _noneOption = Option<string>.None();

public IActionResult OptionSome() =>
ViewOption(
option: _someOption,
option: Option.Some("Hello world"),
viewName: "Detail");

// Returns the ASP.NET default `NotFound` result
public IActionResult OptionNone() =>
ViewOption(
option: _noneOption,
option: Option<string>.NoneValue,
viewName: "Detail");

public IActionResult OptionNoneCustom() =>
ViewOption(
option: _noneOption,
option: Option<string>.NoneValue,
viewName: "Detail",
noneAction: () => NotFound("Not found!"));
}
```

## Working with `Result`
### Result

The `ViewResult` method is used to render a view based on the presence of a `Result` value.

By default the `ViewResult` method will render the view with the value if the `Result` is `Ok`. If the `Result` is `Error`, the `errorAction` is invoked.

A custom view name can be provided to render a view with a different name than the action.

```csharp
using Danom.Mvc;
Expand All @@ -57,58 +73,96 @@ using Microsoft.AspNetCore.Mvc;
public sealed class ResultController
: DanomController
{
private readonly Result<string, ResultErrors> _okResult = Result.Ok("Success!");
private readonly Result<string, ResultErrors> _errorResult = Result<string>.Error(["An error occurred."]);
private readonly Result<string, string> _stringErrorResult = Result<string, string>.Error("An error occurred.");

public IActionResult ResultOk() =>
ViewResult(
result: _okResult,
result: Result<string, string>.Ok("Success!"),
viewName: "Detail");

public IActionResult ResultErrors() =>
ViewResult(
result: _errorResult,
viewName: "Detail");

// Demonstrating error customization, using a string literal for error output
public IActionResult ResultError() =>
ViewResult(
result: _stringErrorResult,
result: Result<string, string>.Error("An error occurred."),
errorAction: errors => View("Detail", errors),
viewName: "Detail");

}
```

## Working with `ResultOption`
Built into Danom is the `ResultErrors` type, which is particularly well suited for reporting model errors in ASP.NET Core MVC applications. The `ViewResultErrors` method, provided by the `DanomController` class, is a proxy for the `View` method that will inject the `ResultErrors` value into the model state.

When using `ResultErrors` as the error type, the `ViewResultErrors` will default the `errorAction` to inject the `ResultErrors` value into the model state.

```csharp
using Danom.Mvc;
using Microsoft.AspNetCore.Mvc;

public sealed class ResultOptionController
public sealed class ResultController
: DanomController
{
private readonly ResultOption<string, ResultErrors> _okResult = ResultOption.Ok("Success!");
private readonly ResultOption<string, ResultErrors> _errorResult = ResultOption<string>.Error("An error occurred.");
private readonly ResultOption<string, string> _stringErrorResult = ResultOption<string, string>.Error("An error occurred.");

public IActionResult ResultOk() =>
ViewResultOption(
resultOption: _okResult,
viewName: "Detail");

public IActionResult ResultErrors() =>
ViewResultOption(
resultOption: _errorResult,
ViewResult(
// notice the lack of second type parameter, which is inferred to be ResultErrors
result: Result<string>.Ok("Success!"),
viewName: "Detail");

// Demonstrating error customization, using a string literal for error output
public IActionResult ResultError() =>
ViewResultOption(
resultOption: _stringErrorResult,
errorAction: errors => View("Detail", errors),
ViewResult(
result: Result<string>.Error("An error occurred."),
viewName: "Detail");


public IActionResult ResultErrorView() =>
// can be used directly
ViewResultErrors(
errors: new("An error occurred."));
}
```

## View

### Option

While not explicitly part of the `Danom.Mvc` library, there are some patterns that make rendering the `Option` type easier in Razor views. Two methods from the base library are especially valuable: `TryGet` and `ToString`.

The `TryGet` method is used to extract the value from an `Option` type. If the `Option` is `Some`, the value is assigned to the `out` parameter and the method returns `true`. If the `Option` is `None`, the method returns `false`.

The custom `ToString` method is used to convert the `Option` value to a string. If the `Option` is `Some`, the value is converted to a string. If the `Option` is `None`, the method returns the default value. The method optionally accepts a second parameter for the format string.

Consider the following type:

```csharp
public record Person(
string Name,
Option<DateOnly> Birthdate,
Option<string> Email);
```

The `TryGet` and `ToString` methods can be used in a Razor view to help render the optional properties.

```cshtml
@model Person
<h1>@Model.Name</h1>
<h2>Email: <i>@Model.Email.ToString("-")</i></h2>
@if (Model.Birthdate.TryGet(out var birthdate))
{
var now = DateTime.Now;
var a = (now.Year * 100 + now.Month) * 100 + now.Day;
var b = (birthdate.Year * 100 + birthdate.Month) * 100 + birthdate.Day;
var age = (a - b) / 10000;
<p>You are born on @birthdate, and are @age years old.</p>
}
else
{
<p>You are an ageless wonder!</p>
}
```

## Find a bug?

There's an [issue](https://github.com/pimbrouwers/Danom/issues) for that.

## License

Built with ♥ by [Pim Brouwers](https://github.com/pimbrouwers) in Toronto, ON. Licensed under [Apache License 2.0](https://github.com/pimbrouwers/Danom/blob/master/LICENSE).

0 comments on commit ee9b83e

Please sign in to comment.