'Task<User>' does not contain a definition for 'LastActive' - c#

Here's my .cs Code, where the error is being generated (line 23):'Task' does not contain a definition for 'LastActive' and no accessible extension method 'LastActive' accepting a first argument of type 'Task' could be found
using System.Security.Claims;
using System.Threading.Tasks;
using DatingApp.Data;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using DatingApp.Models;
namespace DatingApp.Helper
{
public class LogUserActivity : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
var userId = int.Parse(resultContext.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value);
var repo = resultContext.HttpContext.RequestServices
.GetService<IUserRepository>();
var user = repo.GetUserById(userId);
user.LastActive= DateTime.Now;
await repo.SaveAll();
}
}
}
Here is my repository
public async Task<User> GetUserById(int id)
{
var user =await _context.Users.Include(p=>p.Photos).FirstOrDefaultAsync(u=>u.Id==id);
return user;
}

GetUserById doesn't return a User, it returns a Task<User>. You need to await it to get the User:
var user = await repo.GetUserById(userId);

Related

Azure Function V3 configuration with DI

I have a Azure Function with 2 triggers:
I’m registering IService in my Startup like so:
I need a different configuration in the Service class depending on which trigger that is calling DoWork()? How can I achieve this using DI?
public class Service : IService
{
public Service(/*Configuration to be injected depends on calling trigger */)
{ }
public void DoWork()
{ }
}
Configuration extract:
Thankyou user1672994. Posting your suggestion as an answer so that it will be helpful for other community members who face similar kind of issues.
Below is the example code to implement todo work items where this will be helpful in resolving your issue.
using AZV3CleanArchitecture.Models;
using AZV3CleanArchitecture.Options;
using AZV3CleanArchitecture.Providers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
namespace AZV3CleanArchitecture.Services
{
public class ToDoItemsService : IToDoItemsService
{
private readonly HttpClient httpClient;
private readonly ToDoItemsServiceOptions toDoItemsServiceOptions;
private readonly ILogger<ToDoItemsService> logger;
public ToDoItemsService(HttpClient httpClient, IOptions<ToDoItemsServiceOptions> toDoItemsServiceOptions, ILogger<ToDoItemsService> logger)
{
this.httpClient = httpClient;
this.toDoItemsServiceOptions = toDoItemsServiceOptions.Value;
this.logger = logger;
}
public async Task<ToDoItem> GetToDoItem(int id)
{
logger.LogInformation($"Retrieving item: {{{Constants.TodoItemId}}}", id);
var getUrl = $"{this.toDoItemsServiceOptions.BaseUrl.TrimEnd('/')}/todos/{id}";
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, getUrl))
{
using (var response = await this.httpClient.SendAsync(requestMessage))
{
string responseString = await response.Content.ReadAsStringAsync();
logger.LogWarning($"Retrieved item: {{{Constants.TodoItemId}}}. Logged as warning for demo.", id);
return JsonConvert.DeserializeObject<ToDoItem>(responseString);
}
}
}
public async Task<IEnumerable<ToDoItem>> GetAllToDoItems(int id)
{
logger.LogInformation($"Retrieving all todo items");
var getUrl = $"{this.toDoItemsServiceOptions.BaseUrl.TrimEnd('/')}/todos";
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, getUrl))
{
using (var response = await this.httpClient.SendAsync(requestMessage))
{
string responseString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<ToDoItem>>(responseString);
}
}
}
public async Task<ToDoItem> CreateToDoItem(ToDoItem toDoItem)
{
// call service and return the output
return await Task.FromResult(new ToDoItem() { Id = 1, UserId = 1, Title = "Some Dummy Title", Completed = true });
}
public Task<ToDoItem> UpdateToDoItem(ToDoItem toDoItem)
{
throw new System.NotImplementedException();
}
}
}
for further information check the ToDoItemServices link.

Custom claims do not persist outside of middleware

