BotFramework: How to handle multiple entities for a single Intent? - c#

In LUIS I have created some utterances for which Intent is detected and I have set 3 different Entities for it i.e For Example I am trying to build a bot to detect user's issue related to an application. So when User enters Unable to Open Android I have set intent as Find_Issue and Entities as 1.Product 2.Issue 3.Error. But since Unable to Open Android doesn't contain any error code. I am getting only 2 entities Product & Issue. Now I want to get the value for Error if no error it would be stored as none.
Here is the Task Code so far
[LuisIntent("Find_Issue")]
public async Task getIssue(IDialogContext context, LuisResult result)
{
EntityRecommendation getProduct;
EntityRecommendation getIssue;
EntityRecommendation getError;
if(result.TryFindEntity("Product",out getProduct))
{
chatdetails.issuedetails.product = getProduct.Entity;
}
if (result.TryFindEntity("Issue", out getIssue))
{
chatdetails.issuedetails.issue = getIssue.Entity;
}
if (result.TryFindEntity("Error", out getError))
{
chatdetails.issuedetails.error = getError.Entity;
}
}
chatdetails.issuedetails is the class created to store the values of Product,Issue,Error
I am not getting how to proceed further.

When defining an intent in LUIS you can also define it's Action Parameters. Action Parameters consists of a parameter name, an entity type, a prompt and if it's required or not.
Then, when using the latest version of the BotFramework Nuget package, your LuisDialog will automatically detect that a required parameter (as in your scenario with the Error entity) is missing and will automatically fire a prompt for the parameter, using the message defined in the Action Parameter.
For technical details about how this thing around the action parameters works, please check this thread.

Related

Devart Error when I return database object with IActionResult

