Use transaction with ASP.NET identity - c#

I am using ASP.NET identites userManager.CreateAsync so it automatically populates NormalizedEmail from email field and stuff like that. However this time I want to insert user together with some metadata (in some other tables) which means I need transaction.
I successfully made it work without throwing exception but it seems transaction is not rollbacked (UserManager uses some different dbContext?)
Here is code:
try
{
await ResilientTransaction.New(_databaseContext).ExecuteAsync(async () =>
{
var currentUser = await _userManager.FindByIdAsync(_identityContext.UserId.ToString());
user.ParentUserId = _identityContext.UserId;
var existingUserWithEmail = await FindByEmailAsync(user.Email);
if (existingUserWithEmail != null)
{
throw new ValidationException($"User with email {user.Email} already exists");
}
var currentUserRoles = await _roleRepository.GetWithPermissionsByUserId(_identityContext.UserId);
var result = await _userManager.CreateAsync(user);
var rolesResult = await _userManager.AddToRolesAsync(user, currentUserRoles.Select(x => x.Name));
if (!result.Succeeded)
{
throw new ValidationException(result.Errors.FirstOrDefault()!.Description);
}
if (currentUser.OverrideVehicleAccessSettingsByEmailDomainName)
{
user.OverrideVehicleAccessSettingsByEmailDomainName = true;
var vehicleAccessSettings = await _vehicleAccessSettingRepository.GetAll(x => x.UserId == _identityContext.UserId);
foreach (var vehicleAccessSetting in vehicleAccessSettings)
{
vehicleAccessSetting.UserId = user.Id;
// vehicleAccessSetting.Id = 0;
_ = await _vehicleAccessSettingRepository.Insert(vehicleAccessSetting);
}
}
user.Roles = currentUserRoles;
});
}
catch (Exception ex)
{
throw;
}
And this ResillianteTransaction is just this:
public class ResilientTransaction
{
private readonly DbContext _context;
private ResilientTransaction(DbContext context) =>
_context = context ?? throw new ArgumentNullException(nameof(context));
public static ResilientTransaction New(DbContext context) =>
new ResilientTransaction(context);
public async Task ExecuteAsync(Func<Task> action)
{
// Use of an EF Core resiliency strategy when using multiple DbContexts
// within an explicit BeginTransaction():
// https://learn.microsoft.com/ef/core/miscellaneous/connection-resiliency
var strategy = _context.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var transaction = await _context.Database.BeginTransactionAsync();
try
{
await action();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
});
}
}

Related

How to write unit test case for SaveChangesAsync()

I am writing unit test case for my Entity Framework Core SaveAsync method.
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = _dbContext.TableName.AddAsync(dbObj);
_ = _dbContext.SaveChangesAsync(context.CancellationToken);
}
catch (Exception ex)
{
_logger.LogCritical($"{GetType().Name}:{nameof(Consume)} {ex}");
}
await Task.CompletedTask;
I have written unit test case by mocking:
Mock<MyDbContext> dbContext = new();
var data = return new List<TableName>()
{
new TableName
{
Id = 1,
Description = "test"
}
};
dbContext.Setup(r => r.TableName).ReturnsDbSet(data);
dbContext.Setup(r => r.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Check that each method was only called once.
//dbContext.Verify(x => x.TableName.Add(It.IsAny<TableName>()), Times.Once());
dbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
Below is what I get as a result
Moq.MockException : Expected invocation on the mock once, but was 0 times
While debugging the test case, I am getting the below.
Firstly,add await into try catch block.otherwise it would not catch excption
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = await _dbContext.TableName.AddAsync(dbObj);
_ = await _dbContext.SaveChangesAsync(context.CancellationToken);
}
//remove this line
await Task.CompletedTask;
The document related with Asynchronous programming
And you could check the document related with testing non query scenarios
I tried as below:
Assuming a controller:
public async Task<IActionResult> Create([Bind("Id,Name")] SomeEntity someEntity)
{
if (ModelState.IsValid)
{
try
{
await _context.AddAsync(someEntity);
await _context.SaveChangesAsync();
}
catch (Exception e)
{
}
return RedirectToAction(nameof(Index));
}
return View(someEntity);
}
Test:
public class UnitTest1
{
[Fact]
public async Task Test1()
{
var mockset = new Mock<DbSet<SomeEntity>>();
var mockdbcontext = new Mock<WebApplication8Context>(new DbContextOptions<WebApplication8Context>() );
mockdbcontext.Setup(x=>x.SomeEntity).Returns(mockset.Object);
var a = mockdbcontext.Object;
var controller = new SomeEntitiesController(mockdbcontext.Object);
await controller.Create(new SomeEntity() { Name = "someName" });
mockdbcontext.Verify(x => x.SaveChangesAsync(default(CancellationToken)), Times.Once);
}
}
Result:

