Skip to content

Commit

Permalink
Use cancellation token from EnvIO
Browse files Browse the repository at this point in the history
  • Loading branch information
rungwiroon committed Oct 16, 2024
1 parent 85e6983 commit f2c047f
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Reflection;
using LanguageExt;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

using Codehard.Functional.EntityFramework.Tests.Entities;
using Codehard.Infrastructure.EntityFramework.Extensions;

namespace Codehard.Functional.EntityFramework.Tests;

public class QueryableExtensionsTests
{
private static SqliteConnection CreateInMemoryDatabase()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
return connection;
}

[Fact]
public async Task ToListEffRt_ShouldReturnList()
{
// Arrange
var assembly = Assembly.GetExecutingAssembly();
var options = new DbContextOptionsBuilder<TestDbContext>()
.UseSqlite(CreateInMemoryDatabase())
.Options;

await using var context = new TestDbContext(
options,
builder => builder.ApplyConfigurationsFromAssemblyFor<TestDbContext>(assembly));
await context.Database.EnsureCreatedAsync();

var entityId = Guid.NewGuid();
var entity = new EntityA
{
Id = entityId,
};

context.As.Add(entity);
await context.SaveChangesAsync();

var queryable = context.As.AsQueryable();

// Act
var result =
queryable
.ToListEff()
.RunAsync(EnvIO.New(token: CancellationToken.None));

// Assert
var list = await result;

var resultEntity = list.ThrowIfFail();

Assert.NotNull(resultEntity);
Assert.Single(resultEntity);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Linq.Expressions;
using LanguageExt;
using LanguageExt.Traits;

using static LanguageExt.Prelude;

Expand All @@ -18,58 +17,25 @@ public static class QueryableExtensions
/// </summary>
/// <typeparam name="T">The type of the elements of the source sequence.</typeparam>
/// <param name="source">An <see cref="IQueryable{T}"/> to create a list from.</param>
/// <param name="ct">The <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>
/// An Eff monad that represents the asynchronous operation. The Eff monad wraps a <see cref="List{T}"/> that contains elements from the input sequence.
/// </returns>
public static Eff<List<T>> ToListEff<T>(
this IQueryable<T> source, CancellationToken ct = default)
public static Eff<List<T>> ToListEff<T>(this IQueryable<T> source)
{
return liftEff(() => source.ToListAsync(ct));
return liftIO(env => source.ToListAsync(env.Token));
}

/// <summary>
/// Asynchronously converts an IQueryable&lt;T&gt; into a list within a K monad.
/// </summary>
/// <typeparam name="RT">The runtime environment type that implements Readable&lt;RT, EnvIO&gt; and Monad&lt;RT&gt;.</typeparam>
/// <typeparam name="T">The type of elements in the IQueryable.</typeparam>
/// <param name="source">The IQueryable to be converted to a list.</param>
/// <returns>
/// A K&lt;RT, List&lt;T&gt;&gt; representing the asynchronous operation.
/// The K monad wraps the result, which is the list of elements.
/// </returns>
public static K<RT, List<T>> ToListEffRt<RT, T>(
this IQueryable<T> source)
where RT : Readable<RT, EnvIO>, Monad<RT>
{
return
from env in Readable.ask<RT, EnvIO>()
from io in liftIO(() => source.ToListAsync(env.Token))
select io;
}

// public static ReaderT<RT, IO, List<T>> ToListRT2<RT, T>()
// where RT : Readable<RT, QueryableEnv<T>>, Monad<RT>
// {
// return
// from env in Readable.ask<RT, QueryableEnv<T>>()
// from io in Eff<QueryableEnv<T>, List<T>>.Lift(env => env.Queryable.ToListAsync(env.CancellationToken))
// select io;
// }

/// <summary>
/// Asynchronously converts a sequence to an array within an Eff monad.
/// </summary>
/// <typeparam name="T">The type of the elements of the source sequence.</typeparam>
/// <param name="source">An <see cref="IQueryable{T}"/> to create an array from.</param>
/// <param name="ct">The <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>
/// An Eff monad that represents the asynchronous operation. The Eff monad wraps an array that contains elements from the input sequence.
/// </returns>
public static Eff<T[]> ToArrayEff<T>(
this IQueryable<T> source, CancellationToken ct = default)
public static Eff<T[]> ToArrayEff<T>(this IQueryable<T> source)
{
return liftEff(() => source.ToArrayAsync(ct));
return liftIO(env => source.ToArrayAsync(env.Token));
}

/// <summary>
Expand All @@ -81,10 +47,9 @@ public static Eff<T[]> ToArrayEff<T>(
/// <returns>
/// An Eff monad that represents the asynchronous operation. The Eff monad wraps a boolean value that is true if the source sequence contains any elements; otherwise, false.
/// </returns>
public static Eff<bool> AnyEff<T>(
this IQueryable<T> source, CancellationToken ct = default)
public static Eff<bool> AnyEff<T>(this IQueryable<T> source)
{
return liftEff(() => source.AnyAsync(ct));
return liftIO(env => source.AnyAsync(env.Token));
}

/// <summary>
Expand Down Expand Up @@ -135,10 +100,9 @@ public static Task<Option<TSource>> SingleOrNoneAsync<TSource>(
/// The Eff monad wraps the result, which is an Option&lt;TSource&gt; containing the only element of the sequence, or a None value if the sequence is empty.
/// </returns>
public static Eff<Option<TSource>> SingleOrNoneEff<TSource>(
this IQueryable<TSource> source,
CancellationToken ct = default)
this IQueryable<TSource> source)
{
return liftEff(() => source.SingleOrNoneAsync(ct));
return liftIO(env => source.SingleOrNoneAsync(env.Token));
}

/// <summary>
Expand Down Expand Up @@ -179,12 +143,11 @@ public static Task<Option<TSource>> SingleOrNoneAsync<TSource>(
/// </returns>
public static Eff<Option<TSource>> SingleOrNoneEff<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
CancellationToken ct = default)
Expression<Func<TSource, bool>> predicate)
{
return
liftEff(async () =>
await source.SingleOrNoneAsync(predicate, ct));
liftIO(async (env) =>
await source.SingleOrNoneAsync(predicate, env.Token));
}

/// <summary>
Expand All @@ -201,12 +164,11 @@ public static Eff<Option<TSource>> SingleOrNoneEff<TSource>(
/// </returns>
public static Eff<TSource> SingleOrFailEff<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
CancellationToken ct = default)
Expression<Func<TSource, bool>> predicate)
{
return
liftEff(async () =>
await source.SingleAsync(predicate, ct));
liftIO(async env =>
await source.SingleAsync(predicate, env.Token));
}

