ReactiveUI connected Observable CanExecute with IsExecuting properties - c#

If CleanCommand is executing then SearchCommand will be inactive.
If SearchCommand is executing then CleanCommand will be inactive.
Barcode is
public long Barcode
{
get => _barcode;
set => this.RaiseAndSetIfChanged(ref _barcode, value);
}
private readonly ObservableAsPropertyHelper<bool> _isSearching;
private readonly ObservableAsPropertyHelper<bool> _isCleaning;
public bool IsSearching => _isSearching.Value;
public bool IsCleaning => _isCleaning.Value;
public ReactiveCommand<Unit, Unit> SearchCommand { get; }
public ReactiveCommand<Unit, Unit> CleanCommand { get; }
In constructor
SearchCommand = ReactiveCommand.CreateFromObservable(Search, SearchCanExecute());
SearchCommand.IsExecuting.ToProperty(this, x => x.IsSearching, out _isSearching);
CleanCommand = ReactiveCommand.CreateFromObservable(Clean, CleanCanExecute());
CleanCommand.IsExecuting.ToProperty(this, x => x.IsCleaning, out _isCleaning);
In class
IObservable<bool> SearchCanExecute()
{
bool isCleaningSuited = !IsCleaning;
bool isBarcodeSuited = Barcode > 0;
return Observable.Return(isBarcodeSuited);
}
IObservable<bool> CleanCanExecute()
{
bool isSearchingSuited = !IsSearching;
return Observable.Return(isSearchingSuited);
}
I get the process status with *IsExecuting.ToProperty()
I get values with properties like Barcode.
Should i use WhenAny* method or can i do it this way?

I'd probably do this.WhenAnyObservable on the command execution and pipe that into the respective command canExecute. That way you don't really need the functions that return observables and it's a bit more fluid.
Something along the lines of the following
var commandOneCanExecute = this.WhenAnyObservable(x => x.CommandTwo.IsExecuting).StartWith(false).Select(x => !x);
var commandTwoCanExecute = this.WhenAnyObservable(x => x.CommandOne.IsExecuting).StartWith(false).Select(x => !x);
CommandOne = ReactiveCommand.CreateFromObservable(ExecuteCommand, commandOneCanExecute);
CommandTwo = ReactiveCommand.CreateFromObservable(ExecuteCommand, commandTwoCanExecute);

I think i solved the problem.
IObservable<bool> searchCommandObservable = this.WhenAnyObservable(x => x.SearchCommand.IsExecuting).StartWith(false).Select(x => x);
IObservable<bool> cleanCommandObservable = this.WhenAnyObservable(x => x.CleanCommand.IsExecuting).StartWith(false).Select(x => x);
IObservable<bool> barcodeObservable = this.WhenAnyValue(x => x.Barcode).StartWith(0).Select(x => x > 0);
IObservable<bool> searchCanExecute = Observable.Merge(
searchCommandObservable.Select(x => !x),
cleanCommandObservable.Select(x => !x),
barcodeObservable
);
IObservable<bool> cleanCanExecute = Observable.Merge(
searchCommandObservable.Select(x => !x),
cleanCommandObservable.Select(x => !x)
);

Related

EF or Linq: Get list that includes another list but filter the second list where its property is true

I have two Classes:
public Case()
{
public IList<CaseThirdParty> CaseThirdParties { get; private set; }
}
public CaseThirdParty()
{
public bool Received_Claim { get; private set; }
}
I am using ef core to get all Cases but only get
CaseThirdParties where Received_Claim is True, here is the query:
await _context.Cases.Include(c => c.CaseThirdParties)
.Where(c => c.CaseThirdParties.Any(c => c.Received_Claim))
.ToArrayAsync();
Or
await _context.Cases.Include(c => c.CaseThirdParties)
.Where(c => c.CaseThirdParties.Where(c => c.Received_Claim).Count>0)
.ToArrayAsync();
But it retrieves CaseThirdParties where Received_Claim is flase
If you use ef core 5.0 or later you can use Filtered include
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager#filtered-include
like this:
await _context.Cases.Include(case => case.CaseThirdParties.Where(c => c.Received_Claim))
.ToArrayAsync();
if not you have to create an anonymous object with Case and CaseThirdParties like this:
await _context.Cases.Where(case => case.CaseThirdParties.Any(case3rd => case3rd.Received_Claim))
.Select(case =>
{
Case = case,
CaseThirdParties = case.CaseThirdParties.Where(case3rd => case3rd.Received_Claim)
})
.ToArrayAsync();

Passing an array of differently typed Action<T> to a method