I am trying to return the database object that I get from my service in an IActionResult API Call (c# web API project). Whenever I attempt to do that I get this error:
System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles.
Here is my API code that is throwing this:
[HttpGet]
[Route("content")]
public IActionResult GetAllContent()
{
try
{
List<Content> allContent = _contentService.GetAll();
return Ok(allContent);
}
catch (Exception ex)
{
//Log something here
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
I could easily mitigate the error by parsing through the content and creating a dynamic object, but I find it annoying to do whenever I want to return a database object when I'm using the Devart Database Context.
Edit:
Further piece of the error message is this:
$.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.PortalCodeMappings.Content.ContentId.
And I understand the circular reference here, is there a way to tell devart I only want the Content section of this without doing something like this:
allContent.Select(x => new { ... });
Edit: I am using Devart Entity Devloper to generate my models and the dbcontext. I do not use Visual Studio or any IDE.
There are two alternative ways to solve the issue:
Use System.Text.Json (de)serialization
Add JsonIgnoreAttribute to one of the navigation property ends
You can add a custom attribute via the interface of Entity Developer this way:
a) navigate to Model > Settings > Attributes > select the System.Text.Json.dll assembly and make sure that JsonIgnoreAttribute is checked in the window below, press OK
b) select JsonIgnoreAttribute in the Attributes collection of a particular class property

ServiceObjectPropertyException on task,IsReminderSet and task.ReminderDueDate

I am binding a task in ews.
In Microsoft docs it says:
"Binds to an existing task and loads its first class properties."
Due to the List of First Class Properties IsReminderSet is a first class property in task.
Still that property returns an
"ServiceObjectPropertyException was unhandeld:
An unhandled exception of type 'Microsoft.Exchange.WebServices.Data.ServiceObjectPropertyException' occurred in Microsoft.Exchange.WebServices.dll"
ews.Task task = ews.Task.Bind(service, ID);
if (task.IsReminderSet)
{
//do something
}
As a start make sure you using the latest version of library from github https://github.com/OfficeDev/ews-managed-api. The other thing you can do is enable tracing https://blogs.msdn.microsoft.com/webdav_101/2015/05/03/ews-best-practices-tracing-and-logging/ this will allow you to see exactly what the server is actually sending back to. Its possible especially if mailboxes has been migrated that these properties can be null on the server side (due to the migration method used).

Is it possible to add dynamic data to an MassTransit courier/routing slip custom event?

I have a MassTransit routing slip configured and working. For reference, the routing slip takes in an ID of an item in a MongoDB database and then creates a "version" of that document in a SQL database using EF Core. The activities (as commands) are:
Migrate document to SQL
Update audit info in MongoDB document
Update MongoDB document status (i.e. to published)
All of the above are write commands.
I have added a new 1st step which runs a query to make sure the MongoDB document is valid (e.g. name and description fields are completed) before running the migration. If this step fails it throws a custom exception, which in turns fires a failed event which is then picked up and managed by my saga. Below is a snippet of my activity code followed by the routing slip builder code:
Activity code
var result = await _queryDispatcher.ExecuteAsync<SelectModuleValidationResultById, ModuleValidationResult>(query).ConfigureAwait(false);
if (!result.ModuleValidationMessages.Any())
{
return context.Completed();
}
return context.Faulted(new ModuleNotValidException
{
ModuleId = messageCommand.ModuleId,
ModuleValidationMessages = result.ModuleValidationMessages
});
Routing slip builder code
builder.AddActivity(
nameof(Step1ValidateModule),
context.GetDestinationAddress(ActivityHelper.BuildQueueName<Step1ValidateModule>(ActivityQueueType.Execute)),
new SelectModuleValidationResultById(
context.Message.ModuleId,
context.Message.UserId,
context.Message.LanguageId)
);
builder.AddSubscription(
context.SourceAddress,
RoutingSlipEvents.ActivityFaulted,
RoutingSlipEventContents.All,
nameof(Step1ValidateModule),
x => x.Send<IModuleValidationFailed>(new
{
context.Message.ModuleId,
context.Message.LanguageId,
context.Message.UserId,
context.Message.DeploymentId,
}));
Whilst all of this works and the event gets picked up by my saga I would ideally like to add the ModuleValidationMessages (i.e. any failed validation messages) to the event being returned but I can't figure out how or even if that's possible (or more fundamentally if it's right thing to do).
It's worth noting that this is a last resort check and that the validation is checked by the client before even trying the migration so worse case scenario I can just leave it has "Has validation issues" but ideally I would like to include the derail in the failed response.
Good use case, and yes, it's possible to add the details you need to the built-in routing slip events. Instead of throwing an exception, you can Terminate the routing slip, and include variables - such as an array of messages, which are added to the RoutingSlipTerminated event that will be published.
This way, it isn't a fault but more of a business decision to terminate the routing slip prematurely. It's a contextual difference, which is why it allows variables to be specified (versus Faulted, which is a full-tilt exception).
You can then pull the array from the variables and use those in your saga or consumer.

How to obtain other Entities in C# WebCore Entity Framework

I have a C# WebCore REST service that talks to a front end and uses Entity Framework to CRUD into a database. This uses Dependency Injection to add the contexts upon startup:
services.AddDbContext<FileCacheContext>(options => options.UseSqlServer(Settings.ConnectionSetting));
services.AddDbContext<FileImportContext>(options => options.UseSqlServer(Settings.ConnectionSetting));
The functionality that I have allows a user to upload a file, which is manipulated by the server and some properties of that file are returned to the front end. The uploaded file is cached in the database (FileCacheContext).
After some time has passed, the user now wishes to confirm their action and "promote" the file from the Cache (FileCacheContext) to the Import (FileImportContext); this is done by an action in the front end that contains the id of the cached file.
This parameter is passed to a different REST Controller, which is being invoked using the FileImport context, rather than the FileCache context:
public PromoteController(FileImportContext context)
{
_context = context;
}
[HttpPost]
public void Post([FromBody] int fileCacheId)
{
...
What I now need to do is to "move" this cached file from one "context" to another, something along the lines of:
var cachedFile = _context.FileCache.Where(f => f.FileCacheId == cachedFileId).FirstOrSingle();
var importedFile = new FileImport() { FileData = cachedFile.FileData };
_context.FileImport.Add(importedFile);
_context.SaveChanges();
My issue is that I can only see the one context, so I cannot get hold of the other context to either read from or write into.
The two underlying tables have no relationship, so I cannot link them in any way to load one based upon the other.
How can I get access to another (unrelated) context in EF?
So to answer my own question, I found that I could of course include the additional contexts required when instantiating the controller:
public PromoteController(FileImportContext contextImport, FileCacheContext contextCache)
{
_contextImport = contextImport;
_contextCache = contextCache;
}
And then use these as required.
This doesn't feel like the right way to do it, but if it works, it works...

Exception when attempting to link an object to the currently authenticated user

I currently have an ASP.NET Web Api 2 application which, for the purposes of this question, manages 'Items'. ASP.NET Identity 2 is used to provide authentication for requests, and the data is handed by Entity Framework 6.
My aim is for the item's Owner property to be set automatically to the user who authenticated the POST request. Below is the code so far:
public async Task<IHttpActionResult> PostItem(Item item)
{
var userId = User.Identity.GetUserId();
var user = await UserManager.FindByIdAsync(userId);
// Set the Owner to the currently authenticated user
item.Owner = user;
db.Items.Add(item); // Exception Thrown Here
await db.SaveChangesAsync();
...
}
However, when the method is called, the following exception occurs. The current userId and user are being found correctly, yet there is still an issue adding the Item once its Owner property has been set.
A first chance exception of type 'System.InvalidOperationException'
occurred in EntityFramework.dll
Additional information: An entity object cannot be referenced by
multiple instances of IEntityChangeTracker.
The problem is that you're trying to track the same entity using two different contexts and that doesn't work. maybe you should look in your code and see which of your entities is being tracked by two context, It is possible since you are using a global variable (db) as a reference to your context.

Categories