silverlight, using Observables on WCF calls, casting IEvent<T> where T : AsyncCompletedEventArgs - silverlight

i have a question using Observables in Silverlight 4 to make WCF calls.
Consider the simple webservice call below.
var adminclient = ServiceProxy.WithFactory<AuthenticationClient>();
var results= Observable.FromEvent<AuthorizeAdministratorCompletedEventArgs>(
s => adminclient.AuthorizeAdministratorCompleted += s,
s => adminclient.AuthorizeAdministratorCompleted -= s).ObserveOnDispatcher();
adminclient.AuthorizeAdministratorAsync();
results.Subscribe(e =>
{
//Enable UI Button
});
i have implemented an extension method, that wraps the subscribe method, it does some error validation on the return.
On the return results.Subscribe(e =>
e is System.Collections.Generic.Event<AuthorizeAdministratorCompletedEventArgs>
almost every query will have a different return type such as:
e is System.Collections.Generic.Event<AsyncCompletedEventArgs>
if i had a wrapper that looked something like this, how can i cast every type of xxxCompletedEventArgs to its base type AsyncCompletedEventArgs so that i can access e.EventArgs and inspect the Error property
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action<TSource> onNext = null, Action onError = null, Action onFinal = null)
{
Action<TSource> onNextWrapper = (s) =>
{
var args = (System.Collections.Generic.IEvent<AsyncCompletedEventArgs>)s;
try
{
if (WCFExceptionHandler.HandleError(args.EventArgs))
{
if (onNext != null)
onNext(s);
}
else
{
if (onError != null)
onError();
}
}
finally
{
if (onFinal != null)
onFinal();
}
};
return source.Subscribe<TSource>(onNextWrapper, (e) => { throw e; });
}
The code above will fail
Unable to cast object of type 'System.Collections.Generic.Event1[MyProject.Provider.AuthorizeAdministratorCompletedEventArgs]' to type 'System.Collections.Generic.IEvent1[System.ComponentModel.AsyncCompletedEventArgs]'
This is the method definition of WCFExceptionHandler.HandleError(args.EventArgs))
public static bool HandleError(AsyncCompletedEventArgs e)

I'd probably change you extension method so that it acts to handle the the events as a non blocking operator (much the same as the majority of the Rx extension method operators). Something like:
public static IObservable<IEvent<TEventArgs>> GetHandledEvents<TEventArgs>(this IObservable<IEvent<TEventArgs>> source)
where TEventArgs : AsyncCompletedEventArgs
{
return Observable.CreateWithDisposable<IEvent<TEventArgs>>(observer =>
{
return source.Subscribe(evt =>
{
try
{
if (WCFExceptionHandler.HandleError(evt.EventArgs))
{
observer.OnNext(evt);
}
else
{
observer.OnError(new Exception("Some Exception"));
}
}
finally
{
observer.OnError(new Exception("Some Other Exception"));
}
},
observer.OnError,
observer.OnCompleted);
});
}
Then call it through:
results.GetHandledEvents()
.Finally(() => DoSomethingFinally())
.Subscribe(e =>
{
//Enable UI Button
},
ex => HandleException(ex),
() => HandleComplete());
I think this should solve your issues as the events will funnel through as their original type and it ensures that HandleError gets event args that are the correct type.

Related

.NET MAUI Setting an item SelectedIndex on Page Load is Delayed

