Skip to content

Commit

Permalink
TransformOnRefresh option for TransformWithInlineUpdate. (#875)
Browse files Browse the repository at this point in the history
* Add transformOnRefresh option for TransformWithInlineUpdate. Fixes #873

* Modify API file + eliminate optional overloads

---------

Co-authored-by: Darrin W. Cullop <Darrin.Cullop@microsoft.com>
  • Loading branch information
RolandPheasant and dwcullop committed Mar 17, 2024
1 parent bdad65b commit e8c905a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,14 @@ namespace DynamicData
where TDestination : class
where TSource : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TDestination, TKey>> TransformWithInlineUpdate<TDestination, TSource, TKey>(this System.IObservable<DynamicData.IChangeSet<TSource, TKey>> source, System.Func<TSource, TDestination> transformFactory, System.Action<TDestination, TSource> updateAction, bool transformOnRefresh)
where TDestination : class
where TSource : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TDestination, TKey>> TransformWithInlineUpdate<TDestination, TSource, TKey>(this System.IObservable<DynamicData.IChangeSet<TSource, TKey>> source, System.Func<TSource, TDestination> transformFactory, System.Action<TDestination, TSource> updateAction, System.Action<DynamicData.Kernel.Error<TSource, TKey>> errorHandler, bool transformOnRefresh)
where TDestination : class
where TSource : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.ISortedChangeSet<TObject, TKey>> TreatMovesAsRemoveAdd<TObject, TKey>(this System.IObservable<DynamicData.ISortedChangeSet<TObject, TKey>> source)
where TObject : notnull
where TKey : notnull { }
Expand Down
27 changes: 26 additions & 1 deletion src/DynamicData.Tests/Cache/TransformWithInlineUpdateFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,34 @@ public void Remove()
stub.Results.Data.Count.Should().Be(0, "Should be nothing cached");
}


[Fact]
public void TransformOnRefresh()
{
using var stub = new TransformWithInlineUpdateFixtureStub(true);
var person = new Person("Adult1", 50);
stub.Source.AddOrUpdate(person);

var transformedPerson = stub.Results.Data.Items.First();

person.Age = 51;
stub.Source.Refresh(person);

var updatedTransform = stub.Results.Data.Items.First();

updatedTransform.Age.Should().Be(51, "Age should be updated from 50 to 51.");
stub.Results.Messages.Count.Should().Be(2, "Should be 2 updates");
stub.Results.Data.Count.Should().Be(1, "Should be 1 item in the cache");
transformedPerson.Should().Be(stub.Results.Data.Items.First(), "Should be same transformed person instance.");
}

private class TransformWithInlineUpdateFixtureStub : IDisposable
{
public TransformWithInlineUpdateFixtureStub() => Results = new ChangeSetAggregator<Person, string>(Source.Connect().TransformWithInlineUpdate(TransformFactory, UpdateAction));
public TransformWithInlineUpdateFixtureStub(bool transformOnRefresh = false)
{
Results = new ChangeSetAggregator<Person, string>(Source.Connect()
.TransformWithInlineUpdate(TransformFactory, UpdateAction, transformOnRefresh));
}

public ChangeSetAggregator<Person, string> Results { get; }

Expand Down
13 changes: 11 additions & 2 deletions src/DynamicData/Cache/Internal/TransformWithInlineUpdate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace DynamicData.Cache.Internal;
internal sealed class TransformWithInlineUpdate<TDestination, TSource, TKey>(IObservable<IChangeSet<TSource, TKey>> source,
Func<TSource, TDestination> transformFactory,
Action<TDestination, TSource> updateAction,
Action<Error<TSource, TKey>>? exceptionCallback = null)
Action<Error<TSource, TKey>>? exceptionCallback = null,
bool transformOnRefresh = false)
where TDestination : class
where TSource : notnull
where TKey : notnull
Expand Down Expand Up @@ -41,7 +42,15 @@ internal sealed class TransformWithInlineUpdate<TDestination, TSource, TKey>(IOb
break;
case ChangeReason.Refresh:
cache.Refresh(change.Key);
if (transformOnRefresh)
{
InlineUpdate(cache, change);
}
else
{
cache.Refresh(change.Key);
}
break;
case ChangeReason.Moved:
Expand Down
62 changes: 60 additions & 2 deletions src/DynamicData/Cache/ObservableCacheEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5914,7 +5914,35 @@ sortOrder switch
transformFactory.ThrowArgumentNullExceptionIfNull(nameof(transformFactory));
updateAction.ThrowArgumentNullExceptionIfNull(nameof(updateAction));

return new TransformWithInlineUpdate<TDestination, TSource, TKey>(source, transformFactory, updateAction).Run();
return source.TransformWithInlineUpdate(transformFactory, updateAction, false);
}

