what is the proper way of subscribing to a ReactiveCommand in ReactiveUI 8.2 - reactive-programming

I have following snippet in ViewModel.
public ReactiveCommand<object, System.Reactive.Unit> LoadCustomerDetails;
ReactiveCommand<OrderViewPager<SalesOrderOrderOptionsEnum>, CommandSubmitResultDto<List<SalesOrderDto>>> _loadSalesOrderList;
public ReactiveCommand<OrderViewPager<SalesOrderOrderOptionsEnum>, CommandSubmitResultDto<List<SalesOrderDto>>> LoadSalesOrderList
{
get { return _loadSalesOrderList; }
private set { this.RaiseAndSetIfChanged(ref _loadSalesOrderList, value); }
}
this.LoadSalesOrderList = ReactiveCommand.CreateFromTask<Pager<OrderOptionsEnum>, CommandSubmitResultDto<List<SalesOrderDto>>>(
async filter =>
{
Debug.WriteLine("Load SalesOrderList...");
Debug.WriteLine("Customer Id : " + SelectedCustomerId);
await LoadCustomerDetails.Execute();
var result = await SalesOrderMobApi.GetByCustomerTraderEntityIdPaged(SelectedCustomerId, filter, null, SalesOrderTypeEnum.SalesOrder, SalesOrderPOOptions.NotOriginatingFromPurchaseOrder);
return result;
})
.DisposeWith(ViewModelBindings.Value);
this.LoadSalesOrderList.ThrownExceptions
.Subscribe(ex =>
{
Debug.WriteLine("Load SalesOrderList Failed!");
});
this.LoadSalesOrderList
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(result =>
{
if (result.PagingInfo.CurrentPage > 1)
{
foreach (var item in result.Data)
{
SalesOrdersList.Add(SalesOrderVMM.From(item));
}
}
else
{
SalesOrdersList.Clear();
foreach (var item in result.Data)
{
SalesOrdersList.Add(SalesOrderVMM.From(item));
}
}
});
LoadCustomerDetails = ReactiveCommand.CreateFromTask<object, System.Reactive.Unit>(
async _ =>
{
Debug.WriteLine(SelectedCustomerId);
var customers = await TraderEntityMobApi.GetById(SelectedCustomerId);
var customer = customers.Data;
SelectedCustomer = customer;
return System.Reactive.Unit.Default;
}
).DisposeWith(ViewModelBindings.Value);
It sometimes gives exception as follows.
System.NullReferenceException: Object reference not set to an instance of an object.
06-20 16:05:02.480 I/MonoDroid(15304): at DistributrIII.Mobile.Lib.VM.SalesOrder.CreateSOListVM.<RegisterObservables>b__43_2 (System.Collections.Generic.List`1[T] result) [0x0000e] in C:\Users\gayanbu\Source\Repos\Distributr 3.0 UI\Mobile\DistributrIII.Mobile.Lib\VM\SalesOrder\CreateSOListVM.cs:131 .at System.Reactive.AnonymousSafeObserver`1[T].OnNext (T value) [0x0000a] in <99f8205c51c44bb480747b577b8001ff>:0
06-20 16:05:02.480 I/MonoDroid(15304): at System.Reactive.ScheduledObserver`1[T].Run (System.Object state, System.Action`1[T] recurse) [0x000f5] in <99f8205c51c44bb480747b577b8001ff>:0
06-20 16:05:02.480 I/MonoDroid(15304): at System.Reactive.Concurrency.Scheduler+<>c__DisplayClass49_0`1[TState].<InvokeRec1>b__0 (TState state1) [0x0001e] in <99f8205c51c44bb480747b577b8001ff>:0
06-20 16:05:02.480 I/MonoDroid(15304): at System.Reactive.Concurrency.Scheduler.InvokeRec1[TState] (System.Reactive.Concurrency.IScheduler scheduler,
I guess it tries to execute the code inside reactive command ,LoadSalesOrderList even the result of this is null. How to handle this ? Could someone kindly explain the proper way of subscribing to Reactive Command. I am executing this command in the page load as, this.ViewModel.LoadSalesOrderList.Execute().subscribe(new Pager<OrderOptionsEnum>())
Thanks!

if you want your command when throw exception to be catched in ThrownExceptions execute command like Observable.Return(input).InvokeCommand(Command).DisposeWith(disposable) where input is the input for command and Command is the name of the Command

Related

why scala in this case can't use single abstract method?

i use spring boot's RedisTemplate with scala, and i write this code:
redisTemplate1.executePipelined(new RedisCallback[String] {
override def doInRedis(connection: RedisConnection): String = {
MyCode......
null
}
}, redisTemplate1.getValueSerializer)
usually, it's can be wrote like this:
redisTemplate1.executePipelined((connection: RedisConnection) => {
MyCode......
null
}, redisTemplate1.getValueSerializer)
and this style is running well in java:
redisTemplate1.executePipelined((RedisConnection conn) -> {
MyCode......
return null;
}, redisTemplate1.getValueSerializer());
but when i compile in this style with scala, i get an error, so why this happend and how can i use single abstract method in this case?
overloaded method value executePipelined with alternatives:
(x$1: org.springframework.data.redis.core.RedisCallback[_],x$2: org.springframework.data.redis.serializer.RedisSerializer[_])java.util.List[Object] <and>
(x$1: org.springframework.data.redis.core.SessionCallback[_],x$2: org.springframework.data.redis.serializer.RedisSerializer[_])java.util.List[Object]
cannot be applied to (org.springframework.data.redis.connection.RedisConnection => Null, org.springframework.data.redis.serializer.RedisSerializer[?0(in method syncSegmentSrc)])
redisTemplate1.executePipelined((connection: RedisConnection) => {
the executePipelined function source code like this:
#Override
public List<Object> executePipelined(SessionCallback<?> session, #Nullable RedisSerializer<?> resultSerializer) {
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(session, "Callback object must not be null");
RedisConnectionFactory factory = getRequiredConnectionFactory();
// bind connection
RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
try {
return execute((RedisCallback<List<Object>>) connection -> {
connection.openPipeline();
boolean pipelinedClosed = false;
try {
Object result = executeSession(session);
if (result != null) {
throw new InvalidDataAccessApiUsageException(
"Callback cannot return a non-null value as it gets overwritten by the pipeline");
}
List<Object> closePipeline = connection.closePipeline();
pipelinedClosed = true;
return deserializeMixedResults(closePipeline, resultSerializer, hashKeySerializer, hashValueSerializer);
} finally {
if (!pipelinedClosed) {
connection.closePipeline();
}
}
});
} finally {
RedisConnectionUtils.unbindConnection(factory);
}
}
#Override
public List<Object> executePipelined(RedisCallback<?> action, #Nullable RedisSerializer<?> resultSerializer) {
return execute((RedisCallback<List<Object>>) connection -> {
connection.openPipeline();
boolean pipelinedClosed = false;
try {
Object result = action.doInRedis(connection);
if (result != null) {
throw new InvalidDataAccessApiUsageException(
"Callback cannot return a non-null value as it gets overwritten by the pipeline");
}
List<Object> closePipeline = connection.closePipeline();
pipelinedClosed = true;
return deserializeMixedResults(closePipeline, resultSerializer, hashKeySerializer, hashValueSerializer);
} finally {
if (!pipelinedClosed) {
connection.closePipeline();
}
}
});
}
In cases like this, it should help to specify the type explicitly:
redisTemplate1.executePipelined({ connection =>
YourCode…
}: RedisCallback[String], redisTemplate1.getValueSerializer)
Note the type ascription : RedisCallback[String].

My async metho didn't go after "for" iteration

My code like this:
Future<List<ListClass>> getIndexList() async {
return Future.delayed(delayTime).then((_) async {
//initialize
List<ListClass> resultListItems = new List<ListClass>();
await listCollection.get().then((value) async {
var v = value.data;
for (var data in v) {
String userId = data['userId'];
String authorName=await UserTable().getUserNameById(userId);
print("createUserName=" + authorName);
resultListItems.add(
ListClass(header, content, userId, wordCount, authorName));
}
print("resultListItems.length="+resultListItems.length.toString());
return resultListItems;
});
return resultListItems;
});
}
When I debug,it shows that this method return null,and after the for,the print("resultListItems"); doesn't run too. How can I fix this?Thanks!!
I have found the problem,the for clause doesn't run completely.One of my data in database is null,so it runs with bug.Sorry for my fault

Transaction not working with Web API .net core 2.2

I have a async web API that has services and a connection to the db using an EF context.
In one of the services, where I inject the context using DI I want to make a simple transactional service, like:
public async Task<int> ServiceMethod()
{
using (var transaction = context.Database.BeginTransaction())
{
await context.Table1.AddAsync(new Table1());
await context.SaveChangeAsync();
CallOtherservice(context);
await context.SaveChangeAsync();
transaction.Commit();
}
}
CallOtherService(Context context)
{
await context.Table2.AddAsync();
}
I have now mocked CallOtherService() to throw an exception and I expect nothing to be commited, but it looks like changes to Table1 persist, amid the transaction trhat should stop them. I have tried calling Rollback() in a try catch statement and using a TransactionScope() instead, but both were useless. Also, I have noticed that after calling .BeginTransaction(), context.Database.CurrentTransaction is null, which I consider a bit weird.
EDIT:
the method that I test:
public async Task<int> AddMatchAsync(Models.Database.Match match)
{
//If match is null, nothing can be done
if (match == null)
return 0;
else
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
try
{
//If the match already exists, return its id
var dbId = await ExistsMatchAsync(match.HomeTeam.Name, match.AwayTeam.Name, match.Time);
if (dbId > 0)
{
log.Info($"Found match with id {dbId}");
return dbId;
}
//add computable data
match = await AttachMatchName(match);
match.Season = CommonServices.toSeason(match);
// Prepare the relational entities accordingly
match = PrepareTeamsForAddMatch(match).Result;
match = PrepareLeagueForAddMatch(match).Result;
//add the match and save it to the DB
await context.Matches.AddAsync(match);
await context.SaveChangesAsync();
log.Info($"Successfully added a new match! id: {match.Id}");
// Create new TeamLeagues entities if needed
leaguesService.CreateTeamLeaguesForNewMatchAsync(context, match);
await context.SaveChangesAsync();
scope.Complete();
return match.Id;
}
catch (Exception ex)
{
log.Error($"Error adding match - Rolling back: {ex}");
throw;
}
}
}
}
and the test:
[Test]
public void AddMatchSaveChangesTransaction()
{
//Arrange
var exceptiontext = "This exception should prevent the match from being saved";
var leagueServiceMock = new Mock<ILeaguesService>();
leagueServiceMock.Setup(p => p.CreateTeamLeaguesForNewMatchAsync(It.IsAny<InFormCoreContext>(),It.IsAny<Match>()))
.Throws(new Exception(exceptiontext));
leagueServiceMock.Setup(p => p.GetLeagueAsync(It.IsAny<string>()))
.ReturnsAsync((string name) => new League{Id = 1, Name = name });
var match = new Match
{
HomeTeam = new Team { Name = "Liverpool" },
AwayTeam = new Team { Name = "Everton" },
League = new League { Name = "PL", IsSummer = true },
Time = DateTime.UtcNow,
HomeGoals = 3,
AwayGoals = 0
};
var mcount = context.Matches.Count();
//Act
var matchService = new MatchesService(context, leagueServiceMock.Object, new LogNLog(), TeamsService);
//Assert
var ex = Assert.ThrowsAsync<Exception>(async () => await matchService.AddMatchAsync(match));
Assert.AreEqual(ex.Message, exceptiontext);
Assert.AreEqual(mcount, context.Matches.Count(), "The match has been added - the transaction does not work");
}`
```

How to update many documents using UpdateManyAsync

I have the following method to update a document in MongoDB:
public async Task UpdateAsync(T entity)
{
await _collection.ReplaceOneAsync(filter => filter.Id == entity.Id, entity);
}
Which works fine - I was just wondering if anybody has an example of how the UpdateManyAsync function works:
public async Task UpdateManyAsync(IEnumerable<T> entities)
{
await _collection.UpdateManyAsync(); // What are the parameters here
}
Any advice is appreciated!
UpdateManyAsync works the same way as update with multi: true in Mongo shell. So you can specify filtering condition and update operation and it will affect multiple documents. For instance to increment all a fields where a is greater than 10 you can use this method:
var builder = Builders<SampleClass>.Update;
await myCollection.UpdateManyAsync(x => x.a > 10, builder.Inc(x => x.a, 1));
I guess you'd like to replace multiple documents. That can be achieved using bulkWrite method. If you need generic method in C# then you can introduce some kind of marker interface to build filter part of replace operation:
public interface IMongoIdentity
{
ObjectId Id { get; set; }
}
Then you can add generic constaint to your class and use BuikWrite in .NET like below:
class YourRepository<T> where T : IMongoIdentity
{
IMongoCollection<T> collection;
public async Task UpdateManyAsync(IEnumerable<T> entities)
{
var updates = new List<WriteModel<T>>();
var filterBuilder = Builders<T>.Filter;
foreach (var doc in entities)
{
var filter = filterBuilder.Where(x => x.Id == doc.Id);
updates.Add(new ReplaceOneModel<T>(filter, doc));
}
await collection.BulkWriteAsync(updates);
}
}
As #mickl answer, you can not use x=> x.Id because it is a Generic
Use as below:
public async Task<string> UpdateManyAsync(IEnumerable<T> entities)
{
var updates = new List<WriteModel<T>>();
var filterBuilder = Builders<T>.Filter;
foreach (var doc in entities)
{
foreach (PropertyInfo prop in typeof(T).GetProperties())
{
if (prop.Name == "Id")
{
var filter = filterBuilder.Eq(prop.Name, prop.GetValue(doc));
updates.Add(new ReplaceOneModel<T>(filter, doc));
break;
}
}
}
BulkWriteResult result = await _collection.BulkWriteAsync(updates);
return result.ModifiedCount.ToString();
}
Or you go by Bson attribute:
public async Task UpdateManyAsync(IEnumerable<TEntity> objs, CancellationToken cancellationToken = default)
{
var updates = new List<WriteModel<TEntity>>();
var filterBuilder = Builders<TEntity>.Filter;
foreach (var obj in objs)
{
foreach (var prop in typeof(TEntity).GetProperties())
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
var bsonId = attr as BsonIdAttribute;
if (bsonId != null)
{
var filter = filterBuilder.Eq(prop.Name, prop.GetValue(obj));
updates.Add(new ReplaceOneModel<TEntity>(filter, obj));
break;
}
}
}
}
await _dbCollection.BulkWriteAsync(updates, null, cancellationToken);
}

The current thread has no Dispatcher associated with it

I'm creating an observable and I'm creating the subscription separately:
class CustomQuery {
string Name;
IObservable<int> Occurrences;
}
public IEnumerable<CustomQuery> GatherCustomQueryObservables()
{
yield return new CustomQuery() {
Name = "NameXXX",
Occurrences = Observable.Create<int>(
observer =>
{
int occurrences = this.webservice.GetOccurrences()
observer.OnNext(occurrences);
return System.Reactive.Disposables.Disposable.Empty;
}
);
}
By other hand, there's another method deals with these CustomQueries:
public void CommitCustomQueryObservables(IEnumerable<CustomQuery> queries)
{
foreach (CustomQuery query in queries)
{
query.Occurrences
.Select(o => o)
.SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default)
.ObserveOn(System.Reactive.Concurrency.DispatcherScheduler.Current)
.Subscribe(
occurrences =>
{
string strOccurrences = occurrences > 0 ? occurrences.ToString() : "";
this.Label.Text = strOccurrences;
}
);
}
}
Nevertheless, I'm getting a System.InvalidOperationException exception:
The current thread has no Dispatcher associated with it.
The last line of the stacktrace is at
System.Reactive.Concurrency.DispatcherScheduler.get_Current(‌​).
I don't quite figure out how to handle it.
Any ideas?
For Windows Forms you need to use the ControlScheduler for synchronization, not the DispatcherScheduler.
Now you've added the System.Reactive.Windows.Forms package this can be achieved by simply using [observable].ObserveOn([control]); in your example this could be:
public void CommitCustomQueryObservables(IEnumerable<CustomQuery> queries)
{
foreach (CustomQuery query in queries)
{
query.Occurrences
.Select(o => o)
.SubscribeOn(System.Reactive.Concurrency.TaskPoolScheduler.Default)
.ObserveOn(this.Label)
.Subscribe(
occurrences =>
{
string strOccurrences = occurrences > 0 ? occurrences.ToString() : "";
this.Label.Text = strOccurrences;
}
);
}
}