When the view appears on the screen there is a short delay setting the values to each control. Is it possible to set the values before the user sees the view?
public UserSettingsView()
{
InitializeComponent();
LoadAsync();
}
private async void LoadAsync()
{
try
{
// Loading data from API
Languages = await _languageService.GetAsync(AccessToken);
USStates = await _uSStateService.GetAsync(AccessToken);
// Assigning the list to the ItemSource of each Picker.
ddlLanguages.ItemsSource = Languages;
ddlUSStates.ItemsSource = USStates;
// Getting the user's preferred settings
var userSettings = await _accountService.GetSettingsAsync(UserID, AccessToken);
if (userSettings != null)
{
// Setting user values to each Picker control.
// This is where the delay happens.
ddlLanguages.SelectedIndex = Languages.FindIndex(x => x.ID == userSettings .LanguageID);
ddlUSStates.SelectedIndex = USStates.FindIndex(x => x.ID == userSettings .USStateID);
cbAge.IsChecked = currentSettings.AgeQualified;
}
}
catch
{
await DisplayAlert("Oh no!", "Error loading the page", "OK");
}
}
To resolve the delay, I am passing the two lists for the languages and the States from the previous page.
public UserSettingsView(List<Language> _languages, List<USState> _usStates)
{
InitializeComponent();
Languages = _languages;
USStates = _usStates;
LoadAsync();
}
private async void LoadAsync()
{
try
{
ddlLanguages.ItemsSource = Languages;
ddlUSStates.ItemsSource = USStates;
var currentSettings = await _accountService.GetSettingsAsync(UserID, AccessToken);
if (currentSettings != null)
{
ddlLanguages.SelectedIndex = Languages.FindIndex(x => x.ID == currentSettings.LanguageID);
ddlUSStates.SelectedIndex = USStates.FindIndex(x => x.ID == currentSettings.USStateID);
switchAgeQualification.IsToggled = currentSettings.AgeQualified;
}
}
catch
{
await DisplayAlert("Error", "Could not load page data", "OK");
}
}
If I understand correctly, you currently have a line like this:
await Navigation.PushModalAsync(new UserSettingsView());
I don't see the types of the properties involved, but the basic idea is to do all the slow awaits BEFORE doing new UserSettingsView....
Something like this:
public class UserSettingsData
{
SomeType1 Languages;
SomeType2 USStates;
SomeType3 UserSettings;
}
...
// Slow await calls.
var data = await UserSettingsView.PrepAsync(UserId, AccessToken);
// Now create and display the view.
await Navigation.PushModalAsync(new UserSettingsView(data));
...
public static async UserSettingsData PrepAsync(SomeType4 UserId, SomeType5 AccessToken)
{
var data = new UserSettingsData();
data.Languages = await _accountService.GetSettingsAsync(...);
data.USStates = await ...;
data.UserSettings = await ...;
}
public UserSettingsView(UserSettingsData data)
{
...
// NOT AN ASYNC METHOD, so happens immediately, before page is shown.
Load(data);
}
// NOT AN ASYNC METHOD, so happens immediately.
private void Load(UserSettingsData data)
{
Languages = data.Languages;
USStates = data.USStates;
var userSettings = data.UserSettings;
...
// if still need DisplayAlert
Dispatcher.InvokeOnMainThread(async () =>
await DisplayAlert...
);
}
Replace "SomeType" etc with your actual types.

Filter a broadcasted property in Angular 9

I have a dbService that calls the database!
//DB service code -----------------------
private changedEvents = new BehaviorSubject<IEvent[]>(null);
broadCastEvents = this.changedEvents.asObservable();
getEvents() {
this.http.get<IEvent[]>(this.configUrl + 'Event/GetEvents').subscribe(
(data: IEvent[]) => {
this.changedEvents.next(data)
});
}
In my component on ngOnInit I starts listening to this
ngOnInit(): void {
this.dbService.broadCastEvents.subscribe(data => {
this.events = data;
})
// this.dbService.getEvents();
}
Now all of this working like a charm! But now I'm only interested in records where this.events.type == 2
I tried by a standard filtering like below!
ngOnInit(): void {
this.dbService.broadCastEvents.subscribe(data => {
this.events = data.filter(event => event.eventTypeRefId == 2);
})
// this.dbService.getEvents();
}
But it results in the following Error!? Any ideas how to this in a better way (that works :-))
core.js:6241 ERROR TypeError: Cannot read property 'filter' of null
at SafeSubscriber._next (start-training.component.ts:26)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
at SafeSubscriber.next (Subscriber.js:122)
at Subscriber._next (Subscriber.js:72)
at Subscriber.next (Subscriber.js:49)
at BehaviorSubject._subscribe (BehaviorSubject.js:14)
at BehaviorSubject._trySubscribe (Observable.js:42)
at BehaviorSubject._trySubscribe (Subject.js:81)
at BehaviorSubject.subscribe (Observable.js:28)
at Observable._subscribe (Observable.js:76)
ngOnInit(): void {
this.dbService.broadCastEvents.pipe(filter(event => event.eventTypeRefId == 2)).subscribe(data => {
this.events = data
})
// this.dbService.getEvents();
}
Reference:
https://rxjs-dev.firebaseapp.com/guide/operators
There are multiple ways for it. One way is to use array filter like you did. Other way would be to use RxJS filter pipe as shown by #enno.void. However both these methods might still throw an error when the notification is null. And since the default value of your BehaviorSubject is null, there is high probability of hitting the error again.
One workaround for it is to use ReplaySubject with buffer 1 instead. It's similar to BehaviorSubject in that it holds/buffer the last emitted value and emits it immediately upon subscription, except without the need for a default value. So the need for initial null value is mitigated.
Try the following
private changedEvents = new ReplaySubject<IEvent[]>(1);
broadCastEvents = this.changedEvents.asObservable();
...
Now the error might still occur if you were to push null or undefined to the observable. So in the filter condition you could check for the truthiness of the value as well.
ngOnInit(): void {
this.dbService.broadCastEvents.subscribe(data => {
this.events = data.filter(event => {
if (event) {
return event.eventTypeRefId == 2;
}
return false;
});
});
}