I have the following code, which stores a list of actions to perform based on the type of value passed as an argument...
Action<int> intAction = x => Console.WriteLine($"You sent an int: {x}");
Action<bool> boolAction = x => Console.WriteLine($"You sent a bool: {x}");
var funcsByInputType = new Dictionary<Type, Action<object>>();
funcsByInputType[typeof(int)] = (object x) => intAction((int)x);
funcsByInputType[typeof(bool)] = (object x) => boolAction((bool)x);
PerformAction(true);
PerformAction(42);
void PerformAction<TValue>(TValue value)
{
if (funcsByInputType!.TryGetValue(typeof(TValue), out Action<object>? action))
{
action(value!);
}
}
This works as expected.
What I now want to do is create the dictionary from a single method that I only call once, so I can rewrite my code like this instead.
Action<int> intAction = x => Console.WriteLine($"You sent an int: {x}");
Action<bool> boolAction = x => Console.WriteLine($"You sent a bool: {x}");
funcsByInputType = MakeFuncsByInputType(intAction, boolAction);
PerformAction(true);
PerformAction(42);
void PerformAction<TValue>(TValue value)
{
if (funcsByInputType!.TryGetValue(typeof(TValue), out Action<object>? action))
{
action(value!);
}
}
How could I write the MakeFuncsByInputType method?
Note that I don't want to make multiple calls to the method, and I don't want to have my parameters defined as params object[]
This is not possible, because via an Action<object> as an alias of Action<string> I would be able to pass 42 instead of a string.
So I had to go with a builder pattern instead.
public static partial class Reducer
{
public static Builder New() => new Builder();
public class Builder<TState>
{
private bool Built;
private ImmutableArray<KeyValuePair<Type, Func<TState, object, Result<TState>>>> TypesAndReducers;
internal Builder() => TypesAndReducers = ImmutableArray.Create<KeyValuePair<Type, Func<TState, object, Result<TState>>>>();
public Builder<TState> Add<TDelta>(Func<TState, TDelta, Result<TState>> reducer)
{
if (reducer is null)
throw new ArgumentNullException(nameof(reducer));
EnsureNotBuilt();
TypesAndReducers = TypesAndReducers.Add(new(typeof(TDelta), (state, delta) => reducer(state, (TDelta)delta)));
return this;
}
public Func<TState, object, Result<TState>> Build()
{
EnsureNotBuilt();
if (TypesAndReducers.Length == 0)
throw new InvalidOperationException("Must add at least one reducer to build.");
Built = true;
var dictionary = TypesAndReducers
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(x => x.Value));
return (TState state, object delta) =>
{
if (delta is null)
throw new ArgumentNullException(nameof(delta));
if (!dictionary.TryGetValue(delta.GetType(), out var reducers))
return (false, state);
bool anyChanged = false;
TState newState = state;
foreach(var reducer in reducers)
{
(bool changed, newState) = reducer(newState, delta);
anyChanged |= changed;
}
return anyChanged
? (true, newState)
: (false, state);
};
}
private void EnsureNotBuilt()
{
if (Built)
throw new InvalidOperationException("Reducer has already been built.");
}
}
}

How to execute WhenAnyValue delegate asynchronously?

