I am building a Prism 2.1 demo by way of getting up to speed with the technology. I am having a problem with CompositePresentationEvents published and subscribed via the Event Aggregation service. The event subscription works fine if I set a strong reference (KeepSubscriberReferenceAlive = true), but it fails if I set a weak reference (KeepSubscriberReferenceAlive omitted).
I would like to subscribe with a weak reference, so that I don't have to manage unsubscribing from the event. Is there any way to do that? Why is a strong reference required here? Thanks for your help!
Here are the particulars: My demo app is single-threaded and has two regions, Navigator and Workspace, and three modules, NavigatorModule, WorkspaceAModule, and WorkspaceBModule. The NavigatorModule has two buttons, 'Show Workspace A' and 'Show Workspace B'. When one of these buttons is clicked, an ICommand is invoked that publishes a CompositePresentationEvent called ViewRequested. The event carries a string payload that specifies which workspace module should be shown.
Here is the event declaration, from the app's Infrastructure project:
using Microsoft.Practices.Composite.Presentation.Events;
namespace Prism2Demo.Common.Events
{
public class ViewRequestedEvent : CompositePresentationEvent<string>
{
}
}
Here is the event publishing code, from the Navigator module:
// Publish ViewRequestedEvent
var eventAggregator = viewModel.Container.Resolve<IEventAggregator>();
var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
viewRequestedEvent.Publish(workspaceName);
Here is the event subscription code, which each Workspace module includes in its Initialize() method:
// Subscribe to ViewRequestedEvent
var eventAggregator = m_Container.Resolve<IEventAggregator>();
var viewRequestedEvent = eventAggregator.GetEvent<ViewRequestedEvent>();
viewRequestedEvent.Subscribe(this.ViewRequestedEventHandler, ThreadOption.PublisherThread, true);
The Subscribe() statement is shown with a strong reference.
Thanks again for your help.
A couple of things to check:
Make sure that your EventAggregator instance is being correctly registered with the container or it may itself be garbage collected:
container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
Also make sure that you have a strong reference to the subscribed object held somewhere (this in your subscription code).
Related
We're trying to use Elsa for a project, but we're facing some difficulties now, so need suggestions badly. One thing we're trying to do is to create an Activity based on existing HttpEndpoint. However, with the source code got from https://github.com/elsa-workflows/elsa-core, after googled some docs and samples, we haven't been able to figure it out.
Here is what exactly we're attempting to do.
create a new Activity based on HttpEndpoint
make the Path include WorkflowInstanceId by default
a little bit more customizations needed in our scenario
Looking forward to suggestions and guidance. Thanks!
You could do something like what the Webhooks module is doing. Instead of
inheriting from HttpEndpoint, it uses an IActivityTypeProvider implementation that dynamically yields new activity types that reuse HttpEndpoint.
In your case, your activity type provider would only have to yield a single activity type (e.g. MyEndpoint) that pre-configures any and all aspects that you want, including the default Path property value.
Deriving a new activity type from HttpEndpoint directly works too. You will have to implement your own bookmark provider that provides HttpEndpointBookmark objects - the HTTP middleware for the HttpEndpoint activity relies on that.
Example:
public class MyEndpointBookmarkProvider : BookmarkProvider<HttpEndpointBookmark>
{
public override bool SupportsActivity(BookmarkProviderContext context) => context.ActivityType.TypeName == nameof(MyEndpoint);
public override async ValueTask<IEnumerable<BookmarkResult>> GetBookmarksAsync(BookmarkProviderContext context, CancellationToken cancellationToken)
{
var path = await context.ReadActivityPropertyAsync<MyEndpoint, PathString>(x => x.Path, cancellationToken);
var methods = (await context.ReadActivityPropertyAsync<MyEndpoint, HashSet<string>>(x => x.Methods, cancellationToken))?.Select(ToLower) ?? Enumerable.Empty<string>();
BookmarkResult CreateBookmark(string method) => Result(new(path, method), nameof(HttpEndpoint));
return methods.Select(CreateBookmark);
}
private static string ToLower(string s) => s.ToLowerInvariant();
}
The above bookmark provider provides bookmarks for your custom activity when the activity being indexed is of type "MyEndpoint".
Alternatively, you might go a different route altogether and simply implement an API endpoint (an ASP.NET Core controller, middleware or route endpoint) that triggers workflows based on your custom activity. I've written some documentation about that process here: https://elsa-workflows.github.io/elsa-core/docs/next/guides/guides-blocking-activities
I am trying to implement a bot which uses Qna services and Azure search.
I am taking help of the C# QnA Maker sample github code.
It is using a BotServices.cs class which is taking a QnA service in its constructor. This Botservice object is being passed to the QnABot class constructor.
I want to use Dialog set in QnABot's constructor which need accessors to be added. I really didn't understand how to add accessor class and use them in the startup.cs
I tried to copy some code from other samples but didn't work.
Please help me to add an accessor to the BotServices constructor so that I can use dialog sets inside of it.
I would like to extend the QnA sample for my purpose.
Can you tell us why you want to pass a dialog set tot the botservices class? this class is only used to reference external services such as QnAMaker and LUIS. If you want to start a Dialog, do so in the OnTurnAsync method of the QnABot.cs class. keep in mind that the this method as it is created in this specific sample will send a response on every message the user sends even if they are working through a dialog. You could change the OnTurnAsync in such a way that the first step in the dialog is to check the QnAMaker. See the enterpriseBot sample to see how to start a dialog as well as adding an accessor to a child Dialog. see the following snipped from the MainDialog.cs class how they added the accessor:
protected override async Task OnStartAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
{
var onboardingAccessor = _userState.CreateProperty<OnboardingState>(nameof(OnboardingState));
var onboardingState = await onboardingAccessor.GetAsync(innerDc.Context, () => new OnboardingState());
var view = new MainResponses();
await view.ReplyWith(innerDc.Context, MainResponses.Intro);
if (string.IsNullOrEmpty(onboardingState.Name))
{
// This is the first time the user is interacting with the bot, so gather onboarding information.
await innerDc.BeginDialogAsync(nameof(OnboardingDialog));
}
}
I am building a bot using the given technology stacks:
Microsoft Bot Builder
Node.js
Dialogflow.ai (api.ai)
We used waterfall model to implement a matched intent Dialog, which includes a couple of handler functions and prompts. After following this scenario I need to identify the entities inside an inner handler function, for a user input.
eg:
Bot : Where do you want to fly?
User: Singapore. (For this we added entities like SIN - Singapore,SIN(Synonym), So I need to resolve the value as SIN)
Any help on this scenario is much appreciated.
Here is a post Using api.ai with microsoft bot framework you can refer for your reqirement, and with a sample at https://github.com/GanadiniAkshay/weatherBot/blob/master/api.ai/index.js. The weather api key leveraged in this sample is out of date, but the waterfall and recognizer api key is still working.
Generally speaking:
Use api-ai-recognizer
Instantiate the apiairecognizer and leveraged builder.IntentDialog to include the recognizer:
var recognizer = new apiairecognizer("<api_key>");
var intents = new builder.IntentDialog({
recognizers: [recognizer]
});
In IntentDialogs, use builder.EntityRecognizer.findEntity(args.entities,'<entity>'); to recognize the intent entities.
Another question about ReactiveUi. I have a ViewModel for an edit form. Model is ReactiveObject. I want to enable savecommand only when changes of object was take place. My try:
var canSaveCommand =
this.WhenAnyValue(vm => vm.CurrentClient)
.Where(client => client != null)
.Select(client =>
client.Changed
)
.Any();
But when the form appears the SaveCommand is already enabled. Where my mistake?
You want to use Switch not SelectMany. SelectMany will not unsubscribe from the previous client. It will merge events from all clients. Switch unsubscribes from the previous client before it subscribes to the next.
var canSaveCommand =
this.WhenAnyValue(vm => vm.CurrentClient)
.Where(client => client != null)
.Select(client =>
client.Changed
)
.Switch()
.Any();
For example the following code makes it clear. Let's say we have a class called AudioChannel It generates audio frames we can can process and send to the speaker.
public class IAudioChannel {
public IObservable<AudioFrame> AudioFrameObservable {get;}
}
Then we might have a list of audio nodes that the user can select but we only want the most current sending audio to the speaker. The below class makes available the currently selected audio node as an observable.
public class AudioListViewModel {
public class IObservable<IAudioChannel> CurrentAudioChannelObservable {get;}
}
Now consider the following code
AudioListViewModel viewModel;
viewModel
.CurrentAudioChannelObservable
.SelectMany(current=>current.AudioFrameObservable)
.Subscribe(frame=>frame.Play());
vs
AudioListViewModel viewModel;
viewModel
.CurrentAudioChannelObservable
.Select(current=>current.AudioFrameObservable)
.Switch()
.Subscribe(frame=>frame.Play());
In the first version as we change the selection of audio nodes we add more and more subscriptions. The audio output quickly becomes a garbled mess of mixed channels. In the second version only one channel is subscribed to at a time and the audio output only plays the output from a single channel.
Many people make this mistake when starting out with RX. For example I found a bug in the ReactiveUI framework that used SelectMany instead of Switch.
However
There is a built in way within ReactiveUI to achieve this in a clear way
There is actually another way to achieve what you want and I will put it in another answer just to show you how to use ReactiveUI.
var canSaveCommand =
this
.WhenAnyObservable(vm => vm.CurrentClient.Changed)
.StartWith(false);
Note that null doesn't have to be explicity handled though you should start with false to make sure a value exists when no observable is available to start with.
WhenAnyObservable
WhenAnyObservable acts a lot like the Rx operator CombineLatest, in
that it watches one or multiple observables and allows you to define a
projection based on the latest value from each. WhenAnyObservable
differs from CombineLatest in that its parameters are expressions,
rather than direct references to the target observables. The impact of
this difference is that the watch set up by WhenAnyObservable is not
tied to the specific observable instances present at the time of
subscription. That is, the observable pointed to by the expression can
be replaced later, and the results of the new observable will still be
captured. An example of where this can come in handy is when a view
wants to observe an observable on a viewmodel, but the viewmodel can
be replaced during the view's lifetime. Rather than needing to
resubscribe to the target observable after every change of viewmodel,
you can use WhenAnyObservable to specify the 'path' to watch. This
allows you to use a single subscription in the view, regardless of the
life of the target viewmodel.
Try changing your Select to a SelectMany. That will then give you an Observable of the changes to be passed into Any instead of an Observable of an Observable of the changes to be passed into Any.
What is the distinction between Windows.UI.WebUI.WebUIApplication versus WinJS.Application?
WinJS.Application is a namespace. WebUIApplication is an object (or a class of object). But consider the following:
WebUIApplication supports the following events:
WebUIApplication.activated
WebUIApplication.resuming
WebUIApplication.suspending
WebUIApplication.navigated
WinJS.Application Namespace defines the following events:
onactivated
oncheckpoint
onerror
onloaded
onready
onsettings
onunload
In particular, why is resuming done with WebUIApplication but not with WinJS.Application, but it seems that activation and checkpoint can be done either way?
Windows.UI.WebUI.WebUIApplication.onresuming = function (args) { ... }; // OK
WinJS.Application.oncheckpoint = function (args) { ... }; // OK
WinJS.Application.onactivated = function (args) { ... }; // OK
WinJS.Application.onresuming = function (args) { ... }; // NOT OK
The APIs you refer to that are in the Windows.* namespace are the actual core of the app model. Everything in WinJS, on the other hand, are wrappers that are intended to simplify that app model where there is value in doing so. For example, most apps need to do some work on the suspending event, and WinJS provides a sessionState object that is automatically saved on suspended and reloaded when the app is relaunched. However, because there's typically no action that WinJS needs to do for resuming, it doesn't wrap that particular event.
The Windows.* (WinRT) APIs, in other words, are the essential core that you have to use to write an app. WinJS is an optional library that is not at all required, but contains many essentials (like controls) that most apps will use anyway.
Typically, then, you'll use the WinJS events for convenience. It's also easy to include resuming in this model: add a handler for the WebUIApplication.oneresuming event, and call WinJS.Application.queueEvent("resuming", ...) which will then route a "resuming" event into the WinJS.Application object. That way you can centralize your handling of application events in one place.
I talk more of the relationship between these in Chapter 3 of my free ebook, Programming Windows Store Apps in HTML, CSS, and JavaScript, Second Edition, currently in preview. See http://aka.ms/BrockschmidtBook2.