Cannot await a .find method using mongodb c#

I am a novice so I apologize but I keep getting this CS1061 error when trying to await a .find() method. How do I await this find()?
MongoClient client = new MongoClient(mongoDBSettings.Value.ConnectionURI);
IMongoDatabase database = client.GetDatabase(mongoDBSettings.Value.DatabaseName);
_userCollection = database.GetCollection<User>(mongoDBSettings.Value.CollectionName);
_partyCollection = database.GetCollection<Party>(mongoDBSettings.Value.CollectionName);
public async Task<IEnumerable> GetNearbyParties(string postalCode)
{
var nearbyParties = await _partyCollection.Find(x => x.Address.postalCode == postalCode);
return (IEnumerable<Party>)nearbyParties;
}
MongoClient client = new MongoClient(mongoDBSettings.Value.ConnectionURI);
IMongoDatabase database = client.GetDatabase(mongoDBSettings.Value.DatabaseName);
_userCollection = database.GetCollection(mongoDBSettings.Value.CollectionName);
_partyCollection = database.GetCollection(mongoDBSettings.Value.CollectionName);
public async Task<IEnumerable> GetNearbyParties(string postalCode)
{
var nearbyParties = await _partyCollection.Find(x => x.Address.postalCode == postalCode);
return (IEnumerable<Party>)nearbyParties;
}
I had it originally set up like this running synchronously:
public async Task<IEnumerable> GetNearbyParties(string postalCode)
{
var nearbyParties = _partyCollection.Find(x => x.Address.postalCode == postalCode);
return (IEnumerable<Party>)nearbyParties;
//return results;
}
But I understand that since it's an async method I should have an await when I try to search the database so that other things can run while that is fetched.
You need to call asynchronous API of Mongo not normal Find method:
public async Task<IEnumerable> GetNearbyParties(string postalCode) {
var nearbyParties = await _partyCollection.FindAsync(x => x.Address.postalCode == postalCode);
return (IEnumerable<Party>)nearbyParties;
}

Transactionscope; Revert multiple savechanges