I am trying to create custom claims and persist them through the session. Unfortunately, it looks like they don't persist outside of the middleware itself. Am I missing something?
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using SRD.Data;
using SRD.Extensions;
namespace SRD.Security
{
public class ServerClaimsMiddleware
{
private readonly RequestDelegate _next;
public ServerClaimsMiddleware(RequestDelegate next)
{
_next = next;
}
private const string ClaimsLastCheckedKey = "ClaimsLastChecked";
public async Task Invoke(HttpContext context, DbContext dbContext, IDistributedCache cache)
{
if (context.User.Identity.IsAuthenticated)
{
var claimsLastChecked = await cache.RetrieveFromCache<DateTimeOffset?>(ClaimsLastCheckedKey);
if (!claimsLastChecked.HasValue)
{
var cs = new List<Claim>();
using (var principalContext = new PrincipalContext(ContextType.Domain))
{
if (context.User.Identity is ClaimsIdentity identity)
{
var user = UserPrincipal.FindByIdentity(principalContext, identity.Name);
if (user != null) cs.Add(new Claim(CustomClaimType.DisplayName.ToString(), user.DisplayName));
}
}
var roles = await dbContext.Roles.All();
foreach (var role in roles.Where(r => context.User.IsInRole(r.ADGroupName)))
{
var roleClaims = dbContext.Claims.ByRole(role.ADGroupName);
var customClaims = roleClaims.Select(x => new Claim(CustomClaimType.Permission.ToString(), x.Name));
cs.AddRange(customClaims);
}
if (cs.Any()) context.User.AddIdentity(new ClaimsIdentity(cs, "Kerbros"));
await cache.SaveToCache(ClaimsLastCheckedKey, DateTimeOffset.Now, new TimeSpan(0, 0, 15, 0));
}
}
await _next(context);
}
}
public static class ServerClaimsMiddlewareExtensions
{
public static IApplicationBuilder UseServerClaimsMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<ServerClaimsMiddleware>();
}
}
}
You are adding them to the ClaimsPrincipal, but that doesn't persist the data anywhere.
When ASP.NET Core authenticates a request, it creates a ClaimsPrincipal from the cookie/token/something else.
It does not go the other way around automatically; modifying the principal is purely in-memory.
If your app is the one creating the cookie/token,
I think you can write a new one by calling context.SignInAsync() in your middleware after modifying the principal.
// you need this import
using Microsoft.AspNetCore.Authentication;
// In your middleware Invoke()
await context.SignInAsync(context.User);
You can also specify an authentication scheme to SignInAsync() if you have not configured a default sign-in scheme.

Cannot pass arguments to a BotCallback inside a ContinueConversationAsync method

