Skip to content

Commit

Permalink
Merge pull request #1548 from riganti/feature/new-datasets
Browse files Browse the repository at this point in the history
Refactoring of GridViewDataSet
  • Loading branch information
tomasherceg authored Jul 15, 2024
2 parents ad4cd72 + d209e2a commit 1c9119b
Show file tree
Hide file tree
Showing 112 changed files with 4,132 additions and 701 deletions.
2 changes: 2 additions & 0 deletions .github/uitest/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ runs:
shell: bash
env:
DOTVVM_SAMPLES_CONFIG_PROFILE: ${{ inputs.samples-config }}
GITHUB_TOKEN: ${{ inputs.github-token }}

- if: ${{ runner.os == 'Windows' }}
run: choco install dotnet-aspnetcoremodule-v2 -y
Expand All @@ -53,6 +54,7 @@ runs:
shell: pwsh
env:
DOTVVM_SAMPLES_CONFIG_PROFILE: ${{ inputs.samples-config }}
GITHUB_TOKEN: ${{ inputs.github-token }}

# publish the result to github
- uses: ./.github/test-report
Expand Down
13 changes: 12 additions & 1 deletion .github/uitest/uitest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,16 @@ function Start-Sample {
[int][parameter(Position = 2)]$port
)
Invoke-RequiredCmds "Start sample '$sampleName'" {
Reset-IISServerManager -Confirm:$false

Remove-IISSite -Confirm:$false -Name $sampleName -ErrorAction SilentlyContinue

icacls "$root\artifacts\" /grant "IIS_IUSRS:(OI)(CI)F"

New-IISSite -Name "$sampleName" `
-PhysicalPath "$path" `
-BindingInformation "*:${port}:"

# ensure IIS created the site
while ($true) {
$state = (Get-IISSite -Name $sampleName).State
Expand All @@ -131,6 +133,15 @@ function Start-Sample {
throw "Site '${sampleName}' could not be started. State: '${state}'."
}
}

# add or update environment variable to the application pool
$existingEnvVar = c:\windows\system32\inetsrv\appcmd.exe list config -section:system.applicationHost/applicationPools `
| out-string `
| select-xml -XPath "//add[@name='DefaultAppPool']/environmentVariables/add[@name='GITHUB_TOKEN']"
if ($existingEnvVar -ne $null) {
c:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /-"[name='DefaultAppPool'].environmentVariables.[name='GITHUB_TOKEN']" /commit:apphost
}
c:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='DefaultAppPool'].environmentVariables.[name='GITHUB_TOKEN',value='$env:GITHUB_TOKEN']" /commit:apphost
}
}