I have this code generating a "Calculated" or "Output" property every time another property changes. The derived property is generated correctly, but since CreateBlurImage is a bit long-running, the UI gets frozen as it runs.
What would be the correct way to get the same end result, but asynchronously?
EDIT this is the current version of my code after some suggestions from #Shane, but still with an UI that keeps frozen until all the processing completes. Notice that there are quite a handful of cascading updates, if that matters:
public ColetaIsis Model { get; private set; }
public string NomePaciente { get { return Model?.NomePaciente; } }
public DateTime DataRealização { get { return Model.DataRealização; } }
public BitmapSource Listras
{
get { return _listras; }
set { this.RaiseAndSetIfChanged(ref _listras, value); }
}
BitmapSource _listras;
public double[,] Grayscale { get { return _grayscale.Value; } }
readonly ObservableAsPropertyHelper<double[,]> _grayscale;
public double[,] BlurMenor { get { return _blurMenor.Value; } }
readonly ObservableAsPropertyHelper<double[,]> _blurMenor;
public double[,] BlurMaior { get { return _blurMaior.Value; } }
readonly ObservableAsPropertyHelper<double[,]> _blurMaior;
public double[,] Diferença { get { return _diferença.Value; } }
readonly ObservableAsPropertyHelper<double[,]> _diferença;
public BitmapSource FiltradaMenor { get { return _filtradaMenor?.Value; } }
readonly ObservableAsPropertyHelper<BitmapSource> _filtradaMenor;
public BitmapSource FiltradaMaior { get { return _filtradaMaior?.Value; } }
readonly ObservableAsPropertyHelper<BitmapSource> _filtradaMaior;
public BitmapSource ImagemDiferença { get { return _imagemDiferença?.Value; } }
readonly ObservableAsPropertyHelper<BitmapSource> _imagemDiferença;
public IEnumerable<ScatterPoint> Picos => _picos;
IEnumerable<ScatterPoint> _picos;
// CONSTRUTOR
public ColetaIsisViewModel(ColetaIsis model)
{
this.WhenAnyValue(x => x.Listras)
.Where(item => item != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(im => GetArray.FromChannels(im, 0, 1))
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.Grayscale, out _grayscale, scheduler:RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.Grayscale)
.Where(item => item != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(ar => Gaussian.GaussianConvolution(ar, 1.5))
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.BlurMenor, out _blurMenor, scheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.BlurMenor)
.Where(item => item != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(ar => Gaussian.VerticalGaussianConvolution(ar, 5))
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.BlurMaior, out _blurMaior, scheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.BlurMenor, x => x.BlurMaior)
.Where(tuple => tuple.Item1 != null && tuple.Item2 != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(tuple => ArrayOperations.Diferença(tuple.Item1, tuple.Item2))
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.Diferença, out _diferença, scheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.BlurMenor)
.Where(item => item != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(ar => { ConversorImagem.Converter(ar, out BitmapSource im); return im; })
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.FiltradaMenor, out _filtradaMenor, scheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.BlurMaior)
.Where(item => item != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(ar => { ConversorImagem.Converter(ar, out BitmapSource im); return im; })
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.FiltradaMaior, out _filtradaMaior, scheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.Diferença)
.Where(item => item != null)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(ar => { ConversorImagem.Converter(ar, out BitmapSource im); return im; })
//.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.ImagemDiferença, out _imagemDiferença, scheduler: RxApp.MainThreadScheduler);
Model = model;
Listras = Model.Listras; // fires up the initial cascading updates
}
Would any of these ways work? This way the CreateBlurImage part is done off the UIThread
public MyClass()
{
this.WhenAnyValue(x => x.StripedImage)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(im => CreateBlurImage(im))
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.Filtered, out _filtered);
}
or I'm pretty sure specifying the scheduler on ToProperty will be the same as above
public MyClass()
{
this.WhenAnyValue(x => x.StripedImage)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(im => CreateBlurImage(im))
.ToProperty(this, x => x.Filtered, out _filtered, scheduler:RxApp.MainThreadScheduler);
}

Linq Any returns true despite all values in the collection being false

I see very strange behavior in properties value calculation.
I have a property HasChanged, which is true if any of it's dependent properties is true.
But I got a result - all arguments are false and the result is true.
I am using MVVM Light framework, and each property is INotifyPropertyChanged
Here are helper functions
private static bool PropertyWrapper(bool value, [CallerMemberName] string callerName = "")
{
Logger.Debug($"[{callerName}: {value}]");
return value;
}
private static T PropertyWrapper<T>(Expression<Func<T>> property)
{
var compiled = property.Compile();
var result = (T)compiled.DynamicInvoke();
Logger.Debug($"[{GetName(property)}: {result}]");
return result;
}
private static string GetName<T>(Expression<Func<T>> expr)
{
var mexpr = expr.Body as MemberExpression;
if (mexpr == null) return "(null)";
if (mexpr.Member == null) return "((null))";
return mexpr.Member.Name;
}
And this is the code
public virtual bool HasChanged => PropertyWrapper(new[] {
PropertyWrapper(() => TitleChanged),
PropertyWrapper(() => EnglishTitleChanged),
PropertyWrapper(() => OriginalTitleChanged),
PropertyWrapper(() => PlotChanged),
PropertyWrapper(() => OutlineChanged),
PropertyWrapper(() => DirectorChanged),
PropertyWrapper(() => YearChanged),
PropertyWrapper(() => MovieRolesChanged),
PropertyWrapper(() => GenresChanged),
PropertyWrapper(() => CountriesChanged)
}.Any(), "HasChanged");
public bool YearChanged => this.state == State.Edit && this.source.MovieDescription.Year != this.clone.MovieDescription.Year;
public bool TitleChanged => HasTitleChanges();
public bool EnglishTitleChanged => HasEnglishTitleChanges();
public bool OriginalTitleChanged => HasOriginalTitleChanges();
public bool PlotChanged => HasDescriptionChanges();
public bool DirectorChanged => HasDirectorChanges();
public bool OutlineChanged => HasOutlineChanges();
public bool MovieRolesChanged => HasMovieRolesChanges();
public bool CountriesChanged => HasCountriesChanges();
public bool GenresChanged => HasGenresChanges();
and what is written to log
[TitleChanged: False]
[EnglishTitleChanged: False]
[OriginalTitleChanged: False]
[PlotChanged: False]
[OutlineChanged: False]
[DirectorChanged: False]
[YearChanged: False]
[MovieRolesChanged: False]
[GenresChanged: False]
[CountriesChanged: False]
[HasChanged: True]
It looks impossible dull, but I can't imagine how can it be.
Please, explain me the reason of such behavior.
Any without parameters returns if there are any elements in the collection. To get what you want you have to check the value of your elements as your Any predicate:
public virtual bool HasChanged => PropertyWrapper(new[] {
//...
}.Any(q => q), "HasChanged");

