diff --git a/samples/Controllers/OperationResults.Sample/Controllers/PeopleController.cs b/samples/Controllers/OperationResults.Sample/Controllers/PeopleController.cs index 5f86a0a..c6b4f59 100644 --- a/samples/Controllers/OperationResults.Sample/Controllers/PeopleController.cs +++ b/samples/Controllers/OperationResults.Sample/Controllers/PeopleController.cs @@ -34,6 +34,18 @@ public async Task GetPerson(Guid id) return response; } + [HttpGet("{id:guid}/with-image", Name = nameof(GetPersonWithImage))] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(PersonWithImage))] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetPersonWithImage(Guid id) + { + // You can collapse the following instructions into a single one. + var result = await peopleService.GetWithImageAsync(id); + + var response = HttpContext.CreateResponse(result); // Or result.ToResponse(HttpContext) + return response; + } + [HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] diff --git a/samples/MinimalApis/OperationResults.Sample/Program.cs b/samples/MinimalApis/OperationResults.Sample/Program.cs index 94b78d2..3dd8d63 100644 --- a/samples/MinimalApis/OperationResults.Sample/Program.cs +++ b/samples/MinimalApis/OperationResults.Sample/Program.cs @@ -74,6 +74,19 @@ .WithName("GetPerson") .WithOpenApi(); +peopleApi.MapGet("{id:guid}/with-image", async (Guid id, IPeopleService peopleService, HttpContext httpContext) => +{ + // You can collapse the following instructions into a single one. + var result = await peopleService.GetWithImageAsync(id); + + var response = httpContext.CreateResponse(result); // Or result.ToResponse(httpContext) + return response; +}) +.Produces() +.Produces(StatusCodes.Status404NotFound) +.WithName("GetPersonWithImage") +.WithOpenApi(); + peopleApi.MapPost("/", async (Person person, IPeopleService peopleService, HttpContext httpContext) => { // You can collapse the following instructions into a single one. diff --git a/samples/OperationResults.Sample.BusinessLayer/Services/Interfaces/IPeopleService.cs b/samples/OperationResults.Sample.BusinessLayer/Services/Interfaces/IPeopleService.cs index 5ae25f9..088e34b 100644 --- a/samples/OperationResults.Sample.BusinessLayer/Services/Interfaces/IPeopleService.cs +++ b/samples/OperationResults.Sample.BusinessLayer/Services/Interfaces/IPeopleService.cs @@ -8,6 +8,8 @@ public interface IPeopleService Task> GetAsync(Guid id); + Task> GetWithImageAsync(Guid id); + Task> SaveAsync(Person person); Task DeleteAsync(Guid id); diff --git a/samples/OperationResults.Sample.BusinessLayer/Services/PeopleService.cs b/samples/OperationResults.Sample.BusinessLayer/Services/PeopleService.cs index 48492aa..8915bcb 100644 --- a/samples/OperationResults.Sample.BusinessLayer/Services/PeopleService.cs +++ b/samples/OperationResults.Sample.BusinessLayer/Services/PeopleService.cs @@ -6,7 +6,7 @@ namespace OperationResults.Sample.BusinessLayer.Services; -public class PeopleService(ApplicationDbContext dbContext) : IPeopleService +public class PeopleService(ApplicationDbContext dbContext, IImageService imageService) : IPeopleService { public async Task>> GetAsync() { @@ -45,6 +45,38 @@ public async Task> GetAsync(Guid id) return person; } + public async Task> GetWithImageAsync(Guid id) + { + var personResult = await GetAsync(id); + if (!personResult) // A shortcut to !personResult.Success + { + // The person operation failed + return Result.Fail(personResult.FailureReason); + } + + var person = personResult.Content!; + var imageResult = await imageService.GetImageAsync(); + if (imageResult.TryGet(out var imageFileContent) && imageFileContent is not null) + { + // The image operation succeeded, return person with image + var personWithImage = new PersonWithImage + { + Person = person, + Image = imageFileContent.Content + }; + + return personWithImage; + } + + // The image operation failed, return person without image + var personWithoutImage = new PersonWithImage + { + Person = person + }; + + return personWithoutImage; + } + public async Task> SaveAsync(Person person) { var dbPerson = person.Id != Guid.Empty ? await dbContext.People diff --git a/samples/OperationResults.Sample.Shared/Models/PersonWithImage.cs b/samples/OperationResults.Sample.Shared/Models/PersonWithImage.cs new file mode 100644 index 0000000..21ee318 --- /dev/null +++ b/samples/OperationResults.Sample.Shared/Models/PersonWithImage.cs @@ -0,0 +1,7 @@ +namespace OperationResults.Sample.Shared.Models; + +public class PersonWithImage +{ + public Person Person { get; set; } + public byte[] Image { get; set; } +} \ No newline at end of file diff --git a/src/OperationResults/IGenericResult{OfT}.cs b/src/OperationResults/IGenericResult{OfT}.cs index de6f195..af0dfc7 100644 --- a/src/OperationResults/IGenericResult{OfT}.cs +++ b/src/OperationResults/IGenericResult{OfT}.cs @@ -3,4 +3,6 @@ namespace OperationResults; public interface IGenericResult : IGenericResult { public T? Content { get; } + + public bool TryGet(out T? value); } diff --git a/src/OperationResults/Result.cs b/src/OperationResults/Result.cs index cf30074..73a1a8e 100644 --- a/src/OperationResults/Result.cs +++ b/src/OperationResults/Result.cs @@ -52,4 +52,13 @@ public static Result Fail(int failureReason, string message, string detail, IEnu public static Result Fail(int failureReason, Exception? error, IEnumerable? validationErrors = null) => new(false, failureReason: failureReason, error: error, validationErrors: validationErrors); + + public static bool operator true(Result result) + => result.Success; + + public static bool operator false(Result result) + => !result.Success; + + public static implicit operator bool(Result result) + => result.Success; } diff --git a/src/OperationResults/Result{OfT}.cs b/src/OperationResults/Result{OfT}.cs index 60e4272..f8df882 100644 --- a/src/OperationResults/Result{OfT}.cs +++ b/src/OperationResults/Result{OfT}.cs @@ -29,6 +29,12 @@ internal Result(bool success = true, T? content = default, int failureReason = F ValidationErrors = validationErrors; } + public bool TryGet(out T? value) + { + value = Content; + return Success; + } + public static Result Ok(T? content = default) => new(success: true, content); @@ -67,4 +73,13 @@ public static Result Fail(int failureReason, Exception? error, IEnumerable(Result result) => new(result.Success, default, result.FailureReason, result.ErrorMessage, result.ErrorDetail, result.Error, result.ValidationErrors); + + public static bool operator true(Result result) + => result.Success; + + public static bool operator false(Result result) + => !result.Success; + + public static implicit operator bool(Result result) + => result.Success; }