I am trying to implement proactive messages on bot framework v4, It works, but only with the string on the BotCallback function, I need to pass custom text but ContinueConversationAsync doesnt seems to allow it
public async Task<bool> SendProactiveMessage(MensajeExterno mensajeExterno)
{
var referenciaDeConversacion = ObtenerReferenciaConversacion(mensajeExterno.ConversationId);
var continuationActivity = referenciaDeConversacion.GetContinuationActivity();
if (referenciaDeConversacion == null) return false;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, referenciaDeConversacion, BotCallback, default(CancellationToken));
return true;
}
private ConversationReference ObtenerReferenciaConversacion(string conversationId)
{
return new ConversationReferenceModulo().ObtenerConversationReference(conversationId);
}
public MensajeroDefaultModulo(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
{
_adapter = adapter;
_appId = configuration["MicrosoftAppId"];
if (string.IsNullOrEmpty(_appId))
{
_appId = Guid.NewGuid().ToString(); //if no AppId, use a random Guid
}
}
private async Task BotCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
await turnContext.SendActivityAsync("proactive hello", cancellationToken: cancellationToken);
}
You can make a wrapper around BotCallback using LINQ approach. But personally i do not understand the idea of such delegates without any EventArgs.
[HttpPost]
public async Task<IActionResult> Post([FromBody] string messageText)
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) => await BotCallback(messageText, context, token), default(CancellationToken));
}
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
private async Task BotCallback(string message, ITurnContext turnContext, CancellationToken cancellationToken)
{
await turnContext.SendActivityAsync(message, cancellationToken: cancellationToken);
}
What I understand you are wanting to do is to pass a value to the BotCallback that can be sent back via the method SendActivityAsync.
To do this you can use a lambda expression instead of calling the BotCallback.
public async Task<bool> SendProactiveMessage(MensajeExterno mensajeExterno)
{
var yourVariable = "blah";
var referenciaDeConversacion = ObtenerReferenciaConversacion(mensajeExterno.ConversationId);
var continuationActivity = referenciaDeConversacion.GetContinuationActivity();
if (referenciaDeConversacion == null) return false;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, referenciaDeConversacion, async (context, token) => {
await turnContext.SendActivityAsync("proactive hello " + yourVariable, cancellationToken: cancellationToken);
}, default(CancellationToken));
return true;
}
This comes from this reference to a proactive messaging sample here:
https://github.com/microsoft/botbuilder-dotnet/issues/787
For Lambda expressions see Lambda Expressions (C# Programming Guide)
A lambda expression is an anonymous function that can contain
expressions and statements, and can be used to create delegates or
expression tree types.
All lambda expressions use the lambda operator =>, which is read as
"goes to". The left side of the lambda operator specifies the input
parameters (if any) and the right side holds the expression or
statement block. The lambda expression x => x * x is read "x goes to x
times x."
Updated with API controller example
The SendProactiveMessage needs to be in a Controller in your Bot project, for example:
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
namespace ProactiveBot.Controllers
{
[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;
public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary<string, ConversationReference> conversationReferences)
{
_adapter = adapter;
_conversationReferences = conversationReferences;
_appId = configuration["MicrosoftAppId"];
// If the channel is the Emulator, and authentication is not in use,
// the AppId will be null. We generate a random AppId for this case only.
// This is not required for production, since the AppId will have a value.
if (string.IsNullOrEmpty(_appId))
{
_appId = Guid.NewGuid().ToString(); //if no AppId, use a random Guid
}
}
public async Task<IActionResult> Get([FromQuery(Name = "taskID")] int taskID)
{
foreach (var conversationReference in _conversationReferences.Values)
{
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, async (context, token) => {
await context.SendActivityAsync("proactive task notification for TaskID: " + taskID.ToString());
}, default(CancellationToken));
}
// Let the caller know proactive messages have been sent
return new ContentResult()
{
Content = "<html><body><h1>Proactive messages have been sent.</h1><p>" + taskID.ToString() + "</p></body></html>",
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
};
}
}
}
You can then get access to the context and token through the injected adapter. The above code is largely from the current Proactive Message sample code
So I can then call this API eg. http://localhost:3988/api/notify?taskID=5678 passing in the taskID parameter (in my case) which is then sent through to the user via ContinueConversationAsync.

Azure Durable Function Error "No activity functions are currently registered!"