/// <summary>
/// Projects each update item to a new form using the specified transform function and when an update is received, allows the preservation of the previous instance.
/// </summary>
/// <typeparam name="TDestination">The type of the destination.</typeparam>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <param name="source">The source.</param>
/// <param name="transformFactory">The transform factory.</param>
/// <param name="updateAction">Apply changes to the original. Example (previousTransformedItem, newOriginalItem) => previousTransformedItem.Value = newOriginalItem.</param>
/// <param name="transformOnRefresh">Should a new transform be applied when a refresh event is received.</param>
/// <returns>
/// A transformed update collection.
/// </returns>
/// <exception cref="ArgumentNullException">source
/// or
/// transformFactory.</exception>
public static IObservable<IChangeSet<TDestination, TKey>> TransformWithInlineUpdate<TDestination, TSource, TKey>(this IObservable<IChangeSet<TSource, TKey>> source, Func<TSource, TDestination> transformFactory, Action<TDestination, TSource> updateAction, bool transformOnRefresh)
where TDestination : class
where TSource : notnull
where TKey : notnull
{
source.ThrowArgumentNullExceptionIfNull(nameof(source));
transformFactory.ThrowArgumentNullExceptionIfNull(nameof(transformFactory));
updateAction.ThrowArgumentNullExceptionIfNull(nameof(updateAction));

return new TransformWithInlineUpdate<TDestination, TSource, TKey>(source, transformFactory, updateAction, transformOnRefresh: transformOnRefresh).Run();
}

/// <summary>
Expand Down Expand Up @@ -5943,7 +5971,37 @@ sortOrder switch
updateAction.ThrowArgumentNullExceptionIfNull(nameof(updateAction));
errorHandler.ThrowArgumentNullExceptionIfNull(nameof(errorHandler));

return new TransformWithInlineUpdate<TDestination, TSource, TKey>(source, transformFactory, updateAction, errorHandler).Run();
return source.TransformWithInlineUpdate(transformFactory, updateAction, errorHandler, false);
}

/// <summary>
/// Projects each update item to a new form using the specified transform function and when an update is received, allows the preservation of the previous instance.
/// </summary>
/// <typeparam name="TDestination">The type of the destination.</typeparam>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <param name="source">The source.</param>
/// <param name="transformFactory">The transform factory.</param>
/// <param name="updateAction">Apply changes to the original. Example (previousTransformedItem, newOriginalItem) => previousTransformedItem.Value = newOriginalItem.</param>
/// <param name="errorHandler">The error handler.</param>
/// <param name="transformOnRefresh">Should a new transform be applied when a refresh event is received.</param>
/// <returns>
/// A transformed update collection.
/// </returns>
/// <exception cref="ArgumentNullException">source
/// or
/// transformFactory.</exception>
public static IObservable<IChangeSet<TDestination, TKey>> TransformWithInlineUpdate<TDestination, TSource, TKey>(this IObservable<IChangeSet<TSource, TKey>> source, Func<TSource, TDestination> transformFactory, Action<TDestination, TSource> updateAction, Action<Error<TSource, TKey>> errorHandler, bool transformOnRefresh)
where TDestination : class
where TSource : notnull
where TKey : notnull
{
source.ThrowArgumentNullExceptionIfNull(nameof(source));
transformFactory.ThrowArgumentNullExceptionIfNull(nameof(transformFactory));
updateAction.ThrowArgumentNullExceptionIfNull(nameof(updateAction));
errorHandler.ThrowArgumentNullExceptionIfNull(nameof(errorHandler));

return new TransformWithInlineUpdate<TDestination, TSource, TKey>(source, transformFactory, updateAction, errorHandler, transformOnRefresh).Run();
}

/// <summary>
Expand Down

0 comments on commit e8c905a

Please sign in to comment.