Skip to content

Commit

Permalink
Implementations, tests and design adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
tsutomi committed Nov 19, 2022
1 parent 3a1b047 commit e6a9232
Show file tree
Hide file tree
Showing 81 changed files with 3,678 additions and 2,241 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"dotnet-test-explorer.testProjectPath": "**/*.sln"
}
32 changes: 32 additions & 0 deletions src/Deveel.Repository.Core/Data/ExpressionFieldRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Net;
using System;
using System.Linq.Expressions;

namespace Deveel.Data
{
/// <summary>
/// References a expr of an entity through a selection expression
/// </summary>
/// <typeparam name="TEntity">The type of the entity defining the expr to be selected</typeparam>
public sealed record class ExpressionFieldRef<TEntity> : IFieldRef where TEntity : class, IEntity
{
/// <summary>
/// Constucts the reference with the expression to select
/// the expr from the entity
/// </summary>
/// <param name="expr">The expression that is used to select the expr</param>
/// <exception cref="ArgumentNullException">
/// Thrown if the expression is empty
/// </exception>
public ExpressionFieldRef(Expression<Func<TEntity, object>> expr)
{
Expression = expr ?? throw new ArgumentNullException(nameof(expr));
}

/// <summary>
/// Gets the expression used to select a field from the
/// underlying entity
/// </summary>
public Expression<Func<TEntity, object>> Expression { get; }
}
}
23 changes: 23 additions & 0 deletions src/Deveel.Repository.Core/Data/ExpressionQueryFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Linq.Expressions;

namespace Deveel.Data
{
/// <summary>
/// An implementation of a query expr that uses a lambda expression
/// </summary>
/// <typeparam name="TEntity">The type of entity to construct
/// the expr</typeparam>
public sealed class ExpressionQueryFilter<TEntity> : IQueryFilter where TEntity : class, IEntity
{
public ExpressionQueryFilter(Expression<Func<TEntity, bool>> expr)
{
Expression = expr ?? throw new ArgumentNullException(nameof(expr));
}

/// <summary>
/// Gets the filter expression
/// </summary>
public Expression<Func<TEntity, bool>> Expression { get; }
}
}
125 changes: 125 additions & 0 deletions src/Deveel.Repository.Core/Data/FacadeRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;