NHibernate extension for querying non mapped property

I'm looking for a way to get total price count from the Costs list in my object. I can't get Projections.Sum to work in my QueryOver so I tried another way but I'm having problems with it. I want to use a unmapped property in my QueryOver. I found this example but it's giving an error.
Object:
public class Participant
{
public int Id { get; set; }
public double TotalPersonalCosts { get { return Costs.Where(x => x.Code.Equals("Persoonlijk") && x.CostApprovalStatus == CostApprovalStatus.AdministratorApproved).Sum(x => x.Price.Amount); } }
public IList<Cost> Costs { get; set; }
}
The property TotalPersonalCosts is not mapped and contains the total price count.
Extension Class:
public static class ParticipantExtensions
{
private static string BuildPropertyName(string alias, string property)
{
if (!string.IsNullOrEmpty(alias))
{
return string.Format("{0}.{1}", alias, property);
}
return property;
}
public static IProjection ProcessTotalPersonalCosts(System.Linq.Expressions.Expression expr)
{
Expression<Func<Participant, double>> w = r => r.TotalPersonalCosts;
string aliasName = ExpressionProcessor.FindMemberExpression(expr);
string totalPersonalCostName = ExpressionProcessor.FindMemberExpression(w.Body);
PropertyProjection totalPersonalCostProjection =
Projections.Property(BuildPropertyName(aliasName, totalPersonalCostName));
return totalPersonalCostProjection;
}
}
My QueryOver:
public override PagedList<AccountantViewInfo> Execute()
{
ExpressionProcessor.RegisterCustomProjection(
() => default(Participant).TotalPersonalCosts,
expr => ParticipantExtensions.ProcessTotalPersonalCosts(expr.Expression));
AccountantViewInfo infoLine = null;
Trip tr = null;
Participant pa = null;
Cost c = null;
Price p = null;
var infoLines = Session.QueryOver(() => tr)
.JoinAlias(() => tr.Participants, () => pa);
if (_status == 0)
infoLines.Where(() => pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPrinted || pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPaid);
else if (_status == 1)
infoLines.Where(() => pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPrinted);
else
infoLines.Where(() => pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPaid);
infoLines.WhereRestrictionOn(() => pa.Employee.Id).IsIn(_employeeIds)
.Select(
Projections.Property("pa.Id").WithAlias(() => infoLine.Id),
Projections.Property("pa.Employee").WithAlias(() => infoLine.Employee),
Projections.Property("pa.ProjectCode").WithAlias(() => infoLine.ProjectCode),
Projections.Property("tr.Id").WithAlias(() => infoLine.TripId),
Projections.Property("tr.Destination").WithAlias(() => infoLine.Destination),
Projections.Property("tr.Period").WithAlias(() => infoLine.Period),
Projections.Property("pa.TotalPersonalCosts").WithAlias(() => infoLine.Period)
);
infoLines.TransformUsing(Transformers.AliasToBean<AccountantViewInfo>());
var count = infoLines.List<AccountantViewInfo>().Count();
var items = infoLines.List<AccountantViewInfo>().ToList().Skip((_myPage - 1) * _itemsPerPage).Take(_itemsPerPage).Distinct();
return new PagedList<AccountantViewInfo>
{
Items = items.ToList(),
Page = _myPage,
ResultsPerPage = _itemsPerPage,
TotalResults = count,
};
}
Here the .Expression property is not found from expr.
I don't know what I'm doing wrong. Any help or alternatives would be much appreciated!
Solution with Projection.Sum() thx to xanatos
.Select(
Projections.Group(() => pa.Id).WithAlias(() => infoLine.Id),
Projections.Group(() => pa.Employee).WithAlias(() => infoLine.Employee),
Projections.Group(() => pa.ProjectCode).WithAlias(() => infoLine.ProjectCode),
Projections.Group(() => tr.Id).WithAlias(() => infoLine.TripId),
Projections.Group(() => tr.Destination).WithAlias(() => infoLine.Destination),
Projections.Group(() => tr.Period).WithAlias(() => infoLine.Period),
Projections.Sum(() => c.Price.Amount).WithAlias(() => infoLine.TotalPersonalCost)
);
You can't use unmapped columns as projection columns of a NHibernate query.
And the way you are trying to do it is conceptually wrong: the ParticipantExtensions methods will be called BEFORE executing the query to the server, and their purpose is to modify the SQL query that will be executed. An IProjection (the "thing" that is returned by ProcessTotalPersonaCosts) is a something that will be put between the SELECT and the FROM in the query. The TotalCosts can't be returned by the SQL server because the SQL doesn't know about TotalCosts

Categories