Expand Down
141 changes: 141 additions & 0 deletions src/Framework/Core/Controls/GenericGridViewDataSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace DotVVM.Framework.Controls
{
public class GenericGridViewDataSet<
T,
TFilteringOptions,
TSortingOptions,
TPagingOptions,
TRowInsertOptions,
TRowEditOptions>
: IGridViewDataSet<T>,
IFilterableGridViewDataSet<TFilteringOptions>,
ISortableGridViewDataSet<TSortingOptions>,
IPageableGridViewDataSet<TPagingOptions>,
IRowInsertGridViewDataSet<TRowInsertOptions>,
IRowEditGridViewDataSet<TRowEditOptions>
where TFilteringOptions : IFilteringOptions
where TSortingOptions : ISortingOptions
where TPagingOptions : IPagingOptions
where TRowInsertOptions : IRowInsertOptions
where TRowEditOptions : IRowEditOptions
{

/// <summary>
/// Gets or sets the items for the current page.
/// </summary>
public IList<T> Items { get; set; } = new List<T>();

/// <summary>
/// Gets or sets whether the data should be refreshed. This property is set to true automatically
/// when paging, sorting or other options change.
/// </summary>
public bool IsRefreshRequired { get; set; } = true;

/// <summary>
/// Gets or sets the settings for filtering.
/// </summary>
public TFilteringOptions FilteringOptions { get; set; }

/// <summary>
/// Gets or sets the settings for sorting.
/// </summary>
public TSortingOptions SortingOptions { get; set; }

/// <summary>
/// Gets or sets the settings for paging.
/// </summary>
public TPagingOptions PagingOptions { get; set; }

/// <summary>
/// Gets or sets the settings for row (item) insert feature.
/// </summary>
public TRowInsertOptions RowInsertOptions { get; set; }

/// <summary>
/// Gets or sets the settings for row (item) edit feature.
/// </summary>
public TRowEditOptions RowEditOptions { get; set; }


IList IBaseGridViewDataSet.Items => Items is IList list ? list : new ReadOnlyCollection<T>(Items);

IFilteringOptions IFilterableGridViewDataSet.FilteringOptions => this.FilteringOptions;

ISortingOptions ISortableGridViewDataSet.SortingOptions => this.SortingOptions;

IPagingOptions IPageableGridViewDataSet.PagingOptions => this.PagingOptions;

IRowInsertOptions IRowInsertGridViewDataSet.RowInsertOptions => this.RowInsertOptions;

IRowEditOptions IRowEditGridViewDataSet.RowEditOptions => this.RowEditOptions;



public GenericGridViewDataSet(TFilteringOptions filteringOptions, TSortingOptions sortingOptions, TPagingOptions pagingOptions, TRowInsertOptions rowInsertOptions, TRowEditOptions rowEditOptions)
{
FilteringOptions = filteringOptions;
SortingOptions = sortingOptions;
PagingOptions = pagingOptions;
RowInsertOptions = rowInsertOptions;
RowEditOptions = rowEditOptions;
}


/// <summary>
/// Requests to reload data into the <see cref="GridViewDataSet{T}" />.
/// </summary>
public void RequestRefresh()
{
IsRefreshRequired = true;
}

/// <summary>
/// Applies the options from the specified <see cref="GridViewDataSetOptions{TFilteringOptions, TSortingOptions, TPagingOptions}" /> to this instance.
/// </summary>
public void ApplyOptions(GridViewDataSetOptions<TFilteringOptions, TSortingOptions, TPagingOptions> options)
{
if (options.FilteringOptions != null)
{
FilteringOptions = options.FilteringOptions;
}

if (options.SortingOptions != null)
{
SortingOptions = options.SortingOptions;
}

if (options.PagingOptions != null)
{
PagingOptions = options.PagingOptions;
}
}

public GridViewDataSetOptions<TFilteringOptions, TSortingOptions, TPagingOptions> GetOptions()
{
return new()
{
FilteringOptions = FilteringOptions,
SortingOptions = SortingOptions,
PagingOptions = PagingOptions
};
}

/// <summary> Sets new items + filtering, sorting, paging options. </summary>
public void ApplyResult(GridViewDataSetResult<T, TFilteringOptions, TSortingOptions, TPagingOptions> result)
{
Items = result.Items.ToList();
if (result.FilteringOptions is {})
FilteringOptions = result.FilteringOptions;
if (result.SortingOptions is {})
SortingOptions = result.SortingOptions;
if (result.PagingOptions is {})
PagingOptions = result.PagingOptions;
}
}
}
173 changes: 11 additions & 162 deletions src/Framework/Core/Controls/GridViewDataSet.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -11,167 +9,18 @@ namespace DotVVM.Framework.Controls
/// <summary>
/// Represents a collection of items with paging, sorting and row edit capabilities.
/// </summary>
/// <typeparam name="T">The type of the <see cref="Items" /> elements.</typeparam>
public class GridViewDataSet<T> : IGridViewDataSet<T>
/// <typeparam name="T">The type of the elements in the collection.</typeparam>
public class GridViewDataSet<T>()
: GenericGridViewDataSet<T, NoFilteringOptions, SortingOptions, PagingOptions, NoRowInsertOptions, RowEditOptions>(new(), new(), new(), new(), new())
{
/// <summary>
/// Gets or sets whether the data should be refreshed. This property is set to true automatically
/// when paging, sorting or other options change.
/// </summary>
public bool IsRefreshRequired { get; set; } = true;

/// <summary>
/// Gets or sets the items for the current page.
/// </summary>
public IList<T> Items { get; set; } = new List<T>();

IList IBaseGridViewDataSet.Items => Items is List<T> list ? list : Items.ToList();

/// <summary>
/// Gets or sets the settings for paging.
/// </summary>
public IPagingOptions PagingOptions { get; set; } = new PagingOptions();

/// <summary>
/// Gets or sets the settings for row (item) edit feature.
/// </summary>
public IRowEditOptions RowEditOptions { get; set; } = new RowEditOptions();

/// <summary>
/// Gets or sets the settings for sorting.
/// </summary>
public ISortingOptions SortingOptions { get; set; } = new SortingOptions();

/// <summary>
/// Navigates to the specific page.
/// </summary>
/// <param name="index">The zero-based index of the page to navigate to.</param>
public void GoToPage(int index)
{
PagingOptions.PageIndex = index;
RequestRefresh();
}

/// <summary>
/// Loads data into the <see cref="GridViewDataSet{T}" /> from the given <see cref="IQueryable{T}" /> source.
/// </summary>
/// <param name="source">The source to load data from.</param>
public void LoadFromQueryable(IQueryable<T> source)
{
source = ApplyFilteringToQueryable(source);
Items = ApplyOptionsToQueryable(source).ToList();
PagingOptions.TotalItemsCount = source.Count();
IsRefreshRequired = false;
}

/// <summary>
/// Requests to reload data into the <see cref="GridViewDataSet{T}" />.
/// </summary>
public virtual void RequestRefresh()
{
IsRefreshRequired = true;
}

/// <summary>
/// Sets the sort expression. If the specified expression is already set, switches the sort direction.
/// </summary>
[Obsolete("This method has strange side-effects, assign the expression yourself and reload the DataSet.")]
public virtual void SetSortExpression(string expression)
{
if (SortingOptions.SortExpression == expression)
{
SortingOptions.SortDescending = !SortingOptions.SortDescending;
}
else
{
SortingOptions.SortExpression = expression;
SortingOptions.SortDescending = false;
}

GoToPage(0);
}

/// <summary>
/// Applies filtering to the <paramref name="queryable" /> before the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplyFilteringToQueryable(IQueryable<T> queryable)
{
return queryable;
}

/// <summary>
/// Applies options to the <paramref name="queryable" /> after the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplyOptionsToQueryable(IQueryable<T> queryable)
{
queryable = ApplySortingToQueryable(queryable);
queryable = ApplyPagingToQueryable(queryable);
return queryable;
}

/// <summary>
/// Applies sorting to the <paramref name="queryable" /> after the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplySortingToQueryable(IQueryable<T> queryable)
{
if (SortingOptions?.SortExpression == null)
{
return queryable;
}

var parameterExpression = Expression.Parameter(typeof(T), "p");
Expression sortByExpression = parameterExpression;
foreach (var prop in (SortingOptions.SortExpression ?? "").Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries))
{
var property = sortByExpression.Type.GetTypeInfo().GetProperty(prop);
if (property == null)
{
throw new Exception($"Could not sort by property '{prop}', since it does not exists.");
}
if (property.GetCustomAttribute<BindAttribute>() is BindAttribute bind && bind.Direction == Direction.None)
{
throw new Exception($"Cannot sort by an property '{prop}' that has [Bind(Direction.None)].");
}
if (property.GetCustomAttribute<ProtectAttribute>() is ProtectAttribute protect && protect.Settings == ProtectMode.EncryptData)
{
throw new Exception($"Cannot sort by an property '{prop}' that is encrypted.");
}

sortByExpression = Expression.Property(sortByExpression, property);
}
if (sortByExpression == parameterExpression) // no sorting
{
return queryable;
}
var lambdaExpression = Expression.Lambda(sortByExpression, parameterExpression);
var methodCallExpression = Expression.Call(typeof(Queryable), GetSortingMethodName(),
new[] { parameterExpression.Type, sortByExpression.Type },
queryable.Expression,
Expression.Quote(lambdaExpression));
return queryable.Provider.CreateQuery<T>(methodCallExpression);
}

/// <summary>
/// Applies paging to the <paramref name="queryable" /> after the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplyPagingToQueryable(IQueryable<T> queryable)
{
return PagingOptions != null && PagingOptions.PageSize > 0 ?
queryable.Skip(PagingOptions.PageSize * PagingOptions.PageIndex).Take(PagingOptions.PageSize) :
queryable;
}

private string GetSortingMethodName()
{
return SortingOptions.SortDescending ? "OrderByDescending" : "OrderBy";
// return specialized dataset options
public new GridViewDataSetOptions GetOptions()
{
return new GridViewDataSetOptions {
FilteringOptions = FilteringOptions,
SortingOptions = SortingOptions,
PagingOptions = PagingOptions
};
}
}
}
Loading

0 comments on commit 1c9119b

Please sign in to comment.