Cannot read property 'emit' of undefined when trying to emit a document

I am trying to create a design for tags of entities in PouchDB with ReactJS. I managed to save my design using the put function, but when I query my design, the response is just an empty array and I am getting following error in console:
TypeError: Cannot read property 'emit' of undefined
I think the problem is in my function that I later use as a map parameter to my design variable:
function emitTagsMap(doc)
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
/* Here is probably the problem - this.db is undefined */
this.db.emit(x, null);
});
}
}
};
this.db is declared in constructor:
constructor(service, name)
{
if (!service || !name) throw new Error("PouchDatabase initialized incorrectly");
this.name = name;
this.db = new PouchDB(name);
this.service = service;
this.tagsView();
}
Please bare in mind that I am completely new to PouchDB.
Any ideas how can I initialize the emit function?
Thank you in advance.
I assume, that your function is a part of a JavaScript class (otherwise you have to explain the idea with this). In ES6, you have to bind this to your regular functions. You have two options:
First - bind it via constructor:
constructor() {
this.emitTagsMap = this.emitTagsMap.bind(this);
}
Second - declare the function as an arrow one. This way, react will bind it for you:
emitTagsMap = (doc) =>
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
/* Here is probably the problem - this.db is undefined */
this.db.emit(x, null);
});
}
}
};
You don't need to call emit over the database object.
Try this:
function emitTagsMap(doc)
{
if (doc !== undefined)
{
if (Array.isArray(doc.tags))
{
doc.tags.forEach(x =>
{
emit(x, null);
});
}
}
};
According to the PouchDB docs a design document is formed like this:
// first create a new design doc and pass your map function as string into it
var ddoc = {
_id: "_design/my_index",
views: {
by_name: {
map: "function (doc) { if (doc !== undefined) { if (Array.isArray(doc.tags)) { doc.tags.forEach(x => { emit(x, null); }); } } }"
}
}
};
// save it
db.put(ddoc).then(function () {
// success!
}).catch(function (err) {
// some error (maybe a 409, because it already exists?)
});
//Then you actually query it, by using the name you gave the design document when you saved it:
db.query('my_index/by_name').then(function (res) {
// got the query results
}).catch(function (err) {
// some error
});
https://pouchdb.com/guides/queries.html

How to chain dependent TaskEither operations in FP-TS

