How to execute WhenAnyValue delegate asynchronously? - c#

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);
}

Related

ReactiveUI connected Observable CanExecute with IsExecuting properties

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)
);

Missing type map or unsupported mapping

Hello,
I'm currently building an ASP.NET backend.
For the mapping of the entities and the model object I'm using AutoMapper(Version 4.2.1).
When I send a post request with Postman for creating a task, I always get an exception which says:
Missing type map configuration or unsupported mapping.
Mapping types: NewTask -> Task TaskManager.Web.Api.Models.NewTask ->
TaskManager.Data.Entities.Task
Destination path: Task
Source value: TaskManager.Web.Api.Models.NewTask
I tried several things out but never came to an result which worked.
Here is my AutoMapperAdapter.cs:
I always get the errormessage in this file
namespace TaskManager.Common.TypeMapping {
public class AutoMapperAdapter : IAutoMapper {
public T Map<T>(object objectToMap) {
try {
return Mapper.Map<T>(objectToMap);
} catch (Exception ex) {
throw ex;
}
}
}
}
And the AddTaskMaintenance Processor:
namespace TaskManager.Web.Api.MaintenanceProcessing {
public class AddTaskMaintenanceProcessor : IAddTaskMaintenanceProcessor {
private readonly IAutoMapper _autoMapper;
private readonly IAddTaskQueryProcessor _queryProcessor;
public AddTaskMaintenanceProcessor(IAddTaskQueryProcessor queryProcessor, IAutoMapper autoMapper) {
_queryProcessor = queryProcessor;
_autoMapper = autoMapper;
}
public Models.Task AddTask(NewTask newTask) {
var taskEntity = _autoMapper.Map<Data.Entities.Task>(newTask);
_queryProcessor.AddTask(taskEntity);
var task = _autoMapper.Map<Models.Task>(taskEntity);
return task;
}
}
}
The mapping of the model objects to the entities and reverse looks like that:
namespace TaskManager.Web.Api.AutoMappingConfiguration {
public class NewTaskToTaskEntityAutoMapperTypeConfigurator : IAutoMapperTypeConfigurator {
public void Configure() {
Mapper.Initialize(conf => {
conf.CreateMap<NewTask, TaskManager.Data.Entities.Task>()
.ForMember(opt => opt.Version, x => x.Ignore())
.ForMember(opt => opt.CreatedBy, x => x.Ignore())
.ForMember(opt => opt.TaskId, x => x.Ignore())
.ForMember(opt => opt.CreatedDate, x => x.Ignore())
.ForMember(opt => opt.CompletedDate, x => x.Ignore())
.ForMember(opt => opt.Status, x => x.Ignore())
.ForMember(opt => opt.Users, x => x.Ignore());
});
}
}
}
And the other file:
namespace TaskManager.Web.Api.AutoMappingConfiguration {
public class TaskEntitiyToTaskAutoMapperTypeConfigurator : IAutoMapperTypeConfigurator {
public void Configure() {
Mapper.Initialize(conf => {
conf.CreateMap<Task, Models.Task>()
.ForMember(opt => opt.Links, x => x.Ignore())
.ForMember(opt => opt.Status, x => x.Ignore())
.ForMember(opt => opt.Assignees, x => x.ResolveUsing<TaskAssigneesResolver>());
});
}
}
}
My questions:
What could probably be the problem with my code?
Do I have to upgrade the version of the AutoMapper?

Nhibernate adding new entry for child object on update

public CaseMap()
{
Id(c => c.ID);//.GeneratedBy.Increment();
Map(c => c.CIN);
Map(c => c.CaseNumber);
Map(c => c.Name);
Map(c => c.RegistryNumber);
References(c => c.ApplicantType).Column("FK_ApplicantTypeId").Cascade.None();
References(c => c.ApplicationType).Column("FK_ApplicationTypeId").Cascade.None();
References(c => c.AVSUser).Column("FK_UserId").Cascade.None();
References(x => x.Program).Column("FK_ProgramId").Cascade.None();
References(c => c.CaseApplicant).Column("FK_CaseApplicantId").Cascade.SaveUpdate();
References(c => c.Address).Column("FK_AddressId").Cascade.SaveUpdate();
References(x => x.Spouse).Column("FK_SpouseId").Cascade.SaveUpdate();
Map(c => c.SpouseLink);
Map(c => c.Status);
Map(c => c.CreatedDate);
Map(c => c.UpdatedDate);
Map(c => c.IsArchived);
Table("tbl_Case");
}
Update method -
public bool Update(T persistableEntity)
{
if (persistableEntity != null)
{
session.BeginTransaction();
session.Update(persistableEntity);
session.Transaction.Commit();
return true;
}
return false;
}
The Update Call updates the Case table but adding new entry for child objects like CaseApplicant,Address and Spouse.
There was code error
Instead of
cCase.Address = new CaseAddress();
cCase.Address.Street = record.ResidenceStreet;
It should have been just
cCase.Address.Street = record.ResidenceStreet;
cCase.Address = new CaseAddress() was creating a new object for CaseAddress result in new entry.
Also the update method should be session.Merge instead of update
public bool Update(T persistableEntity)
{
if (persistableEntity != null)
{
session.BeginTransaction();
session.Merge(persistableEntity);
session.Transaction.Commit();
return true;
}
return false;
}

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

Create an iqueryable list that's distinct

I have a list of objects, GroupStudentStatus, that I need to make distinct.
I wrote the class below to do this.
The 2 attributes that are relevant are GroupStudentStatus.IsLastActionRemoved (DateTime) and GroupStudentStatus.Student.Guid.
protected List<GroupStudentStatus> RemovedStudents
{
get
{
return AllStudents.Where(s => s.IsLastActionRemoved).Distinct().OrderByDescending(d => d.LastActionDate).ToList();
}
}
public class GroupStudentStatusComparer : IEqualityComparer<GroupStudentStatus>
{
public GroupStudentStatus Compare(GroupStudentStatus x, GroupStudentStatus y)
{
//get the student that was last removed
if (!Equals(x, y))
{
return x.LastActionDate > y.LastActionDate ? x : y;
}
return x;
}
public bool Equals(GroupStudentStatus x, GroupStudentStatus y)
{
return x.Student.Guid.Equals(y.Student.Guid);
}
public int GetHashCode(GroupStudentStatus obj)
{
return obj.Student.Guid.GetHashCode();
}
}
I think this is right, except I can't figure out how to test it.
I was trying to do this:
return AllStudents.Where(s => s.IsLastActionRemoved)
.Distinct(new GroupStudentStatusComparer((x, y) => x.Compare(x,y)))
.OrderByDescending(d => d.LastActionDate).ToList();
return AllStudents.Where(s => s.IsLastActionRemoved)
.Distinct(new GroupStudentStatusComparer())
.OrderByDescending(d => d.LastActionDate)
.ToList();
return AllStudents.Where(s => s.IsLastActionRemoved).GroupBy(gss => gss.Student).Select(g => new GroupStudentStatus(g.Key, g.Select(gss2 => gss2.LastActionDate).Max(), true)).OrderByDescending(d => d.LastActionDate).ToList();
Ended up using groupBy.

Categories