I am using transactionscope for a multiple DB context call. Something similar to this.
using (TransactionScope scope = new TransactionScope())
{
//Do something with context1
//Do something with context2
//Save and discard changes
context1.SaveChanges();
//Save and discard changes
context2.SaveChanges();
//if we get here things are looking good.
scope.Complete();
}
But in my case each context is created in a repository that is being called via this service. Each context is initialized via DI, so its scope is limited to the repository.
var empRepo;
var cityTravelRepo;
using (TransactionScope scope = new TransactionScope())
{
try
{
var empId = await empRepo.CreateEmp("Soul");
await cityTravelRepo.LogTravelling(listLocation, empId);
//if we get here things are looking good.
scope.Complete();
scope.Dispose();
}
catch (Exception ex)
{
scope.Dispose();
return BadRequest(ex.Message);
}
}
//empRepo
private readonly WritableMasterDbContext db;
public EmployeeRepository(WritableMasterDbContext db)
{
this.db = db;
}
public async Task<long> CreateEmp(string name)
{
if (!AlreadyExists(outlet.Id))
{
var emp = new Employee() { Name : name};
db.Employee.Add(emp);
await db.SaveChangesAsync();
return emp.Id;
}
return 0;
}
//cityTravelRepo
private readonly TransactionDbContext db;
public CityTravelRepository(TransactionDbContext db)
{
this.db = db;
}
public async Task LogTravelling(list<long> locationId, long empId)
{
foreach (var location in locationId)
{
var travelEvent = new TravelEvent();
travelEvent.locationId = location;
travelEvent.EmployeeId = empId;
travelEvent.CreatedAt = DateTime.UtcNow;
db.FieldEvents.Add(travelEvent);
}
await db.SaveChangesAsync();
}
For my case:
var empId = await empRepo.CreateEmp("Soul");
await cityTravelRepo.LogTravelling(listLocation, empId); //Some Error occures
scope.Complete();
}
catch (Exception ex)
{
scope.Dispose(); //This will dispose only LogTravelling, but not CreateEmp
return BadRequest(ex.Message);
}
Is it possible to dispose of the whole transaction?
Reason for using Transactionscope https://stackoverflow.com/a/542704/11519765
Reason for thinking only the second will be rolled back https://stackoverflow.com/a/35775480/11519765

ASP.NET Core - call one controller method from another

I need to call from create method delete for cleanup. I want to call DeleteDevice like lazy cleanup and do not care if it will succeed or not.
People suggested to use this way:
DeleteDevice(param).ConfigureAwait(false);
Is it safe?
public async Task<ActionResult<Device>> CreateDevice([FromBody] CreateDeviceRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
var registeredDevice = await RegisterDevice(request.DisplayName);
bool isCreatePrinterSucceed = false;
try
{
var updatedDevice = await UpdateDevice(registeredDevice.Id);
isCreatePrinterSucceed = true;
return Ok(new DeviceReference
{
DisplayName = request.DisplayName,
Id = updatedDevice.Id
});
}
finally
{
if (!isCreatePrinterSucceed)
{
var param = new DeleteDeviceRequest()
{
Id = registeredDevice.Id,
AzureUserBearerToken = request.AzureUserBearerToken
};
DeleteDevice(param).ConfigureAwait(false); ;
}
}
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError, ex.Message);
}
}

Bot Framework messes up dialog state