I am new to FP-TS and still don't quite understand how to work with TaskEither. I am attempting to asynchronously read a file and then parse the resulting string with yaml-parse-promise.
==EDIT==
I updated the code with the full contents of the file to give more context and applied some of the suggestions provided by MnZrK. Sorry I am still new to FP-TS and I am still struggling with getting the types to match up.
Now my error is with the the map(printConfig) line:
Argument of type '<E>(fa: TaskEither<E, AppConfig>) => TaskEither<E, AppConfig>' is not assignable to parameter of type '(a: TaskEither<unknown, AppConfig>) => Either<unknown, Task<any>>'.
Type 'TaskEither<unknown, AppConfig>' is not assignable to type 'Either<unknown, Task<any>>'.
Type 'TaskEither<unknown, AppConfig>' is missing the following properties from type 'Right<Task<any>>': _tag, rightts(2345)
[ I resolved this by using the getOrElse from TaskEither rather than from Either library]
==END EDIT==
I have successfully performed this with IOEither as a synchronous operation with this project: https://github.com/anotherhale/fp-ts_sync-example.
I have also looked at the example code here:
https://gcanti.github.io/fp-ts/recipes/async.html
Full code is here: https://github.com/anotherhale/fp-ts_async-example
import { pipe } from 'fp-ts/lib/pipeable'
import { TaskEither, tryCatch, chain, map, getOrElse } from "fp-ts/lib/TaskEither";
import * as T from 'fp-ts/lib/Task';
import { promises as fsPromises } from 'fs';
const yamlPromise = require('js-yaml-promise');
// const path = require('path');
export interface AppConfig {
service: {
interface: string
port: number
};
}
function readFileAsyncAsTaskEither(path: string): TaskEither<unknown, string> {
return tryCatch(() => fsPromises.readFile(path, 'utf8'), e => e)
}
function readYamlAsTaskEither(content: string): TaskEither<unknown, AppConfig> {
return tryCatch(() => yamlPromise.safeLoad(content), e => e)
}
// function getConf(filePath:string){
// return pipe(
// readFileAsyncAsTaskEither(filePath)()).then(
// file=>pipe(file,foldE(
// e=>left(e),
// r=>right(readYamlAsTaskEither(r)().then(yaml=>
// pipe(yaml,foldE(
// e=>left(e),
// c=>right(c)
// ))
// ).catch(e=>left(e)))
// ))
// ).catch(e=>left(e))
// }
function getConf(filePath: string): TaskEither<unknown, AppConfig> {
return pipe(
readFileAsyncAsTaskEither(filePath),
chain(readYamlAsTaskEither)
)
}
function printConfig(config: AppConfig): AppConfig {
console.log("AppConfig is: ", config);
return config;
}
async function main(filePath: string): Promise<void> {
const program: T.Task<void> = pipe(
getConf(filePath),
map(printConfig),
getOrElse(e => {
return T.of(undefined);
})
);
await program();
}
main('./app-config.yaml')
The resulting output is:
{ _tag: 'Right', right: Promise { <pending> } }
But I want the resulting AppConfig:
{ service: { interface: '127.0.0.1', port: 9090 } }
All these e=>left(e) and .catch(e=>left(e)) are unnecessary.
Your second approach is more idiomatic.
// convert nodejs-callback-style function to function returning TaskEither
const readFile = taskify(fs.readFile);
// I don't think there is `taskify` alternative for Promise-returning functions but you can write it yourself quite easily
const readYamlAsTaskEither = r => tryCatch(() => readYaml(r), e => e);
function getConf(filePath: string): TaskEither<unknown, AppConfig> {
return pipe(
readFile(path.resolve(filePath)),
chain(readYamlAsTaskEither)
);
}
Now your getConf returns TaskEither<unknown, AppConfig> which is actually a () => Promise<Either<unknown, AppConfig>>. If you have more specific error type than unknown, then use that instead.
In order to "unpack" the actual value, you need to have some main entry point function where you compose other stuff you need to do with your config using map or chain (ie printing it to console), then apply some error handling to get rid of Either part and finally get just Task (which is actually simply lazy () => Promise):
import * as T from 'fp-ts/lib/Task';
function printConfig(config: AppConfig): AppConfig {
console.log("AppConfig is", config);
return config;
}
function doSomethingElseWithYourConfig(config: AppConfig): TaskEither<unknown, void> {
// ...
}
async function main(filePath: string): Promise<void> {
const program: T.Task<void> = pipe(
getConf(filePath),
map(printConfig),
chain(doSomethingElseWithYourConfig),
// getting rid of `Either` by using `getOrElse` or `fold`
getOrElse(e => {
// error handling (putting it to the console, sending to sentry.io, whatever is needed for you app)
// ...
return T.of(undefined);
})
);
await program();
}

Retrieve only one item with MVVM light in Silverlight

To grab some content from a WCF Data Service into my View Model is straight forward:
public const string RequestsPropertyName = "Requests";
private DataServiceCollection<Request> _requests = null;
public DataServiceCollection<Request> Requests
{
get { return _requests; }
set
{
if (_requests == value) { return; }
var oldValue = _requests;
_requests = value;
RaisePropertyChanged(RequestsPropertyName, oldValue, value, true);
}
}
and then
Requests.LoadAsync(query);
But what if I have a property which is not a collection?
public const string RequestDetailsPropertyName = "RequestDetails";
private Request _requestDetails = null;
public Request RequestDetails
{
get { return _requestDetails; }
and so on.
Where do I get the 'LoadAsync(query)' method from?
Thank you,
Ueli
This is a pretty simple thing to do. You just need to use the DomainContext in your application. This is where you create your query from, then apply the result to your property.
Here is an example of what this might look like in your code:
void LoadRequest(int requstID)
{
var query = workContext.GetRequestByIDQuery(requestID);
workContext.Load(query, lo =>
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
if (lo.HasError)
throw lo.Error;
else
RequestDetails = lo.Entities.Single();
});
}, null);
}
In this example, the workContext object is the DomainContext. The query is an specific version on the server - you can also just contruct the query client side with:
.Where(r => r.RequestID == requestID)
After the async call, it thows any errors that occurred from the async call, and then returns the only entity returned. If you get more than 1 entity, you might use .First() instead.
If this is not enough to get you going, let me know and I can explain further.

Resources