namespace Deveel.Data
{
class FacadeRepository<TEntity, TFacade> : IRepository<TFacade>
where TEntity : class, IEntity, TFacade
where TFacade : class, IEntity
{
private readonly IRepository<TEntity> repository;

public FacadeRepository(IRepository<TEntity> repository)
{
this.repository = repository;
}

public bool SupportsPaging => repository.SupportsPaging;

public bool SupportsFilters => repository.SupportsFilters;

Type IRepository.EntityType => typeof(TFacade);

private TEntity Assert(TFacade entity)
{
if (!(entity is TEntity other))
throw new ArgumentException($"The object of type '{typeof(TFacade)}' cannot be converted to '{typeof(TEntity)}'");

return other;
}

private TEntity Assert(object entity)
{
if (!(entity is TEntity other))
throw new ArgumentException($"The object cannot be converted to '{typeof(TEntity)}'");

return other;
}

public Task<string> CreateAsync(TFacade entity, CancellationToken cancellationToken)
=> repository.CreateAsync(Assert(entity), cancellationToken);

public Task<string> CreateAsync(IDataTransaction session, TFacade entity, CancellationToken cancellationToken)
=> repository.CreateAsync(session, Assert(entity), cancellationToken);

public Task<string> CreateAsync(IEntity entity, CancellationToken cancellationToken)
=> repository.CreateAsync(Assert(entity), cancellationToken);

public Task<string> CreateAsync(IDataTransaction session, IEntity entity, CancellationToken cancellationToken)
=> repository.CreateAsync(session, Assert(entity), cancellationToken);

public Task<IList<string>> CreateAsync(IEnumerable<IEntity> entities, CancellationToken cancellationToken = default)
=> repository.CreateAsync(entities.Select(Assert), cancellationToken);

public Task<IList<string>> CreateAsync(IDataTransaction transaction, IEnumerable<IEntity> entities, CancellationToken cancellationToken = default)
=> repository.CreateAsync(transaction, entities.Select(Assert), cancellationToken);

public Task<IList<string>> CreateAsync(IEnumerable<TFacade> entities, CancellationToken cancellationToken = default)
=> repository.CreateAsync(entities.Select(Assert), cancellationToken);

public Task<IList<string>> CreateAsync(IDataTransaction transaction, IEnumerable<TFacade> entities, CancellationToken cancellationToken = default)
=> repository.CreateAsync(transaction, entities.Select(Assert), cancellationToken);

public Task<bool> DeleteAsync(TFacade entity, CancellationToken cancellationToken)
=> repository.DeleteAsync(Assert(entity), cancellationToken);

public Task<bool> DeleteAsync(IDataTransaction session, TFacade entity, CancellationToken cancellationToken)
=> repository.DeleteAsync(session, Assert(entity), cancellationToken);

public Task<bool> DeleteAsync(IEntity entity, CancellationToken cancellationToken)
=> repository.DeleteAsync(Assert(entity), cancellationToken);

public Task<bool> DeleteAsync(IDataTransaction session, IEntity entity, CancellationToken cancellationToken)
=> repository.DeleteAsync(session, Assert(entity), cancellationToken);

public async Task<TFacade?> FindByIdAsync(string id, CancellationToken cancellationToken)
=> await repository.FindByIdAsync(id, cancellationToken);

public Task<bool> UpdateAsync(TFacade entity, CancellationToken cancellationToken)
=> repository.UpdateAsync(Assert(entity), cancellationToken);

public Task<bool> UpdateAsync(IDataTransaction session, TFacade entity, CancellationToken cancellationToken)
=> repository.UpdateAsync(session, Assert(entity), cancellationToken);

public Task<bool> UpdateAsync(IEntity entity, CancellationToken cancellationToken)
=> repository.UpdateAsync(Assert(entity), cancellationToken);

public Task<bool> UpdateAsync(IDataTransaction session, IEntity entity, CancellationToken cancellationToken)
=> repository.UpdateAsync(session, Assert(entity), cancellationToken);

async Task<IEntity?> IRepository.FindByIdAsync(string id, CancellationToken cancellationToken)
=> await repository.FindByIdAsync(id, cancellationToken);

async Task<PaginatedResult> IRepository.GetPageAsync(PageRequest page, CancellationToken cancellationToken)
=> await repository.GetPageAsync(page, cancellationToken);

public Task<PaginatedResult<TFacade>> GetPageAsync(PageRequest<TFacade> request, CancellationToken cancellationToken = default)
=> throw new NotImplementedException();

public async Task<TFacade?> FindAsync(IQueryFilter filter, CancellationToken cancellationToken)
=> await repository.FindAsync(filter, cancellationToken);

async Task<IEntity?> IRepository.FindAsync(IQueryFilter filter, CancellationToken cancellationToken)
=> await repository.FindAsync(filter, cancellationToken);

public async Task<IList<TFacade>> FindAllAsync(IQueryFilter filter, CancellationToken cancellationToken = default)
{
var result = await repository.FindAllAsync(filter, cancellationToken);
return result.Cast<TFacade>().ToList();
}

public Task<bool> ExistsAsync(IQueryFilter filter, CancellationToken cancellationToken = default)
=> repository.ExistsAsync(filter, cancellationToken);

public Task<long> CountAsync(IQueryFilter filter, CancellationToken cancellationToken = default)
=> repository.CountAsync(filter, cancellationToken);

async Task<IList<IEntity>> IRepository.FindAllAsync(IQueryFilter filter, CancellationToken cancellationToken)
{
var result = await FindAllAsync(filter, cancellationToken);
return result.Cast<IEntity>().ToList();
}

Task<IEntity?> IRepository.FindByIdAsync(IDataTransaction transaction, string id, CancellationToken cancellationToken)
=> repository.FindByIdAsync(transaction, id, cancellationToken);
}
}
20 changes: 20 additions & 0 deletions src/Deveel.Repository.Core/Data/FacadeRepositoryProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Deveel.Data
{
class FacadeRepositoryProvider<TEntity, TFacade> : IRepositoryProvider<TFacade>
where TEntity : class, TFacade, IEntity
where TFacade : class, IEntity
{
private readonly IRepositoryProvider<TEntity> provider;

public FacadeRepositoryProvider(IRepositoryProvider<TEntity> provider)
{
this.provider = provider;
}

public IRepository<TFacade> GetRepository(string tenantId) => (IRepository<TFacade>)provider.GetRepository(tenantId);

IRepository IRepositoryProvider.GetRepository(string tenantId) => provider.GetRepository(tenantId);
}
}
9 changes: 9 additions & 0 deletions src/Deveel.Repository.Core/Data/IConvertibleQueryFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace Deveel.Data
{
public interface IConvertibleQueryFilter : IQueryFilter
{
IQueryFilter ConvertFor<TEntity>();
}
}
18 changes: 18 additions & 0 deletions src/Deveel.Repository.Core/Data/IDataTransaction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace Deveel.Data
{
/// <summary>
/// Represents a transaction provided by the underlying
/// storage of the repository, to isolate operations
/// of access to the data
/// </summary>
public interface IDataTransaction : IDisposable
{
Task BeginAsync(CancellationToken cancellationToken = default);

Task CommitAsync(CancellationToken cancellationToken = default);

Task RollbackAsync(CancellationToken cancellationToken = default);
}
}
20 changes: 20 additions & 0 deletions src/Deveel.Repository.Core/Data/IDataTransactionFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Deveel.Data {
/// <summary>
/// A factory that provides instances of transactions
/// used to isolate the access to data layers of an underlying storage
/// </summary>
public interface IDataTransactionFactory {
/// <summary>
/// Creates a new stransaction and starts it
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>
/// Returns a new instance of <see cref="IDataTransaction"/> that
/// can be used in a <see cref="IRepository"/> to isolate
/// the access to the data of an underlying storage
/// </returns>
Task<IDataTransaction> CreateTransactionAsync(CancellationToken cancellationToken = default);
}
}
12 changes: 12 additions & 0 deletions src/Deveel.Repository.Core/Data/IFieldRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Deveel.Data
{
/// <summary>
/// A marker interface that is implemented by objects referencing
/// a field of an entity
/// </summary>
public interface IFieldRef
{
}
}
12 changes: 12 additions & 0 deletions src/Deveel.Repository.Core/Data/IQueryFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Deveel.Data
{
/// <summary>
/// A marker interface that is implemented by objects
/// representing filters of a query to a repository
/// </summary>
public interface IQueryFilter
{
}
}
9 changes: 9 additions & 0 deletions src/Deveel.Repository.Core/Data/IQueryableRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace Deveel.Data
{
public interface IQueryableRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
IQueryable<TEntity> AsQueryable();
}
}
Loading

0 comments on commit e6a9232

Please sign in to comment.