I'm currently making a chatbot with Microsoft's Bot Framework. In my flow I have a final dialog that lets the user know, that they are participating in the competition. There is also an error-handling method for unknown input. The two methods are seen here:
[Serializable]
public class ConcertCityDialog : AbstractBasicDialog<DialogResult>
{
private static FacebookService FacebookService => new FacebookService(new FacebookClient());
[LuisIntent("ConcertCity")]
public async Task ConcertCityIntent(IDialogContext context, LuisResult result)
{
var fbAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
var selectedCityName = result.Entities.FirstOrDefault()?.Entity;
concert_city selectedCity;
using (var concertCityService = new ConcertCityService())
{
selectedCity = concertCityService.FindConcertCity(selectedCityName);
}
if (selectedCity == null)
{
await NoneIntent(context, result);
return;
}
user_interaction latestInteraction;
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookIdIncludeInteractions(context.Activity.From.Id);
latestInteraction = user.user_interaction.MaxBy(e => e.created_at);
}
latestInteraction.preferred_city_id = selectedCity.id;
latestInteraction.gif_created = true;
using (var userInteractionService = new UserInteractionService())
{
userInteractionService.UpdateUserInteraction(latestInteraction);
}
var shareIntroReply = context.MakeMessage();
shareIntroReply.Text = "Great choice! You are now participating in the competition. If you dare then pass your message \uD83D\uDE0E";
await context.PostAsync(shareIntroReply);
var reply = await MessageUtility.MakeShareMessageCard(context, fbAccount, latestInteraction, false);
await context.PostAsync(reply);
context.Done(DialogResult.Done);
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
messenger_user user;
using (var userService = new MessengerUserService())
{
user = userService.FindByFacebookId(context.Activity.From.Id);
}
var phrase = CreateMisunderstoodPhrase(user, result.Query);
using (var misunderstoodPhraseService = new MisunderstoodPhraseService())
{
misunderstoodPhraseService.CreatePhrase(phrase);
}
List<concert_city> concertCities;
using (var concertCityService = new ConcertCityService())
{
concertCities = concertCityService.GetUpcomingConcertCities().ToList();
}
// Prompt city
var reply = context.MakeMessage();
reply.Text = "I'm not sure what you mean \uD83E\uDD14<br/>Which Grøn Koncert would you like to attend?";
reply.SuggestedActions = new SuggestedActions
{
Actions = concertCities.Select(e => MessageUtility.MakeQuickAnswer(e.name)).ToList()
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
protected override void OnDeserializedCustom(StreamingContext context)
{
}
}
And here is the AbstractBasicDialog implementation:
[Serializable]
public abstract class AbstractBasicDialog<T> : LuisDialog<T>
{
protected AbstractBasicDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("Cancel")]
public virtual async Task CancelIntent(IDialogContext context, LuisResult result)
{
var randomQuotes = new List<string>
{
"If you say so, I'll leave you alone for now",
"alright then, I'll leave you alone",
"Okay then, I won't bother you anymore"
};
await context.PostAsync(MessageUtility.RandAnswer(randomQuotes));
context.Done(DialogResult.Cancel);
}
[LuisIntent("Start")]
public virtual async Task StartIntent(IDialogContext context, LuisResult result)
{
context.Done(DialogResult.Restart);
}
[LuisIntent("CustomerSupport")]
public async Task CustomerSupportIntent(IDialogContext context, LuisResult result)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = true;
userService.UpdateUser(user);
}
}
await context.PostAsync("I'll let customer service know, that you want to talk to them. They will get back to you within 24 hours.<br/>If at any time you want to return to me, and start passing a message, just type \"Stop customer support\".");
context.Call(new CustomerSupportDialog(), ResumeAfterCustomerSupport);
}
private async Task ResumeAfterCustomerSupport(IDialogContext context, IAwaitable<DialogResult> result)
{
context.Done(await result);
}
protected misunderstood_phrase CreateMisunderstoodPhrase(messenger_user user, string phrase)
{
return new misunderstood_phrase
{
phrase = phrase,
dialog = GetType().Name,
messenger_user_id = user.id
};
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
OnDeserializedCustom(context);
}
protected abstract void OnDeserializedCustom(StreamingContext context);
}
The call chain starts at this dialog:
[Serializable]
public class BasicLuisDialog : LuisDialog<DialogResult>
{
private static FacebookService FacebookService => new FacebookService(new FacebookClient());
public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
var facebookAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
RegisterUser(facebookAccount, null, out var user);
var phrase = CreateMisunderstoodPhrase(user, result.Query);
using (var misunderstoodPhraseService = new MisunderstoodPhraseService())
{
misunderstoodPhraseService.CreatePhrase(phrase);
}
var reply = context.MakeMessage();
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
new CardAction { Title = "Get started", Type = ActionTypes.ImBack, Value = "Get started" },
new CardAction { Title = "Customer support", Type = ActionTypes.ImBack, Value = "Customer support" }
}
};
var name = string.IsNullOrEmpty(facebookAccount.FirstName) ? "" : $"{facebookAccount.FirstName} ";
reply.Text = $"Hm, I'm not sure what you mean {name} \uD83E\uDD14 Here are some ways you can interact with me:";
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
[LuisIntent("Greeting")]
[LuisIntent("Positive")]
[LuisIntent("Start")]
public async Task GreetingIntent(IDialogContext context, LuisResult result)
{
var rnd = new Random();
var facebookAccount = await FacebookService.GetAccountAsync(context.Activity.From.Id);
// Initial Greeting
var greetings = new List<string>
{
"Well hello there",
"Hi there"
};
if (!string.IsNullOrEmpty(facebookAccount.FirstName))
{
greetings.Add("Hi {0}");
greetings.Add("Hello {0}");
greetings.Add("Welcome {0}");
}
if (facebookAccount.Gender == "male")
greetings.Add("Hey handsome");
else if (facebookAccount.Gender == "female")
greetings.Add("Hi gorgeous");
var randIndex = rnd.Next(greetings.Count);
var greeting = string.Format(greetings[randIndex], facebookAccount.FirstName);
await context.PostAsync(greeting);
await MessageUtility.StartTyping(context, 300);
country country;
using (var countryService = new CountryService())
{
country = countryService.FindCountry(facebookAccount.Locale);
}
var userHasCountry = RegisterUser(facebookAccount, country, out var user);
// If user contry not found prompt for answer
if (!userHasCountry)
{
var countryReply = context.MakeMessage();
countryReply.Text = "You are hard to keep track of - where are you from?";
countryReply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Denmark"),
MessageUtility.MakeQuickAnswer("Norway"),
MessageUtility.MakeQuickAnswer("Sweden"),
MessageUtility.MakeQuickAnswer("Other")
}
};
await context.PostAsync(countryReply);
context.Call(new CountryDialog(), AfterCountryDialog);
}
else
{
await FunPrompt(context, country);
}
}
private async Task AfterCountryDialog(IDialogContext countryContext, IAwaitable<country> countryAwaitable)
{
var country = await countryAwaitable;
var facebookAccount = await FacebookService.GetAccountAsync(countryContext.Activity.From.Id);
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(facebookAccount.Id);
user.country = country;
userService.UpdateUser(user);
}
var reply = countryContext.MakeMessage();
reply.Text = "That's cool \uD83D\uDE0E";
await countryContext.PostAsync(reply);
await MessageUtility.StartTyping(countryContext, 350);
await FunPrompt(countryContext, country);
}
private async Task FunPrompt(IDialogContext context, country country)
{
if (country?.name == "norway" && DateTime.Now < new DateTime(2018, 8, 13))
{
var reply = context.MakeMessage();
reply.Text = "Unfortunately the competition isn't open in Norway yet. You can still talk to customer support if you want to";
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Customer support")
}
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
else if ((country?.name == "denmark" && DateTime.Now >= new DateTime(2018, 7, 29)) ||
(country?.name == "norway" && DateTime.Now >= new DateTime(2018, 10, 21)))
{
var reply = context.MakeMessage();
reply.Text = "The competition has ended. You can still talk to customer support if you want to";
reply.SuggestedActions = new SuggestedActions
{
Actions = new List<CardAction>
{
MessageUtility.MakeQuickAnswer("Customer support")
}
};
await context.PostAsync(reply);
context.Wait(MessageReceived);
}
else
{
await context.PostAsync("Are you up for some fun?");
context.Call(new IntroductionDialog(), ResumeAfterDialog);
}
}
[LuisIntent("CustomerSupport")]
public async Task CustomerSupportIntent(IDialogContext context, LuisResult result)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = true;
userService.UpdateUser(user);
}
}
await context.PostAsync("I'll let customer support know, that you want to talk to them. They should be messaging you shortly.<br/>You can end your conversation with customer support at any time by typing \"Stop customer support\".");
context.Call(new CustomerSupportDialog(), ResumeAfterDialog);
}
private async Task ResumeAfterDialog(IDialogContext context, IAwaitable<DialogResult> result)
{
var resultState = await result;
if (resultState == DialogResult.Restart)
await GreetingIntent(context, null);
else if (resultState == DialogResult.CustomerSupport)
await ResumeAfterCustomerSupport(context);
else if (resultState == DialogResult.Done || resultState == DialogResult.Cancel)
context.Done(resultState);
else
context.Wait(MessageReceived);
}
private async Task ResumeAfterCustomerSupport(IDialogContext context)
{
using (var userService = new MessengerUserService())
{
var user = userService.FindByFacebookId(context.Activity.From.Id);
if (user != null)
{
user.receiving_support = false;
userService.UpdateUser(user);
}
}
await context.PostAsync("I hope you got the help you needed. Would you like to pass a message to a friend?");
context.Call(new IntroductionDialog(), ResumeAfterDialog);
}
private bool RegisterUser(FacebookAccount fbAccount, country country, out messenger_user user)
{
if (string.IsNullOrEmpty(fbAccount?.Id))
{
user = null;
return false;
}
using (var userService = new MessengerUserService())
{
user = userService.FindByFacebookId(fbAccount.Id);
if (user != null)
return user.country != null;
user = new messenger_user
{
id = fbAccount.Id,
country = country
};
userService.CreateUser(user);
return user.country != null;
}
}
protected misunderstood_phrase CreateMisunderstoodPhrase(messenger_user user, string phrase)
{
return new misunderstood_phrase
{
phrase = phrase,
dialog = GetType().Name,
messenger_user_id = user.id
};
}
}
This works most of the time. The user is told that their registration was a success and the flow exits with the context.Done() call. Sometimes however the chatbot doesn't register the dialog as being exited, as seen here:
As you can see the chatbot is still in the same Dialog even though I have called the Done() method. This is a general problem in my chatbot, as it happens sometimes in all my dialogs.
Do you have any input as to what could be wrong?
EDIT:
When debugging this I've added breakpoints every time it calls context.Call. When my issue arises it stops hitting these breakpoints afterwards. Could this be a side-effect of some DI or something? This is my DI code:
Conversation.UpdateContainer(builder =>
{
builder.RegisterModule(new DialogModule());
builder.RegisterModule(new ReflectionSurrogateModule());
builder.RegisterModule(new DialogModule_MakeRoot());
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
builder.Register(c => new CachingBotDataStore(store,
CachingBotDataStoreConsistencyPolicy
.ETagBasedConsistency))
.As<IBotDataStore<BotData>>()
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterType<BasicLuisDialog>().As<LuisDialog<DialogResult>>().InstancePerDependency();
});
I think I finally found the problem. In my code I had implemented a helper method in a static class that would send a typing response and wait a certain amount of time. Seeing as the context was passed into this static method it seems that this was causing some issues.
After changing the method to an extension method of the LuisDialog I no longer have this issue.
I would appreciate if anyone can expand on why this might have been a problem.
EDIT: The method in question:
public static async Task StartTyping(IDialogContext context, int sleep)
{
var typingMsg = context.MakeMessage();
typingMsg.Type = ActivityTypes.Typing;
await context.PostAsync(typingMsg);
await Task.Delay(sleep);
}
I faced a very similar issue and while moving the typing sending into a base class from a static helper class as Frederik did help to highly reduce the number of times the problem occured, the final solution was this: https://github.com/Microsoft/BotBuilder/issues/4477
In short, I had to downgrade the bot-related NuGet packages (Microsoft.Bot.Builder, Microsoft.Bot.Builder.History, Microsoft.Bot.Connector) to 3.13.1 and the issue disappeared.
since in [LuisIntent("ConcertCity")] you are using context.Done() so the current dialog gets exit from the stack. This is why the next message is being handled by the previous dialog or the message controller where the 'None' intent is being called and you are getting this response
reply.Text = "I'm not sure what you mean \uD83E\uDD14<br/>Which Grøn Koncert would you like to attend?";
You should not do context.Done() every places, this should only be called when you have to go to the previous dialog on the stack.

Categories