Is there someone who can explain how to code dialog from bot framework properly?
I'm trying to code from scratch using the empty bot template to understand every pieces of code and why and how they piece together. But even after reading so many times I don't how it is properly implemented or coding for dialogs in Microsoft Bot Framework. I've read the documentation from microsoft many times and many version or doc from microsoft but still can't comprehend how it link together every piece of code. Even blogs or website i found did not explain why such pieces of code is needed but just ask you to add this and that. I understand concept but not the mechanics. The code seems to span from startup.cs, yourMainBotLogic.cs, dialogClassName.cs, BotAccessors.cs which confuse me which are the steps the program is run on and how.
Please explain in detail why the pieces of code/components is needed/what use it has and why it has to be there in such files (e.g. Startup.cs). For example;
var accessors = new BotAccessors(conversationState) { ConversationDialogState = conversationState.CreateProperty<DialogState>("DialogState"), }; return accessors;
Create a accessor for the DialogState and then return it. This is just example and my description of the code might not be right.
Your question about how everything fits together is a bit broad, but I will attempt some explanation:
startup.cs: bot configuration should be loaded here, and singletons created. Including IStatePropertyAccessors. Many of the samples contain a BotConfig file with bot specific setup code, and call it from startup. Many samples also contain a bot file. Bot files can make loading some bot services easier. But, they aren't necessary. Ids and passwords can still be retrieved from App Settings, or web.config and your code can create the services.
Some things usually initialized in startup are:
ICredentialProvider is used by the sdk to create the BotAdapter and provide JWT Token Auth. For single appid/password bots, the SDK provides a SimpleCredentialProvider. If your bot is using the integration libraries, you can create one during the IBot initialization, or just supply the botConfig with appid/pass:
webapi:
public static void Register(HttpConfiguration config)
{
config.MapBotFramework(botConfig =>
{
var appId = ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppIdKey];
var pass = ConfigurationManager.AppSettings[MicrosoftAppCredentials.MicrosoftAppPasswordKey];
botConfig.UseMicrosoftApplicationIdentity(appId, pass);
}
}
netcore:
public void ConfigureServices(IServiceCollection services)
{
services.AddBot(options =>
{
options.CredentialProvider = new SimpleCredentialProvider(appId, appPassword);
});
}
IStorage is an implementation for interacting with a state store. The sdk provides MemoryStorage CosmosDbStorage and AzureBlobStorage These are each just using JsonSerializer to store and retrieve objects from the underlying storage.
BotState are objects that provide keys into the IStorage implementation. The SDK provides three examples:
ConversationState scoped by {channelId}/conversations/{conversationId}
UserState scoped by {channelId}/users/{userId}
PrivateConversationState scoped by {channelId}/conversations/{conversationId}/users/{userId}
IStatePropertyAccessors These are an implementation layer providing for typed access into the scoped BotState explained above. When a get/set is performed, the actual state store is queried and persisted (through an internal cache provided by the sdk).
BotAccessors.cs is just a container to hold the state classes and IStatePropertyAccessors. This isn't needed, but is for convenience.
yourMainBotLogic.cs: this is where the adapter's OnTurn implementation exists, and should load the dialog stack and process the user's message. The dialog stack is managed by a dialog set that contains an IStatePropertyAccessor of DialogData type. When a get is performed on this property accessor by calling create context, the state store is queried to fill the dialog stack of the DialogContext.
dialogClassName.cs is a dialog implementation. The specific dialog types are delineated here: Dialog types Examples of how to use them are in the samples on github and documentation.
As with other asp.net applications, startup is run when the application first loads (see aspnet-web-api-poster or lifecycle-of-an-aspnet-mvc-5-application and note that the Microsoft.Bot.Builder.Integration.AspNet.Core uses an IApplicationBuilder extension to add the message handler to the request pipeline ApplicationBuilderExtensions and Microsoft.Bot.Builder.Integration.AspNet.WebApi uses an HttpMessageHandler implementation). However, you can choose to not use the integration libraries, and create your own controllers. Like this sample: MVC-Bot
V4 additional references
Implement sequential conversation flow
Create advanced conversation flow using branches and loops
Gather user input using a dialog prompt
Managing state
Save user and conversation data
Implement custom storage for your bot
Related
Testing the MAUI app dev by creating a simple Android application.
I've defined an collection and UI elements in MainPage.xaml/cs.
public ObservableCollection<Person> persons = new();
Also defined an broadcast Intent receiver.
internal class TestReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
// handle intent
}
}
I want to access persons collection defined in MainPage and add items to it. There is so many pages defined what is what. App.xaml/Mainpage.xaml/MainActivity/AppShell. I'm a bit lost.
Why broadcast receivers?
I suggest a different approach of your problem.
Make a class. Lets call it PersonService. Put the collection there, add method to add/remove persons. Inject it as singleton, and use it across your app.
Tomorrow when you decide not to use some static list, but database (or web-api), all you have to do is edit it at one place.
Another alternative.
Use messaging. CommunityToolkit.MAUI/MVVM have messaging system.
When you decide to go from Android to IOS? It will be much easier.
If you still want to do Android Broadcasts, please go ahead. But google search some of my alternatives. It saves me a lot of work every day, and works stable.
In the latest bot samples, we can see that bot is being added to services collection as below
services.AddTransient<IBot, MyBot>();
but in older samples, we saw below approach
services.AddBot<MyBot>(options => { });
Here I am trying to understand the benefits of adding bot using AddTransient() over using AddBot().
What I know is that internally AddBot uses AddTransient only, then why use AddTransient. Referred remarks section from this link.
You can see in the source code that the AddBot methods are used for automatically adding a bot adapter to DI in addition to the bot and for configuring bot-related options like credentials and error handling. The conventions for using the Bot Builder v4 SDK were very different when those samples were made, and the bot's configuration along with its credentials were loaded from something called a bot file. The current convention for using the SDK is much easier because it takes advantage of ASP.NET Core automatically loading the app's configuration from appsettings.json. Since we're not using AddBot anymore you'll notice that the adapter is added to DI explicitly, and you can configure things like error handling and middleware either by accessing the properties and methods of the adapter directly or by deriving your own adapter class, as seen in the samples.
Advance warning: Im an absolute newbie to Dynamics CRM!
Intention
I want to have a feature like Lync/Skype integration but use my own URL. (Click on any telephone number inside CRM and call it).
For eg. assuming I have a web service which can initiate a call per URL: http://telephony.com/call?nr=012345678. Now, whenever a CRM user clicks onto a telephone number field (in forms and views) inside the CRM my web service should be called instead of Skype/Lync.
In fact I'm trying to reproduce sth. like the InGenius Connecter.
Attempts
I already tried to inject a JS web resource to a specific formular (in my case it was the default contact form) and override the Mscrm.ReadFormUtilities.openPhoneClient callback (which seems to handle the Lync/Skype integration).
function load() {
// override integrated CTC (Lync/Skype)
Mscrm.ReadFormUtilities.openPhoneClient = function (telephoneNr) {
// redirect user to my web service
window.location.replace("http://telephony.com/call?nr="+telephoneNr);
return;
}
}
Found this method at: Disable Lync completely
This does work well in forms of Dynamics 2015 (my custom link pops up instead of Skype/Lync). However, this does only work on entity forms since I can't inject web resources into an entity view.
My other ideas how to implement such a feature are:
Inject global JS resource which disables Lync/Skype and encapsulate every telephone number with link to my custom URL.
Extend/Manipulate Lync/Skype integration to use my custom URL instead of Lync/Skype.
Write plugin which encapsulate telephone numbers server side.
Question
Since I have a grasp understanding of Dynamics and no experience in plugin/resource development, I'm left a bit confused with these questions.
Is it possible to achieve any of the three ideas above ?
If not, any idea how InGenius solved this problem ?
Do you have any other idea/resources about this topic ?
Currently I found two options available to achieve a custom CTC feature. (Both has the downside of not being officialy supported by the dynamics crm.)
Global Ribbon
Pretty simple: Add a Click-To-Call button to global ribbon which is only enabled on specific grids when one row is selected.
This button refers to an JS-Action which retrieves the telephone number via ODATA and then launches the dial process.
Global Ribbon CustomRule injection
Add a global button to ribbon which refers to a JS resource per <CustomRule>. The JScript then unbinds all actions from links with .ms-crm-Phone classes and replaces its href-attribute.
This would be useful if one want to override the integrated "Skype / Lync - Click to Dial" feature with his own logic.
I didn't test this method until now, so I can't guarentee it's working !
Note: I will include example scripts as soon I got the time.
I'm looking for recommendations on how to best share code between multiple Visual Studio projects. I'm struggling with a fundamental topic and trying to get some ideas to get over it.
My solution has:
Several web app projects
Several standalone process projects, eg Windows Services and/or console apps and/or Azure WebJobs
An example of functionality which is common to all projects is the need to call some common web service, or the need to read and write from Amazon S3, for example.
Where I struggle is this: Obviously the code that implements the common functionality should be broken out on its own, for example in a separate class library project. To talk to S3 for example, the code needs to know my Amazon credentials, S3 endpoints, etc. - all these things would normally be stored in app configuration files. But I don't like the idea of putting config files in class library projects because it binds a particular implementation to them. But in order to not do that, I have to pass in this information from the calling project. So for example the web app's web.config and the console app's app.config files contain this connection information. When calling the S3 code, I would assumingly pass this config info into the shared code.
However, this seems yucky* to me and I'm not sure why. It still feels to me like I'm "binding" the S3 code (for example) to a particular config method, if that makes sense. I'm not sure if my feeling is a mis-formed bias.
*For example, I may have an arbitrary amount of configuration data that would have to be passed in:
Connection strings
Credentials for web services
API endpoints
Arbitrary data from my app's config settings (which some of the callers will need, but others won't, so lots of times the data will just be useless yet I have to do the work of passing something in)
So every time I added a config variable in my main app, I'd have to modify the constructor of the common code. Things would be in constant motion.
Can you give me suggestions on this?
I like to use Configuration objects as parameters, that way the signature never changes, even if you add/remove properties. For instance....
public class AmazonConfigSettings {
public string AWSkey { get; set; }
public string ApiEndpoint { get; set; }
......
}
Your signature could then always look like:
public MySharedClass(AmazonConfigSettings config) { .... }
Even if (when) Amazon overhauls their webservice settings completely.
I am trying to figure out how to setup a StructureMap3 configuration, that works in both a WebApi and in a Console application, like:
For<ISession>().HybridHttpOrThreadLocalScoped().Use(p => p.GetInstance<TestingContainer>().GetSession());
For console apps I would like the object to live as long as the thread lives, and for websites as long as the http-session lives.
This is possible with MVC websites because HybridHttpOrThreadLocalScoped use the HttpSessionState to determine whether to create a new instance or to reuse an existing instance.
WebApi doesn't have this HttpSessionState and therefore HybridHttpOrThreadLocalScoped won't work.
If I didn't care about the console app, then I would probably configure structuremap with Transient() or AlwaysUnique or similar.
So, what is the equivalent to HybridHttpOrThreadLocalScoped when there are no HttpSessionState instance.
Thank you.
EDIT
-to rearrange the question...
In general you should favor Nested Containers for lifecycle management. The reasons behind this are exactly what you've just noted, that in some situations using either Thread, HTTP, or hybrid scoped simply doesn't work. I've seen it cause huge issues before where people assume DB connections are being disposed because they are in other environments, but in one environment they aren't. Also, the explicitness is nice.
To do this set the dependencies you want disposed per request to Transient (the default) and dispose of the nested container at the end of the request. I've written about this workflow in webapi here. Additionally the official docs recommend this nuget.
For the console app you'll want to do something like this:
//parent Container set up at app start
public void On_UserAction()
{
//global container set up at app start, either use ObjectFactory (bad, deprecated and to be removed) or just keep track of it yourself.
using(var nestedContainer = GlobalContainer.GetNestedContainer())
{
var dependency = nestedContainer.GetInstance<DependencyThatHandlesUserInput>();
}
}
and that's it, the using block handles all the disposal for you.
If you have any other questions please ask, I've spent a lot of time on this sort of thing :).