/// <summary>
Expand Down Expand Up @@ -239,10 +201,9 @@ public static Task<Option<T>> FirstOrNoneAsync<T>(
/// The Eff monad wraps the result, which contains the first element of the sequence as an Option&lt;T&gt;,
/// or a None value if the sequence is empty.
/// </returns>
public static Eff<Option<T>> FirstOrNoneEff<T>(
this IQueryable<T> source, CancellationToken ct = default)
public static Eff<Option<T>> FirstOrNoneEff<T>(this IQueryable<T> source)
{
return liftEff(() => source.FirstOrNoneAsync(ct));
return liftIO(env => source.FirstOrNoneAsync(env.Token));
}

/// <summary>
Expand Down Expand Up @@ -283,10 +244,9 @@ public static Task<Option<T>> FirstOrNoneAsync<T>(
/// </returns>
public static Eff<Option<T>> FirstOrNoneEff<T>(
this IQueryable<T> source,
Expression<Func<T, bool>> predicate,
CancellationToken ct = default)
Expression<Func<T, bool>> predicate)
{
return liftEff(() => source.FirstOrNoneAsync(predicate, ct));
return liftIO(env => source.FirstOrNoneAsync(predicate, env.Token));
}

/// <summary>
Expand All @@ -298,10 +258,9 @@ public static Eff<Option<T>> FirstOrNoneEff<T>(
/// <returns>
/// An Eff monad that represents the asynchronous operation. The Eff monad wraps an integer that represents the number of elements in the input sequence.
/// </returns>
public static Eff<int> CountEff<T>(
this IQueryable<T> source, CancellationToken ct = default)
public static Eff<int> CountEff<T>(this IQueryable<T> source)
{
return liftEff(() => source.CountAsync(ct));
return liftIO(env => source.CountAsync(env.Token));
}

/// <summary>
Expand All @@ -316,10 +275,9 @@ public static Eff<int> CountEff<T>(
/// </returns>
public static Eff<int> CountEff<T>(
this IQueryable<T> source,
Expression<Func<T, bool>> predicate,
CancellationToken ct = default)
Expression<Func<T, bool>> predicate)
{
return liftEff(() => source.CountAsync(predicate, ct));
return liftIO(env => source.CountAsync(predicate, env.Token));
}

/// <summary>
Expand All @@ -332,10 +290,9 @@ public static Eff<int> CountEff<T>(
/// An Eff monad that represents the asynchronous operation. The Eff monad wraps a long integer that represents the number of elements in the input sequence.
/// </returns>
public static Eff<long> LongCountEff<T>(
this IQueryable<T> source,
CancellationToken ct = default)
this IQueryable<T> source)
{
return liftEff(() => source.LongCountAsync(ct));
return liftIO(env => source.LongCountAsync(env.Token));
}

/// <summary>
Expand All @@ -353,6 +310,6 @@ public static Eff<long> LongCountEff<T>(
Expression<Func<T, bool>> predicate,
CancellationToken ct = default)
{
return liftEff(() => source.LongCountAsync(predicate, ct));
return liftIO(env => source.LongCountAsync(predicate, env.Token));
}
}

0 comments on commit f2c047f

Please sign in to comment.