diff --git a/src/Codehard.Functional/CodeHard.Function.FSharp.Tests.Types/CodeHard.Functional.FSharp.Tests.Types.fsproj b/src/Codehard.Functional/CodeHard.Function.FSharp.Tests.Types/CodeHard.Functional.FSharp.Tests.Types.fsproj index 0bc9a20..17a7ad5 100644 --- a/src/Codehard.Functional/CodeHard.Function.FSharp.Tests.Types/CodeHard.Functional.FSharp.Tests.Types.fsproj +++ b/src/Codehard.Functional/CodeHard.Function.FSharp.Tests.Types/CodeHard.Functional.FSharp.Tests.Types.fsproj @@ -11,7 +11,7 @@ - + diff --git a/src/Codehard.Functional/Codehard.Functional.AspNetCore/ControllerExtensions.cs b/src/Codehard.Functional/Codehard.Functional.AspNetCore/ControllerExtensions.cs index 7e3392a..214bde3 100644 --- a/src/Codehard.Functional/Codehard.Functional.AspNetCore/ControllerExtensions.cs +++ b/src/Codehard.Functional/Codehard.Functional.AspNetCore/ControllerExtensions.cs @@ -111,9 +111,10 @@ public static IActionResult RunToResult( public static async Task RunToResultAsync( this Eff eff, HttpStatusCode successStatusCode = HttpStatusCode.OK, - ILogger? logger = default) + ILogger? logger = default, + CancellationToken cancellationToken = default) { - var fin = await eff.RunAsync(); + var fin = await eff.RunAsync(EnvIO.New(token: cancellationToken)); return fin.MatchToResult( diff --git a/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/Codehard.Functional.EntityFramework.Tests.csproj b/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/Codehard.Functional.EntityFramework.Tests.csproj index 4a14b63..a56fc53 100644 --- a/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/Codehard.Functional.EntityFramework.Tests.csproj +++ b/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/Codehard.Functional.EntityFramework.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/QueryableExtensionsTests.cs b/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/QueryableExtensionsTests.cs index ddaf2bd..7e10bab 100644 --- a/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/QueryableExtensionsTests.cs +++ b/src/Codehard.Functional/Codehard.Functional.EntityFramework.Tests/QueryableExtensionsTests.cs @@ -5,6 +5,7 @@ using Codehard.Functional.EntityFramework.Tests.Entities; using Codehard.Infrastructure.EntityFramework.Extensions; +using static Codehard.Functional.EntityFramework.Extensions.QueryablePrelude; namespace Codehard.Functional.EntityFramework.Tests; @@ -18,7 +19,7 @@ private static SqliteConnection CreateInMemoryDatabase() } [Fact] - public async Task ToListEffRt_ShouldReturnList() + public async Task ToListEff_ShouldReturnList() { // Arrange var assembly = Assembly.GetExecutingAssembly(); @@ -29,6 +30,7 @@ public async Task ToListEffRt_ShouldReturnList() await using var context = new TestDbContext( options, builder => builder.ApplyConfigurationsFromAssemblyFor(assembly)); + await context.Database.EnsureCreatedAsync(); var entityId = Guid.NewGuid(); @@ -56,4 +58,44 @@ public async Task ToListEffRt_ShouldReturnList() Assert.NotNull(resultEntity); Assert.Single(resultEntity); } + + [Fact] + public async Task FilterRtAndToListEff_ShouldReturnList() + { + // Arrange + var assembly = Assembly.GetExecutingAssembly(); + var options = new DbContextOptionsBuilder() + .UseSqlite(CreateInMemoryDatabase()) + .Options; + + await using var context = new TestDbContext( + options, + builder => builder.ApplyConfigurationsFromAssemblyFor(assembly)); + + await context.Database.EnsureCreatedAsync(); + + var entityId = Guid.NewGuid(); + var entity = new EntityA + { + Id = entityId, + }; + + context.As.Add(entity); + await context.SaveChangesAsync(); + + // Act + var workflow = + from @as in From() + from list in + @as.Where(a => a.Id == entityId) + .ToListEff() + select list; + + // Assert + var listResult = await workflow.RunAsync(context); + var resultEntity = listResult.ThrowIfFail(); + + Assert.NotNull(resultEntity); + Assert.Single(resultEntity); + } } diff --git a/src/Codehard.Functional/Codehard.Functional.EntityFramework/Codehard.Functional.EntityFramework.csproj b/src/Codehard.Functional/Codehard.Functional.EntityFramework/Codehard.Functional.EntityFramework.csproj index e7b5870..e8a1031 100644 --- a/src/Codehard.Functional/Codehard.Functional.EntityFramework/Codehard.Functional.EntityFramework.csproj +++ b/src/Codehard.Functional/Codehard.Functional.EntityFramework/Codehard.Functional.EntityFramework.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryableExtensions.cs b/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryableExtensions.cs index bbc100c..eaa7254 100644 --- a/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryableExtensions.cs +++ b/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryableExtensions.cs @@ -1,6 +1,5 @@ using System.Linq.Expressions; using LanguageExt; - using static LanguageExt.Prelude; // ReSharper disable once CheckNamespace diff --git a/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryablePrelude.cs b/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryablePrelude.cs new file mode 100644 index 0000000..faa9577 --- /dev/null +++ b/src/Codehard.Functional/Codehard.Functional.EntityFramework/Extensions/QueryablePrelude.cs @@ -0,0 +1,63 @@ +using System.Linq.Expressions; +using LanguageExt; +using Microsoft.EntityFrameworkCore; + +using Codehard.Common.DomainModel; + +using static LanguageExt.Prelude; + +namespace Codehard.Functional.EntityFramework.Extensions; + +public static class QueryablePrelude +{ + /// + /// Asynchronously converts a sequence to a list within an Eff monad. + /// + /// The type of the elements of the source sequence. + /// + /// An Eff monad that represents the asynchronous operation. The Eff monad wraps a that contains elements from the input sequence. + /// + public static Eff, List> ToListEff() + { + return + LanguageExt.Eff, List>.LiftIO( + static source => + liftIO(env => source.ToListAsync(env.Token))); + } + + /// + /// Asynchronously converts a sequence to an array within an Eff monad. + /// + /// The type of the elements of the source sequence. + /// + /// An Eff monad that represents the asynchronous operation. The Eff monad wraps an array that contains elements from the input sequence. + /// + public static Eff, T[]> ToArrayEff() + { + return + LanguageExt.Eff, T[]>.LiftIO( + static source => + liftIO(env => source.ToArrayAsync(env.Token))); + } + + public static Eff> From() + where T : class + { + return + LanguageExt.Eff>.Lift( + dbContext => dbContext.Set().AsQueryable()); + } + + public static Eff, Option> FindByKey( + TKey key) + where TEntity : class, IEntity + where TKey : struct + { + return + LanguageExt.Eff, Option>.LiftIO( + source => + liftIO(async env => Optional(await source.FindAsync( + new object[] { key }, + cancellationToken: env.Token)))); + } +} \ No newline at end of file diff --git a/src/Codehard.Functional/Codehard.Functional.FSharp/Codehard.Functional.FSharp.csproj b/src/Codehard.Functional/Codehard.Functional.FSharp/Codehard.Functional.FSharp.csproj index b98e4a2..809a987 100644 --- a/src/Codehard.Functional/Codehard.Functional.FSharp/Codehard.Functional.FSharp.csproj +++ b/src/Codehard.Functional/Codehard.Functional.FSharp/Codehard.Functional.FSharp.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/Codehard.Functional/Codehard.Functional.Marten/Codehard.Functional.Marten.csproj b/src/Codehard.Functional/Codehard.Functional.Marten/Codehard.Functional.Marten.csproj index 5b37860..bf62988 100644 --- a/src/Codehard.Functional/Codehard.Functional.Marten/Codehard.Functional.Marten.csproj +++ b/src/Codehard.Functional/Codehard.Functional.Marten/Codehard.Functional.Marten.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Codehard.Functional/Codehard.Functional.MediatR/Codehard.Functional.MediatR.csproj b/src/Codehard.Functional/Codehard.Functional.MediatR/Codehard.Functional.MediatR.csproj index 406fadb..a7677b7 100644 --- a/src/Codehard.Functional/Codehard.Functional.MediatR/Codehard.Functional.MediatR.csproj +++ b/src/Codehard.Functional/Codehard.Functional.MediatR/Codehard.Functional.MediatR.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Codehard.Functional/Codehard.Functional.Tests/AffGuardExtTests.cs b/src/Codehard.Functional/Codehard.Functional.Tests/AffGuardExtTests.cs deleted file mode 100644 index b12ad4e..0000000 --- a/src/Codehard.Functional/Codehard.Functional.Tests/AffGuardExtTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -using static Codehard.Functional.Prelude; - -namespace Codehard.Functional.Tests; - -public class AffGuardExtTests -{ - [Fact] - public async Task WhenGuardNoneOnSomeValue_ShouldStayOnSuccCase() - { - // Act - var eff = - EffOption(() => ValueTask.FromResult((int?)1)) - .GuardNotNone("There is something wrong"); - - var fin = await eff.RunAsync(); - - // Assert - var a = fin.ThrowIfFail(); - - Assert.Equal(1, a); - } - - [Fact] - public async Task WhenGuardNoneOnNullValue_ShouldGoToFailCase() - { - // Act - var eff = - EffOption(() => ValueTask.FromResult((int?)null)) - .GuardNotNone("There is no value"); - - var fin = await eff.RunAsync(); - - // Assert - Assert.False(fin.IsSucc); - } -} \ No newline at end of file diff --git a/src/Codehard.Functional/Codehard.Functional.Tests/EffGuardExtTests.cs b/src/Codehard.Functional/Codehard.Functional.Tests/EffGuardExtTests.cs index eef3d1c..065354a 100644 --- a/src/Codehard.Functional/Codehard.Functional.Tests/EffGuardExtTests.cs +++ b/src/Codehard.Functional/Codehard.Functional.Tests/EffGuardExtTests.cs @@ -33,4 +33,34 @@ public void WhenGuardNoneOnNullValue_ShouldGoToFailCase() // Assert Assert.False(fin.IsSucc); } + + [Fact] + public async Task WhenGuardNoneOnSomeValueAsync_ShouldStayOnSuccCase() + { + // Act + var eff = + EffOption(() => ValueTask.FromResult((int?)1)) + .GuardNotNone("There is something wrong"); + + var fin = await eff.RunAsync(); + + // Assert + var a = fin.ThrowIfFail(); + + Assert.Equal(1, a); + } + + [Fact] + public async Task WhenGuardNoneOnNullValueAsync_ShouldGoToFailCase() + { + // Act + var eff = + EffOption(() => ValueTask.FromResult((int?)null)) + .GuardNotNone("There is no value"); + + var fin = await eff.RunAsync(); + + // Assert + Assert.False(fin.IsSucc); + } } \ No newline at end of file diff --git a/src/Codehard.Functional/Codehard.Functional/Codehard.Functional.csproj b/src/Codehard.Functional/Codehard.Functional/Codehard.Functional.csproj index 510bfc4..30a7c22 100644 --- a/src/Codehard.Functional/Codehard.Functional/Codehard.Functional.csproj +++ b/src/Codehard.Functional/Codehard.Functional/Codehard.Functional.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Codehard.Infrastructure/Codehard.Infrastructure.EntityFramework.Tests/ModelBuilderExtensionsTests.cs b/src/Codehard.Infrastructure/Codehard.Infrastructure.EntityFramework.Tests/ModelBuilderExtensionsTests.cs index 99bb9e4..e02b9f0 100644 --- a/src/Codehard.Infrastructure/Codehard.Infrastructure.EntityFramework.Tests/ModelBuilderExtensionsTests.cs +++ b/src/Codehard.Infrastructure/Codehard.Infrastructure.EntityFramework.Tests/ModelBuilderExtensionsTests.cs @@ -35,6 +35,8 @@ public void WhenUseApplyConfigurationsFromAssemblyForSpecificContext_ShouldApply // +2 because of the Money type and the Nullable Money type Assert.Equal(expectedEntityTypes + 3, actualEntityTypes); + + return; static SqliteConnection CreateInMemoryDatabase() {