As a caveat I'm a novice with Rx (2 weeks) and have been experimenting with using Rx, RxUI and Roland Pheasant's DynamicData.
I have a service that initially loads data from local persistence and then, upon some user (or system) instruction will contact the server (TriggerServer in the example) to get additional or replacement data. The solution I've come up with uses a Subject and I've come across many a site discussing the pros/cons of using them. Although I understand the basics of hot/cold it's all based on reading rather than real world.
So, using the below as a simplified version, is this 'right' way of going about this problem or is there something I haven't properly understood somewhere?
NB: I'm not sure how important it is, but the actual code is taken from a Xamarin.Forms app, that uses RxUI, the user input being a ReactiveCommand.
Example:
using DynamicData;
using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
public class MyService : IDisposable
{
private CompositeDisposable _cleanup;
private Subject<Unit> _serverSubject = new Subject<Unit>();
public MyService()
{
var data = Initialise().Publish();
AllData = data.AsObservableCache();
_cleanup = new CompositeDisposable(AllData, data.Connect());
}
public IObservableCache<MyData, Guid> AllData { get; }
public void TriggerServer()
{
// This is what I'm not sure about...
_serverSubject.OnNext(Unit.Default);
}
private IObservable<IChangeSet<MyData, Guid>> Initialise()
{
return ObservableChangeSet.Create<MyData, Guid>(async cache =>
{
// inital load - is this okay?
cache.AddOrUpdate(await LoadLocalData());
// is this a valid way of doing this?
var sync = _serverSubject.Select(_ => GetDataFromServer())
.Subscribe(async task =>
{
var data = await task.ConfigureAwait(false);
cache.AddOrUpdate(data);
});
return new CompositeDisposable(sync);
}, d=> d.Id);
}
private IObservable<MyData> LoadLocalData()
{
return Observable.Timer(TimeSpan.FromSeconds(3)).Select(_ => new MyData("localdata"));
}
private async Task<MyData> GetDataFromServer()
{
await Task.Delay(2000).ConfigureAwait(true);
return new MyData("serverdata");
}
public void Dispose()
{
_cleanup?.Dispose();
}
}
public class MyData
{
public MyData(string value)
{
Value = value;
}
public Guid Id { get; } = Guid.NewGuid();
public string Value { get; set; }
}
And a simple Console app to run:
public static class TestProgram
{
public static void Main()
{
var service = new MyService();
service.AllData.Connect()
.Bind(out var myData)
.Subscribe(_=> Console.WriteLine("data in"), ()=> Console.WriteLine("COMPLETE"));
while (Continue())
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine($"Triggering Server Call, current data is: {string.Join(", ", myData.Select(x=> x.Value))}");
service.TriggerServer();
}
}
private static bool Continue()
{
Console.WriteLine("Press any key to call server, x to exit");
var key = Console.ReadKey();
return key.Key != ConsoleKey.X;
}
}
Looks very good for first try with Rx
I would suggest few changes:
1) Remove the Initialize() call from the constructor and make it a public method - helps a lot with unit tests and now you can await it if you need to
public static void Main()
{
var service = new MyService();
service.Initialize();
2) Add Throttle to you trigger - this fixes parallel calls to the server returning the same results
3) Don't do anything that can throw in Subscribe, use Do instead:
var sync = _serverSubject
.Throttle(Timespan.FromSeconds(0.5), RxApp.TaskPoolScheduler) // you can pass a scheduler via arguments, or use TestScheduler in unit tests to make time pass faster
.Do(async _ =>
{
var data = await GetDataFromServer().ConfigureAwait(false); // I just think this is more readable, your way was also correct
cache.AddOrUpdate(data);
})
// .Retry(); // or anything alese to handle failures
.Subscribe();
I'm putting what I've come to as my solution just in case there's others that find this while they're wandering the internets.
I ended up removing the Subjects all together and chaining together several SourceCache, so when one changed it pushed into the other and so on. I've removed some code for brevity:
public class MyService : IDisposable
{
private SourceCache<MyData, Guid> _localCache = new SourceCache<MyData, Guid>(x=> x.Id);
private SourceCache<MyData, Guid> _serverCache = new SourceCache<MyData, Guid>(x=> x.Id);
public MyService()
{
var localdata = _localCache.Connect();
var serverdata = _serverCache.Connect();
var alldata = localdata.Merge(serverdata);
AllData = alldata.AsObservableCache();
}
public IObservableCache<MyData, Guid> AllData { get; }
public IObservable<Unit> TriggerLocal()
{
return LoadLocalAsync().ToObservable();
}
public IObservable<Unit> TriggerServer()
{
return LoadServerAsync().ToObservable();
}
}
EDIT: I've changed this again to remove any chaining of caches - I just manage the one cache internally. Lesson is not to post too early.
Related
I have a weird problem with my async method. I made second project in my solution in VS to connect my app to an API (using RestSharp for it). I made dependencies etc.
The problem is when I call this method from the UI by clicking a button (it only start backend code, there is no relation to UI etc.) app getting stuck. There is no errors, the only things I can see in the output window are "The thread ****** has exited with code 0 (0x0)." and it's going infinitely.
I took that code (only from project responsible for connecting and taking data from an api) and made a new solution, new project, but exacly copied code and it is working fine.
This is method what I am calling in the "main" WPF app using ICommand etc:
private void Api()
{
_orderService = new OrderService();
}
And those are classes in API project:
BLContext.cs
public class BLContext
{
private RestClient _client;
public RestClient Client { get; set; }
private string _token;
public string Token { get; set; }
public BLContext()
{
Client = new RestClient("https://api.baselinker.com/connector.php");
Token = "************************";
}
}
BaseAPIRepository.cs
public class BaseAPIRepository
{
private BLContext _bl = new BLContext();
RestRequest Request = new RestRequest();
public BaseAPIRepository() { }
public async Task<List<Order>> GetOrders()
{
List<Order> orders = new List<Order>();
List<JToken> orderList = new List<JToken>();
StartRequest("getOrders");
Request.AddParameter("parameters", "{ \"status_id\": 13595 }");
Request.AddParameter("parameters", "{ \"get_unconfirmed_orders\": false }");
RestResponse restResponse = await _bl.Client.PostAsync(Request);
JObject response = (JObject)JsonConvert.DeserializeObject(restResponse.Content);
orderList = response["orders"].ToList();
foreach (JToken order in orderList)
{
Order newOrder = new Order();
newOrder.Id = (int)order["order_id"];
newOrder.ProductsInOrder = GetProductsFromOrder((JArray)order["products"]);
orders.Add(newOrder);
}
return orders;
}
public void StartRequest(string method)
{
Request.AddParameter("token", _bl.Token);
Request.AddParameter("method", method);
}
public List<OrderedProduct> GetProductsFromOrder(JArray productsInOrder)
{
List<OrderedProduct> tmpListOfProducts = new List<OrderedProduct>();
foreach (var item in productsInOrder)
{
OrderedProduct tmpOrderedProduct = new OrderedProduct();
//tmpOrderedProduct.Id = (int)item["product_id"];
tmpOrderedProduct.Signature = (string)item["sku"];
tmpOrderedProduct.Quantity = (int)item["quantity"];
tmpListOfProducts.Add(tmpOrderedProduct);
}
return tmpListOfProducts;
}
}
OrderService.cs
public class OrderService
{
private BaseAPIRepository _repo;
private List<Order> _ordersList;
public List<Order> OrdersList { get; set; }
public OrderService()
{
_repo = new BaseAPIRepository();
OrdersList = new List<Order>();
OrdersList = _repo.GetOrders().Result;
Console.WriteLine("Test line to see if it passed 24th line.");
}
}
App is getting stuck on line:
RestResponse restResponse = await _bl.Client.PostAsync(Request);
The core problem - as others have noted - is that your code is blocking on asynchronous code, which you shouldn't do (as I explain on my blog). This is particularly true for UI apps, which deliver a bad user experience when the UI thread is blocked. So, even if the code wasn't deadlocking, it wouldn't be a good idea to block on the asynchronous code anyway.
There are certain places in a UI app where the code simply cannot block if you want a good user experience. View and ViewModel construction are two of those places. When a VM is being created, the OS is asking your app to display its UI right now, and waiting for a network request before displaying data is just a bad experience.
Instead, your application should initialize and return its UI immediately (synchronously), and display that. If you have to do a network request to get some data to display, it's normal to synchronously initialize the UI into a "loading" state, start the network request, and then at that point the construction/initialization is done. Later, when the network request completes, the UI is updated into a "loaded" state.
If you want to take this approach, there's a NotifyTask<T> type in my Nito.Mvvm.Async package which may help. Its design is described in this article and usage looks something like this (assuming OrderService is actually a ViewModel):
public class OrderService
{
private BaseAPIRepository _repo;
public NotifyTask<List<Order>> OrdersList { get; set; }
public OrderService()
{
_repo = new BaseAPIRepository();
OrdersList = NotifyTask.Create(() => _repo.GetOrders());
}
}
Then, instead of data-binding to OrderService.OrdersList, you can data-bind to OrderService.OrdersList.Result, OrderService.OrdersList.IsCompleted, etc.
You should never call Task.Result on an incomplete Task to avoid deadlocking the application. Always await a Task.
C# doesn't allow async constructors. Constructors are meant to return fast after some brief initialization. They are not a place for long-running operations or starting background threads (even if async constructors were allowed).
There are a few solutions to avoid the requirement of async constructors.
A simple alternative solution using Lazy<T> or AsyncLazy<T> (requires to install the Microsoft.VisualStudio.Threading package via the NuGet Package Manager). Lazy<T> allows to defer the instantiation or allocation of expensive resources.
public class OrderService
{
public List<object> Orders => this.OrdersInitializer.GetValue();
private AsyncLazy<List<object>> OrdersInitializer { get; }
public OrderService()
=> this.OrdersInitializer = new AsyncLazy<List<object>>(InitializeOrdersAsync, new JoinableTaskFactory(new JoinableTaskContext()));
private async Task<List<object>> InitializeOrdersAsync()
{
await Task.Delay(TimeSpan.FromSeconds(5));
return new List<object> { 1, 2, 3 };
}
}
public static void Main()
{
var orderService = new OrderService();
// Trigger async initialization
orderService.Orders.Add(4);
}
You can expose the data using a method instead of a property
public class OrderService
{
private List<object> Orders { get; set; }
public async Task<List<object>> GetOrdersAsync()
{
if (this.Orders == null)
{
await Task.Delay(TimeSpan.FromSeconds(5));
this.Orders = new List<object> { 1, 2, 3 };
}
return this.Orders;
}
}
public static async Task Main()
{
var orderService = new OrderService();
// Trigger async initialization
List<object> orders = await orderService.GetOrdersAsync();
}
Use an InitializeAsync method that must be called before using the instance
public class OrderService
{
private List<object> orders;
public List<object> Orders
{
get
{
if (!this.IsInitialized)
{
throw new InvalidOperationException();
}
return this.orders;
}
private set
{
this.orders = value;
}
}
public bool IsInitialized { get; private set; }
public async Task<List<object>> InitializeAsync()
{
if (this.IsInitialized)
{
return;
}
await Task.Delay(TimeSpan.FromSeconds(5));
this.Orders = new List<object> { 1, 2, 3 };
this.IsInitialized = true;
}
}
public static async Task Main()
{
var orderService = new OrderService();
// Trigger async initialization
await orderService.InitializeAsync();
}
Instantiate the instance by passing the expensive arguments to the constructor
public class OrderService
{
public List<object> Orders { get; }
public async Task<List<object>> OrderService(List<object> orders)
=> this.Orders = orders;
}
public static async Task Main()
{
List<object> orders = await GetOrdersAsync();
// Instantiate with the result of the async operation
var orderService = new OrderService(orders);
}
private static async Task<List<object>> GetOrdersAsync()
{
await Task.Delay(TimeSpan.FromSeconds(5));
return new List<object> { 1, 2, 3 };
}
Use a factory method and a private constructor
public class OrderService
{
public List<object> Orders { get; set; }
private OrderServiceBase()
=> this.Orders = new List<object>();
public static async Task<OrderService> CreateInstanceAsync()
{
var instance = new OrderService();
await Task.Delay(TimeSpan.FromSeconds(5));
instance.Orders = new List<object> { 1, 2, 3 };
return instance;
}
}
public static async Task Main()
{
// Trigger async initialization
OrderService orderService = await OrderService.CreateInstanceAsync();
}
I want to use IAsyncEnumerable like a Source for akka streams. But I not found, how do it.
No sutiable method in Source class for this code.
using System.Collections.Generic;
using System.Threading.Tasks;
using Akka.Streams.Dsl;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
Source.From(await AsyncEnumerable())
.Via(/*some action*/)
//.....
}
private static async IAsyncEnumerable<int> AsyncEnumerable()
{
//some async enumerable
}
}
}
How use IAsyncEnumerbale for Source?
This has been done in the past as a part of Akka.NET Streams contrib package, but since I don't see it there anymore, let's go through on how to implement such source. The topic can be quite long, as:
Akka.NET Streams is really about graph processing - we're talking about many-inputs/many-outputs configurations (in Akka.NET they're called inlets and outlets) with support for cycles in graphs.
Akka.NET is not build on top of .NET async/await or even on top of .NET standard thread pool library - they're both pluggable, which means that the lowest barier is basically using callbacks and encoding what C# compiler sometimes does for us.
Akka.NET streams is capable of both pushing and pulling values between stages/operators. IAsyncEnumerable<T> can only pull data while IObservable<T> can only push it, so we get more expressive power here, but this comes at a cost.
The basics of low level API used to implement custom stages can be found in the docs.
The starter boilerplate looks like this:
public static class AsyncEnumerableExtensions {
// Helper method to change IAsyncEnumerable into Akka.NET Source.
public static Source<T, NotUsed> AsSource<T>(this IAsyncEnumerable<T> source) =>
Source.FromGraph(new AsyncEnumerableSource<T>(source));
}
// Source stage is description of a part of the graph that doesn't consume
// any data, only produce it using a single output channel.
public sealed class AsyncEnumerableSource<T> : GraphStage<SourceShape<T>>
{
private readonly IAsyncEnumerable<T> _enumerable;
public AsyncEnumerableSource(IAsyncEnumerable<T> enumerable)
{
_enumerable = enumerable;
Outlet = new Outlet<T>("asyncenumerable.out");
Shape = new SourceShape<T>(Outlet);
}
public Outlet<T> Outlet { get; }
public override SourceShape<T> Shape { get; }
/// Logic if to a graph stage, what enumerator is to enumerable.
protected override GraphStageLogic CreateLogic(Attributes inheritedAttributes) =>
new Logic(this);
sealed class Logic: OutGraphStageLogic
{
public override void OnPull()
{
// method called whenever a consumer asks for new data
}
public override void OnDownstreamFinish()
{
// method called whenever a consumer stage finishes,used for disposals
}
}
}
As mentioned, we don't use async/await straight away here: even more, calling Logic methods in asynchronous context is unsafe. To make it safe we need to register out methods that may be called from other threads using GetAsyncCallback<T> and call them via returned wrappers. This will ensure, that not data races will happen when executing asynchronous code.
sealed class Logic : OutGraphStageLogic
{
private readonly Outlet<T> _outlet;
// enumerator we'll call for MoveNextAsync, and eventually dispose
private readonly IAsyncEnumerator<T> _enumerator;
// callback called whenever _enumerator.MoveNextAsync completes asynchronously
private readonly Action<Task<bool>> _onMoveNext;
// callback called whenever _enumerator.DisposeAsync completes asynchronously
private readonly Action<Task> _onDisposed;
// cache used for errors thrown by _enumerator.MoveNextAsync, that
// should be rethrown after _enumerator.DisposeAsync
private Exception? _failReason = null;
public Logic(AsyncEnumerableSource<T> source) : base(source.Shape)
{
_outlet = source.Outlet;
_enumerator = source._enumerable.GetAsyncEnumerator();
_onMoveNext = GetAsyncCallback<Task<bool>>(OnMoveNext);
_onDisposed = GetAsyncCallback<Task>(OnDisposed);
}
// ... other methods
}
The last part to do are methods overriden on `Logic:
OnPull used whenever the downstream stage calls for new data. Here we need to call for next element of async enumerator sequence.
OnDownstreamFinish called whenever the downstream stage has finished and will not ask for any new data. It's the place for us to dispose our enumerator.
Thing is these methods are not async/await, while their enumerator's equivalent are. What we basically need to do there is to:
Call corresponding async methods of underlying enumerator (OnPull → MoveNextAsync and OnDownstreamFinish → DisposeAsync).
See, if we can take their results immediately - it's important part that usually is done for us as part of C# compiler in async/await calls.
If not, and we need to wait for the results - call ContinueWith to register our callback wrappers to be called once async methods are done.
sealed class Logic : OutGraphStageLogic
{
// ... constructor and fields
public override void OnPull()
{
var hasNext = _enumerator.MoveNextAsync();
if (hasNext.IsCompletedSuccessfully)
{
// first try short-path: values is returned immediately
if (hasNext.Result)
// check if there was next value and push it downstream
Push(_outlet, _enumerator.Current);
else
// if there was none, we reached end of async enumerable
// and we can dispose it
DisposeAndComplete();
}
else
// we need to wait for the result
hasNext.AsTask().ContinueWith(_onMoveNext);
}
// This method is called when another stage downstream has been completed
public override void OnDownstreamFinish() =>
// dispose enumerator on downstream finish
DisposeAndComplete();
private void DisposeAndComplete()
{
var disposed = _enumerator.DisposeAsync();
if (disposed.IsCompletedSuccessfully)
{
// enumerator disposal completed immediately
if (_failReason is not null)
// if we close this stream in result of error in MoveNextAsync,
// fail the stage
FailStage(_failReason);
else
// we can close the stage with no issues
CompleteStage();
}
else
// we need to await for enumerator to be disposed
disposed.AsTask().ContinueWith(_onDisposed);
}
private void OnMoveNext(Task<bool> task)
{
// since this is callback, it will always be completed, we just need
// to check for exceptions
if (task.IsCompletedSuccessfully)
{
if (task.Result)
// if task returns true, it means we read a value
Push(_outlet, _enumerator.Current);
else
// otherwise there are no more values to read and we can close the source
DisposeAndComplete();
}
else
{
// task either failed or has been cancelled
_failReason = task.Exception as Exception ?? new TaskCanceledException(task);
FailStage(_failReason);
}
}
private void OnDisposed(Task task)
{
if (task.IsCompletedSuccessfully) CompleteStage();
else {
var reason = task.Exception as Exception
?? _failReason
?? new TaskCanceledException(task);
FailStage(reason);
}
}
}
As of Akka.NET v1.4.30 this is now natively supported inside Akka.Streams via the RunAsAsyncEnumerable method:
var input = Enumerable.Range(1, 6).ToList();
var cts = new CancellationTokenSource();
var token = cts.Token;
var asyncEnumerable = Source.From(input).RunAsAsyncEnumerable(Materializer);
var output = input.ToArray();
bool caught = false;
try
{
await foreach (var a in asyncEnumerable.WithCancellation(token))
{
cts.Cancel();
}
}
catch (OperationCanceledException e)
{
caught = true;
}
caught.ShouldBeTrue();
I copied that sample from the Akka.NET test suite, in case you're wondering.
You can also use an existing primitive for streaming large collections of data. Here is an example of using Source.unfoldAsync to stream pages of data - in this case github repositories using Octokit - until there is no more.
var source = Source.UnfoldAsync<int, RepositoryPage>(startPage, page =>
{
var pageTask = client.GetRepositoriesAsync(page, pageSize);
var next = pageTask.ContinueWith(task =>
{
var page = task.Result;
if (page.PageNumber * pageSize > page.Total) return Option<(int, RepositoryPage)>.None;
else return new Option<(int, RepositoryPage)>((page.PageNumber + 1, page));
});
return next;
});
To run
using var sys = ActorSystem.Create("system");
using var mat = sys.Materializer();
int startPage = 1;
int pageSize = 50;
var client = new GitHubClient(new ProductHeaderValue("github-search-app"));
var source = ...
var sink = Sink.ForEach<RepositoryPage>(Console.WriteLine);
var result = source.RunWith(sink, mat);
await result.ContinueWith(_ => sys.Terminate());
class Page<T>
{
public Page(IReadOnlyList<T> contents, int page, long total)
{
Contents = contents;
PageNumber = page;
Total = total;
}
public IReadOnlyList<T> Contents { get; set; } = new List<T>();
public int PageNumber { get; set; }
public long Total { get; set; }
}
class RepositoryPage : Page<Repository>
{
public RepositoryPage(IReadOnlyList<Repository> contents, int page, long total)
: base(contents, page, total)
{
}
public override string ToString() =>
$"Page {PageNumber}\n{string.Join("", Contents.Select(x => x.Name + "\n"))}";
}
static class GitHubClientExtensions
{
public static async Task<RepositoryPage> GetRepositoriesAsync(this GitHubClient client, int page, int size)
{
// specify a search term here
var request = new SearchRepositoriesRequest("bootstrap")
{
Page = page,
PerPage = size
};
var result = await client.Search.SearchRepo(request);
return new RepositoryPage(result.Items, page, result.TotalCount);
}
}
New: Entire source code with tests is now at https://github.com/bboyle1234/ReactiveTest
Let's imagine we have a view state object that is able to be updated by small partial view change events. Here are some example models of the total view, the incremental view update events and the accumulator function Update that builds the total view:
interface IDeviceView : ICloneable {
Guid DeviceId { get; }
}
class DeviceTotalView : IDeviceView {
public Guid DeviceId { get; set; }
public int Voltage { get; set; }
public int Currents { get; set; }
public object Clone() => this.MemberwiseClone();
}
class DeviceVoltagesUpdateView : IDeviceView {
public Guid DeviceId { get; set; }
public int Voltage { get; set; }
public object Clone() => this.MemberwiseClone();
}
class DeviceCurrentsUpdateView : IDeviceView {
public Guid DeviceId { get; set; }
public int Current { get; set; }
public object Clone() => this.MemberwiseClone();
}
class DeviceUpdateEvent {
public DeviceTotalView View;
public IDeviceView LastUpdate;
}
static DeviceUpdateEvent Update(DeviceUpdateEvent previousUpdate, IDeviceView update) {
if (update.DeviceId != previousUpdate.View.DeviceId) throw new InvalidOperationException("Device ids do not match (numskull exception).");
var view = (DeviceTotalView)previousUpdate.View.Clone();
switch (update) {
case DeviceVoltagesUpdateView x: {
view.Voltage = x.Voltage;
break;
}
case DeviceCurrentsUpdateView x: {
view.Currents = x.Current;
break;
}
}
return new DeviceUpdateEvent { View = view, LastUpdate = update };
}
Next, let's imagine we already have an injectable service that is able to produce an observable stream of the small update events for all devices, and that we want to create a service that can produce an aggregated view stream for individual devices.
Here is the interface of the service we want to create:
interface IDeviceService {
/// <summary>
/// Gets an observable that produces aggregated update events for the device with the given deviceId.
/// On subscription, the most recent event is immediately pushed to the subscriber.
/// There can be multiple subscribers.
/// </summary>
IObservable<DeviceUpdateEvent> GetDeviceStream(Guid deviceId);
}
How can I implement this interface and its requirements using the reactive extensions in the System.Reactive v4 library, targeting .netstandard2.0? Here's my boiler code with comments and that's as far as I've been able to get.
class DeviceService : IDeviceService {
readonly IObservable<IDeviceView> Source;
public DeviceService(IObservable<IDeviceView> source) { // injected parameter
/// When injected here, "source" is cold in the sense that it won't produce events until the first time it is subscribed.
/// "source" will throw an exception if its "Subscribe" method is called more than once as it is intended to have only one observer and
/// be read all the way from the beginning.
Source = source;
/// Callers of the "Subscribe" method below will expect data to be preloaded and will expect to be immediately delivered the most
/// recent event. So we need to immediately subscribe to "source" and start preloading the aggregate streams.
/// I'm assuming there is going to need to be a groupby to split the stream by device id.
var groups = source.GroupBy(x => x.DeviceId);
/// Now somehow we need to perform the aggregrate function on each grouping.
/// And create an observable that immediately delivers the most recent aggregated event when "Subscribe" is called below.
}
public IObservable<DeviceUpdateEvent> GetDeviceStream(Guid deviceId) {
/// How do we implement this? The observable that we return must be pre-loaded with the latest update
throw new NotImplementedException();
}
}
You have some weird code in that gist. Here's what I got working:
public class DeviceService : IDeviceService, IDisposable
{
readonly IObservable<IDeviceView> Source;
private readonly Dictionary<Guid, IObservable<DeviceUpdateEvent>> _updateStreams = new Dictionary<Guid, IObservable<DeviceUpdateEvent>>();
private readonly IObservable<(Guid, IObservable<DeviceUpdateEvent>)> _groupedStream;
private readonly CompositeDisposable _disposable = new CompositeDisposable();
public DeviceService(IObservable<IDeviceView> source)
{
Source = source;
_groupedStream = source
.GroupBy(v => v.DeviceId)
.Select(o => (o.Key, o
.Scan(new DeviceUpdateEvent { View = DeviceTotalView.GetInitialView(o.Key), LastUpdate = null }, (lastTotalView, newView) => lastTotalView.Update(newView))
.Replay(1)
.RefCount()
));
var groupSubscription = _groupedStream.Subscribe(t =>
{
_updateStreams[t.Item1] = t.Item2;
_disposable.Add(t.Item2.Subscribe());
});
_disposable.Add(groupSubscription);
}
public void Dispose()
{
_disposable.Dispose();
}
public IObservable<DeviceUpdateEvent> GetDeviceStream(Guid deviceId)
{
/// How do we implement this? The observable that we return must be pre-loaded with the latest update
if(this._updateStreams.ContainsKey(deviceId))
return this._updateStreams[deviceId];
return _groupedStream
.Where(t => t.Item1 == deviceId)
.Select(t => t.Item2)
.Switch();
}
}
The meat here is the _groupedStream piece. You group by DeviceId, as you said, then you use Scan to update state. I also moved Update to a static class and made it an extension method. You'll need an initial state, so I modified your DeviceTotalView class to get that. Modify accordingly:
public class DeviceTotalView : IDeviceView
{
public Guid DeviceId { get; set; }
public int Voltage { get; set; }
public int Currents { get; set; }
public object Clone() => this.MemberwiseClone();
public static DeviceTotalView GetInitialView(Guid deviceId)
{
return new DeviceTotalView
{
DeviceId = deviceId,
Voltage = 0,
Currents = 0
};
}
}
Next, the .Replay(1).Refcount() serves to remember the most recent update then provide that on subscription. We then stuff all of these child observables into a dictionary for easy retrieval on the method call. The dummy subscriptions (_disposable.Add(t.Item2.Subscribe())) are necessary for Replay to work.
In the event that there's an early request for a DeviceId that doesn't yet have an update, we subscribe to the _groupedStream which will wait for the first update, producing that Id's observable, then .Switch subscribes to that child observable.
However, all of this failed against your test code, I'm guessing because of the ConnectableObservableForAsyncProducerConsumerQueue class. I didn't want to debug that, because I wouldn't recommend doing something like that. In general it's not recommended to mix TPL and Rx code. They problems they solve largely overlap and they get in each other's way. So I modified your test code replacing that connectable observable queue thing with a Replay subject.
I also added the test-case for an early request (before an updates for that Device have arrived):
DeviceUpdateEvent deviceView1 = null;
DeviceUpdateEvent deviceView2 = null;
DeviceUpdateEvent deviceView3 = null;
var subject = new ReplaySubject<IDeviceView>();
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var id3 = Guid.NewGuid();
subject.OnNext(new DeviceVoltagesUpdateView { DeviceId = id1, Voltage = 1 });
subject.OnNext(new DeviceVoltagesUpdateView { DeviceId = id1, Voltage = 2 });
subject.OnNext(new DeviceVoltagesUpdateView { DeviceId = id2, Voltage = 100 });
var service = new DeviceService(subject);
service.GetDeviceStream(id1).Subscribe(x => deviceView1 = x);
service.GetDeviceStream(id2).Subscribe(x => deviceView2 = x);
service.GetDeviceStream(id3).Subscribe(x => deviceView3 = x);
/// I believe there is no need to pause here because the Subscribe method calls above
/// block until the events have all been pushed into the subscribers above.
Assert.AreEqual(deviceView1.View.DeviceId, id1);
Assert.AreEqual(deviceView2.View.DeviceId, id2);
Assert.AreEqual(deviceView1.View.Voltage, 2);
Assert.AreEqual(deviceView2.View.Voltage, 100);
Assert.IsNull(deviceView3);
subject.OnNext(new DeviceVoltagesUpdateView { DeviceId = id2, Voltage = 101 });
Assert.AreEqual(deviceView2.View.Voltage, 101);
subject.OnNext(new DeviceVoltagesUpdateView { DeviceId = id3, Voltage = 101 });
Assert.AreEqual(deviceView3.View.DeviceId, id3);
Assert.AreEqual(deviceView3.View.Voltage, 101);
That passes fine and can be run without async.
Also, as a general tip, I would recommend doing unit tests for Rx code with the Microsoft.Reactive.Testing package, rather than time-gapping things.
A huge thanks to #Shlomo for the answer above.
The implementation given in the accepted answer, whilst a magical education for me, had a couple of issues that also needed to be solved in turn. The first was a threadrace problem, and the second was performance when a large number of devices were in the system. I ended up solving the threadrace AND dramatically improving performance with this modified implementation:
In the constructor, the grouped and scanned device stream is subscribed directly to a BehaviorSubject, which implements the Replay(1).RefCount() functionality required to immediately notify new subscribers of the latest value in the stream.
In the GetDeviceStream method, we continue to use a dictionary lookup to find the device stream, creating a preloaded BehaviorSubject if it doesn't already exist in the dictionary. We have removed the Where search that existed in the previous implementation in the question above. Using the where search caused a threadrace problem that was solved by making the grouped stream replayable. That caused an expontial performance issue. Replacing it with FirstOrDefault reduced the time take by half, and then removing it completely in favor of the GetCreate dictionary technique gave perfect perfomance O(1) instead of O(n2).
GetCreateSubject uses the Lazy proxy object as the dictionary value because the ConcurrentDictionary can sometimes call the Create method more than once for a single key. Supplying a Lazy to the dictionary ensures that the Value property is only called on one of the lazies, and therefore only one BehaviorSubject is created per device.
class DeviceService : IDeviceService, IDisposable {
readonly CompositeDisposable _disposable = new CompositeDisposable();
readonly ConcurrentDictionary<Guid, Lazy<BehaviorSubject<DeviceUpdateEvent>>> _streams = new ConcurrentDictionary<Guid, Lazy<BehaviorSubject<DeviceUpdateEvent>>>();
BehaviorSubject<DeviceUpdateEvent> GetCreateSubject(Guid deviceId) {
return _streams.GetOrAdd(deviceId, Create).Value;
Lazy<BehaviorSubject<DeviceUpdateEvent>> Create(Guid id) {
return new Lazy<BehaviorSubject<DeviceUpdateEvent>>(() => {
var subject = new BehaviorSubject<DeviceUpdateEvent>(DeviceUpdateEvent.GetInitialView(deviceId));
_disposable.Add(subject);
return subject;
});
}
}
public DeviceService(IConnectableObservable<IDeviceView> source) {
_disposable.Add(source
.GroupBy(x => x.DeviceId)
.Subscribe(deviceStream => {
_disposable.Add(deviceStream
.Scan(DeviceUpdateEvent.GetInitialView(deviceStream.Key), DeviceUtils.Update)
.Subscribe(GetCreateSubject(deviceStream.Key)));
}));
_disposable.Add(source.Connect());
}
public void Dispose() {
_disposable.Dispose();
}
public IObservable<DeviceUpdateEvent> GetDeviceStream(Guid deviceId) {
return GetCreateSubject(deviceId).AsObservable();
}
}
[TestMethod]
public async Task Test2() {
var input = new AsyncProducerConsumerQueue<IDeviceView>();
var source = new ConnectableObservableForAsyncProducerConsumerQueue<IDeviceView>(input);
var service = new DeviceService(source);
var ids = Enumerable.Range(0, 100000).Select(i => Guid.NewGuid()).ToArray();
var idsRemaining = ids.ToHashSet();
var t1 = Task.Run(async () => {
foreach (var id in ids) {
await input.EnqueueAsync(new DeviceVoltagesUpdateView { DeviceId = id, Voltage = 1 });
}
});
var t2 = Task.Run(() => {
foreach (var id in ids) {
service.GetDeviceStream(id).Subscribe(x => idsRemaining.Remove(x.View.DeviceId));
}
});
await Task.WhenAll(t1, t2);
var sw = Stopwatch.StartNew();
while (idsRemaining.Count > 0) {
if (sw.Elapsed.TotalSeconds > 600) throw new Exception("Failed");
await Task.Delay(100);
}
}
See entire problem source code and test code here: https://github.com/bboyle1234/ReactiveTest
Is there a possibility to cache a collection, retrieved using WCF from an OData service.
The situation is the following:
I generated a WCF service client with Visual Studio 2015 using the metadata of the odata service. VS generated a class inheriting from System.Data.Services.Client.DataServiceContext. This class has some properties of type System.Data.Services.Client.DataServiceQuery<T>. The data of some of these properties change seldom. Because of performance reasons I want the WCF client to load these properties just the first time and not every time I use it in the code.
Is there a built in possibility to cache the data of these properties? Or can I tell the service client not to load specific proeprties newly every time.
Assuming the service client class is ODataClient and one of its properties is `Area, for now I get the values in the following way:
var client = new ODataClient("url_to_the_service");
client.IgnoreMissingProperties = true;
var propertyInfo = client.GetType().GetProperty("Area");
var area = propertyInfo.GetValue(client) as IEnumerable<object>;
The reason why I do this in such a complicated way is, that the client should be very generic: The properties to be handled can be configured in a configuration file.
* EDIT *
I already tried to find properties in the System.Data.Services.Client.DataServiceContext class or the System.Data.Services.Client.DataServiceQuery<T> class for the caching. But i wasn't able to find any.
To my knowledge there is no "out of the box" caching concept on the client. There are options for caching the output of a request on the server which is something you might want consider as well. Googling "WCF Caching" would get you a bunch of info on this.
Regarding client side caching...#Evk is correct it is pretty straight forward. Here is an sample using MemoryCache.
using System;
using System.Runtime.Caching;
namespace Services.Util
{
public class CacheWrapper : ICacheWrapper
{
ObjectCache _cache = MemoryCache.Default;
public void ClearCache()
{
MemoryCache.Default.Dispose();
_cache = MemoryCache.Default;
}
public T GetFromCache<T>(string key, Func<T> missedCacheCall)
{
return GetFromCache<T>(key, missedCacheCall, TimeSpan.FromMinutes(5));
}
public T GetFromCache<T>(string key, Func<T> missedCacheCall, TimeSpan timeToLive)
{
var result = _cache.Get(key);
if (result == null)
{
result = missedCacheCall();
if (result != null)
{
_cache.Set(key, result, new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.Add(timeToLive) });
}
}
return (T)result;
}
public void InvalidateCache(string key)
{
_cache.Remove(key);
}
}
}
This is an example of code that uses the cache...
private class DataAccessTestStub
{
public const string DateTimeTicksCacheKey = "GetDateTimeTicks";
ICacheWrapper _cache;
public DataAccessTestStub(ICacheWrapper cache)
{
_cache = cache;
}
public string GetDateTimeTicks()
{
return _cache.GetFromCache(DateTimeTicksCacheKey, () =>
{
var result = DateTime.Now.Ticks.ToString();
Thread.Sleep(100); // Create some delay
return result;
});
}
public string GetDateTimeTicks(TimeSpan timeToLive)
{
return _cache.GetFromCache(DateTimeTicksCacheKey, () =>
{
var result = DateTime.Now.Ticks.ToString();
Thread.Sleep(500); // Create some delay
return result;
}, timeToLive);
}
public void ClearDateTimeTicks()
{
_cache.InvalidateCache(DateTimeTicksCacheKey);
}
public void ClearCache()
{
_cache.ClearCache();
}
}
And some tests if you fancy...
[TestClass]
public class CacheWrapperTest
{
private DataAccessTestStub _dataAccessTestClass;
[TestInitialize]
public void Init()
{
_dataAccessTestClass = new DataAccessTestStub(new CacheWrapper());
}
[TestMethod]
public void GetFromCache_ShouldExecuteCacheMissCall()
{
var original = _dataAccessTestClass.GetDateTimeTicks();
Assert.IsNotNull(original);
}
[TestMethod]
public void GetFromCache_ShouldReturnCachedVersion()
{
var copy1 = _dataAccessTestClass.GetDateTimeTicks();
var copy2 = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreEqual(copy1, copy2);
}
[TestMethod]
public void GetFromCache_ShouldRespectTimeToLive()
{
_dataAccessTestClass.ClearDateTimeTicks();
var copy1 = _dataAccessTestClass.GetDateTimeTicks(TimeSpan.FromSeconds(2));
var copy2 = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreEqual(copy1, copy2);
Thread.Sleep(3000);
var copy3 = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreNotEqual(copy1, copy3);
}
[TestMethod]
public void InvalidateCache_ShouldClearCachedVersion()
{
var original = _dataAccessTestClass.GetDateTimeTicks();
_dataAccessTestClass.ClearDateTimeTicks();
var updatedVersion = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreNotEqual(original, updatedVersion);
}
}
The project that I'm working on at the moment uses an IDisposable object in every method in a class. It has started getting tedious re-typing the using block at the start of every method, and was wondering if there was a way to specify a disposable variable for use in every method of the class?
public static class ResourceItemRepository
{
public static ResourceItem GetById(int id)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
// Code goes here...
}
}
public static List<ResourceItem> GetInCateogry(int catId)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
// Code goes here...
}
}
public static ResourceItem.Type GetType(int id)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
// Code goes here...
}
}
}
No, there's nothing particularly geared towards this. You could write:
public static ResourceItem GetById(int id)
{
WithDataContext(db =>
{
// Code goes here...
});
}
// Other methods here, all using WithDataContext
// Now the only method with a using statement:
private static T WithDataContext<T>(Func<TestDataContext, T> function)
{
using (var db = DataContextFactory.Create<TestDataContext>())
{
return function(db);
}
}
I'm not sure that it would be particularly beneficial though.
(Note that I've had to change it from Action<TestDataContext> in my original version to Func<TestDataContext, T> as you want to be able to return values from your methods.)
Frankly, i'd keep the verbose code, but using a snippet instead of typing it all each time.
Either create your own snippet with a special tool or use text-replacement tools like Texter
Maybe a simple refactoring is the best that you can do without resorting to something like PostSharp:
public static class ResourceItemRepository {
public static ResourceItem GetById(int id) {
using (var db = CreateDataContext()) {
// Code goes here...
}
}
public static List<ResourceItem> GetInCateogry(int catId) {
using (var db = CreateDataContext()) {
// Code goes here...
}
}
public static ResourceItem.Type GetType(int id) {
using (var db = CreateDataContext()) {
// Code goes here...
}
}
private static TestDataContext CreateDataContext() {
return DataContextFactory.Create<TestDataContext>();
}
}