Full Message:
Function 'Function1 (Orchestrator)' failed with an error.
Reason: System.ArgumentException:
The function 'Function1_GetData' doesn't exist,
is disabled, or is not an activity function.
Additional info: No activity functions are currently registered!
[FunctionName("Function1")]
public static async Task<List<string>> RunOrchestrator([OrchestrationTrigger] DurableOrchestrationContext context) {
var outputs = new List<string>();
using (var efContext = new DbContext()) {
foreach (var s in efContext.Table) {
var x= await context.CallActivityAsync<bool>("Function2_GetSummonerChanges", s.Id);
outputs.Add(x.ToString());
}
}
context.ContinueAsNew(null);
return outputs;
}
[FunctionName("Function1_GetData")]
public static bool GetData(long Id) {
return true;
}
you don't have definition for activity function "Function2_GetSummonerChanges"
it could be something like
[FunctionName("Function2_GetSummonerChanges")]
public static int MyActivityFunction([ActivityTrigger] int id, ILogger log)
{
// your logic
}
Take a look at following example and link for more information on building blocks of Durable functions
https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-create-first-csharp
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace FunctionApp28
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
var outputs = new List<string>();
// Replace "hello" with the name of your Durable Activity Function.
outputs.Add(await context.CallActivityAsync<string>("Function1_Hello", "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>("Function1_Hello", "Seattle"));
outputs.Add(await context.CallActivityAsync<string>("Function1_Hello", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
[FunctionName("Function1_Hello")]
public static string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}
[FunctionName("Function1_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("Function1", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
}
In my case this was because i missed to add the trigger type in the activity class
public async Task Run(
[ActivityTrigger])

QnA & LUIS Chatbot C#

I am trying to implement a QnaMakerDialog after LUIS recognizes that it is a QnA Intent. In my Dialogs folder, I have two files: BasicLuisDialog.cs and BasicQnAMakerDialog.cs. In my Controller, it calls BasicLuisDialog, and I want to call the QnAMakerDialog within that. Here is my full BasicLuisDialog code:
using System;
using System.Configuration;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Builder.Dialogs;
using System.Web.Http.Description;
using System.Net.Http;
using System.Diagnostics;
using Microsoft.Bot.Builder.Luis;
using Microsoft.Bot.Builder.Luis.Models;
using System.Threading;
using Microsoft.Bot.Sample.QnABot;
namespace Microsoft.Bot.Sample.LuisBot
{
[Serializable]
public class BasicLuisDialog : LuisDialog<object>
{
public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute("asdf", "asdf")))
//replace asdf with keys
{
}
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
await context.PostAsync($"You have reached the none intent. You said: {result.Query}"); //
context.Wait(MessageReceived);
}
[LuisIntent("Weather.GetCondition")]
public async Task ConditionIntent(IDialogContext context, LuisResult result)
{
await context.PostAsync($"You have reached the Condition intent. You said: {result.Query}"); //
context.Wait(MessageReceived);
}
[LuisIntent("Weather.GetForecast")]
public async Task ForecastIntent(IDialogContext context, LuisResult result)
{
await context.PostAsync($"You have reached the ForecastIntent intent. You said: {result.Query}"); //
context.Wait(MessageReceived);
}
[LuisIntent("GetQnA")]
public async Task GetQnA(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
var msg = await activity;
var cts = new CancellationTokenSource();
var faq = new BasicQnAMakerDialog();
await context.Forward(faq, AfterFAQDialog, msg, CancellationToken.None);
}
private async Task AfterFAQDialog(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
}
}
I get the error:
Dialogs\BasicLuisDialog.cs(51,23): error CS0246: The type or namespace name 'BasicQnAMakerDialog' could not be found (are you missing a using directive or an assembly reference?)
Code for BasicQnAMakerDialog.cs:
using System;
using System.Configuration;
using Microsoft.Bot.Builder.CognitiveServices.QnAMaker;
namespace Microsoft.Bot.Sample.QnABot
{
[Serializable]
public class BasicQnAMakerDialog : QnAMakerDialog
{
public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute("asdf", "asdf","No good match in FAQ",0.2,3)))
{
}
protected override async Task DefaultWaitNextMessageAsync(IDialogContext context, IMessageActivity message, QnAMakerResults results)
{
Console.WriteLine("KB Question: " + results.Answers.First().Questions.First());
Console.WriteLine("KB Answer: " + results.Answers.First().Answer);
await base.DefaultWaitNextMessageAsync(context, message, results);
}
}
}
Add a using for the namespace where the BasicQnAMakerDialog.cs is at.
using Microsoft.Bot.Sample.QnABot

Categories