I am having trouble with adding both my AAD1 Authentication and custom Conversation logger onto my ChatBot. One or the other works fine, but when combining the two, I get HTTP timeouts. Any assistance would be greatly appreciated. Relevant code below:
Global.asax.cs
protected void Application_Start()
{
// Adding DocumentDB endpoint and primary key
var docDbServiceEndpoint = new Uri("-----------------------------------");//REMOVED Uri for question, no issue with connection as is
var docDbKey = "--------------------------------------------"; //REMOVED Key for question, no issue with connection as is
Conversation.UpdateContainer(builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new DocumentDbBotDataStore(docDbServiceEndpoint, docDbKey); // requires Microsoft.BotBuilder.Azure Nuget package
builder.RegisterType<DebugActivityLogger>().AsImplementedInterfaces().InstancePerDependency();
});
//authorization stuff
AuthSettings.Mode = ConfigurationManager.AppSettings["ActiveDirectory.Mode"];
AuthSettings.EndpointUrl = ConfigurationManager.AppSettings["ActiveDirectory.endpointUrl"];
AuthSettings.Tenant = ConfigurationManager.AppSettings["ActiveDirectory.Tenant"];
AuthSettings.RedirectUrl = ConfigurationManager.AppSettings["ActiveDirectory.RedirectUrl"];
AuthSettings.ClientId = ConfigurationManager.AppSettings["ActiveDirectory.ClientId"];
AuthSettings.ClientSecret = ConfigurationManager.AppSettings["ActiveDirectory.ClientSecret"];
GlobalConfiguration.Configure(WebApiConfig.Register);
}
AuthenticationHelper.cs
[Serializable]
public class AuthenticationHelper : IDialog<string>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(ProcessMessageAsync);
}
public async Task ProcessMessageAsync(IDialogContext context, IAwaitable<IMessageActivity> item)
{
var message = await item;
if (string.IsNullOrEmpty(await context.GetAccessToken("https://graph.microsoft.com/")))
{
//NO ACCESS TOKEN, GET IT
await context.Forward(new AzureAuthDialog("https://graph.microsoft.com"), this.ProcessAuthResultAsync, message, System.Threading.CancellationToken.None);
}
else
{
//have token
await context.Forward(new LuisAskQuestionDialog(), this.QuitMessageReceivedAsync, message, System.Threading.CancellationToken.None);
}
}
public async Task ProcessAuthResultAsync(IDialogContext context, IAwaitable<string> result)
{
var message = await result;
await context.PostAsync(message);
context.Wait(ProcessMessageAsync);
}
protected async Task QuitMessageReceivedAsync(IDialogContext context, IAwaitable<object> item)
{
var message = await item;
//StartRecordingProcess();
context.Done(message);
}
}
ChatBotLogging.cs
public class DebugActivityLogger : IActivityLogger
{
private const string EndpointUrl = "------------------------------";
private const string PrimaryKey = "------------------------------------";
private DocumentClient client;
// ADD THIS PART TO YOUR CODE
public async Task LogAsync(IActivity activity)
{
//Update this information
//What this needs to have: ConversationID, From, To, Date, Message
//Get all the texts information ready for upload;
//Get connection to table
//upload the inforamtion onto the table
//disconnect from the table
// Retrieve the storage account from the connection string.
//This Task is called to intercept messages
var fromid = activity.From.Id;
var toId = activity.Recipient.Id;
var chatMessage = activity.AsMessageActivity()?.Text;
var timeStamp = activity.Timestamp;
var conversationId = activity.Conversation.Id;
//timestamp converted to string.
string strTimeStamp = timeStamp.ToString();
try
{
this.client = new DocumentClient(new Uri(EndpointUrl), PrimaryKey);
await this.client.CreateDatabaseIfNotExistsAsync(new Database { Id = "botdb" });
await this.client.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri("botdb"), new DocumentCollection { Id = "botcollection" });
ChatLogEntity chatLog1 = new ChatLogEntity
{
TimeStamp = strTimeStamp,
ConversationId = conversationId,
FromID = fromid,
ToID = toId,
ChatMessage = chatMessage
};
await this.CreateChatDocumentIfNotExists("botdb", "botcollection", chatLog1);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
//entity class for demo purposes
// ADD THIS PART TO YOUR CODE
private async Task CreateChatDocumentIfNotExists(string databaseName, string collectionName, ChatLogEntity chatEntity)
{
try
{
await this.client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseName, collectionName, chatEntity.TimeStamp));
}
catch (DocumentClientException de)
{
if (de.StatusCode == HttpStatusCode.NotFound)
{
await this.client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(databaseName, collectionName), chatEntity);
}
else
{
throw;
}
}
}
public class ChatLogEntity
{
[JsonProperty(PropertyName = "timestamp")]
public string TimeStamp { get; set; }
public string ConversationId { get; set; }
public string ToID { get; set; }
public string FromID { get; set; }
public string ChatMessage { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
}
The AuthBot package has been discontinued. As mentioned in the comments, You should use the BotAuth for AADv1 authentication. The BotAuth supports the state data conversation support (so you will not get the deprecated state client warnings too).
Related
In my ASP.NET Core-6 Web API, I am given a third party API to consume and then return the account details. I am using HttpClient.
api:
https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber=
In appsettings.json I have:
"Endpoints": {
"customerUrl": "https://api.thirdpartycompany.com:2233/UserAccount/api/AccountDetail?accountNumber="
}
DTO:
public class GetCustomerDetailDto
{
public class CustomerDetail
{
public string AccountNumber { get; set; }
public string Fullname { get; set; }
public string EmailAddress { get; set; }
}
}
Then I have this Data Util:
public class DataUtil : IDataUtil
{
private readonly IConfiguration _config;
private readonly ILogger<DataUtil> _logger;
private readonly HttpClient _myClient;
public DataUtil
(
IConfiguration config,
ILogger<DataUtil> logger,
HttpClient myClient
)
{
_config = config;
_logger = logger;
_myClient = myClient;
}
public CustomerDetail GetCustomerDetail(string accountNumber)
{
var responseResults = new CustomerDetail();
try
{
string custAccountNoUrl = _config.GetSection("Endpoints").GetValue<string>("customerUrl") + accountNumber;
_myClient.DefaultRequestHeaders.Accept.Clear();
_myClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = _myClient.GetAsync(custAccountNoUrl).Result;
if (response.IsSuccessStatusCode)
{
var stringResult = response.Content.ReadAsStringAsync().Result;
responseResults = JsonConvert.DeserializeObject<CustomerDetail>(stringResult);
}
}
catch (Exception ex)
{
_logger.LogError($"An Error occured " + ex.ToString());
}
return responseResults;
}
}
Then this is the implementation:
public async Task<Response<string>> CreateCustomerDetailAsync(CreateDto requestDto)
{
var response = new Response<string>();
var accDetail = _dataAccess.GetCustomerDetail(requestDto.DrAccountNumber);
if (accDetail.EmailAddress != null)
{
var accountName = accDetail.Fullname;
var emailAddress = accDetail.EmailAddress.ToLower();
var mailBody = await EmailBodyBuilder.GetCustomerEmailBody(accountName, emailTempPath: "wwwroot/files/Html/CustomerEmail.html");
var mailRequest = new MailRequest()
{
Subject = "Customer Notification",
Body = mailBody,
ToEmail = emailAddress
};
bool emailResult = await _mailService.SendEmailAsync(mailRequest);
if (emailResult)
{
response.StatusCode = (int)HttpStatusCode.OK;
response.Successful = true;
response.Message = "Successful!";
return response;
}
}
}
From the third-party API given, there are moments when the customer does not have email address, then the EmailAddress field will not even appear at all from the response.
So that made me to get error here:
bool emailResult = await _mailService.SendEmailAsync(mailRequest);
I tried
if (accDetail.EmailAddress != null)
but it's not solving the problem.
Making use of
public async Task<Response<string>> CreateCustomerDetailAsync(CreateDto requestDto)
How do I make the application not to send email at all whenever EmailAddress field does not exist or it's null?
So, I have been struggling with this for a while now, but I am getting a bit further each day. But now I am really stuck.
What I am trying to do:
I have an automation account with a runbook (PowerShell)
I want to create a schedule
Then I want to connect that schedule to the runbook so the runbook is executed at a specific date and time.
I want to achieve this through C# with the Management API of Azure. I found a few webpages that should give me all the information I needed, but it doesn't work.
The first one is this: https://learn.microsoft.com/en-us/rest/api/automation/schedule/create-or-update?tabs=HTTP
I get this to work and I can see the schedule after I ran the code.
The second one is this: https://learn.microsoft.com/en-us/rest/api/automation/job-schedule/create?tabs=HTTP
And here I get stuck. I get a 404 error when I execute the request and I have no idea what I am doing wrong.
The code below is far from perfect, but it's a POC and not the actual application. I want to make it work before I refactor.
This class has all the logic for the schedule:
public class ScheduleLogic
{
const string subscriptionId = "######";
const string resourceGroupName = "######";
const string automationAccountName = "######";
const string tenantId = "######";
const string clientId = "######";
const string clientSecret = "######";
const string resourceId = "https://management.azure.com/";
public string accessToken { get; set; }
// THIS ONE DOESN'T WORK!
public async Task ConnectScheduleToRunbook(RunbookSchedule runbookSchedule)
{
StringContent body = new(JsonSerializer.Serialize(runbookSchedule));
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/jobSchedules/{runbookSchedule.Properties.Schedule.JobScheduleId}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Put, url, body);
}
public async Task CreateSchedule(Schedule schedule)
{
if (string.IsNullOrEmpty(schedule.JobScheduleId))
throw new Exception("Job schedule ID is empty");
StringContent body = new(JsonSerializer.Serialize(schedule));
body.Headers.ContentType = new MediaTypeHeaderValue("application/json");
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/schedules/{schedule.JobScheduleId}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Put, url, body);
}
public async Task DeleteSchedule(string scheduleName)
{
string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Automation/automationAccounts/{automationAccountName}/schedules/{scheduleName}?api-version=2019-06-01";
await ExecuteRequest(RequestType.Delete, url, null);
}
private async Task ExecuteRequest(RequestType requestType, string url, StringContent? body)
{
HttpResponseMessage response;
using (HttpClient client = new())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {await GetAccessToken()}");
switch (requestType)
{
case RequestType.Put:
if (body == null)
throw new Exception("Body cannot be empty");
response = await client.PutAsync(url, body);
break;
case RequestType.Delete:
response = await client.DeleteAsync(url);
break;
default:
throw new Exception("Unknown request type");
}
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
throw new Exception(content);
}
}
}
private async Task<string> GetAccessToken()
{
if (!string.IsNullOrEmpty(accessToken))
return accessToken;
AuthenticationContext authContext = new("https://login.microsoftonline.com/" + tenantId);
ClientCredential clientCreds = new(clientId, clientSecret);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(resourceId, clientCreds);
accessToken = authResult.AccessToken;
return accessToken;
}
}
Models:
public class RunbookSchedule
{
public RunbookScheduleProperties Properties { get; set; }
}
public class RunbookScheduleProperties
{
public Schedule Schedule { get; set; }
public Runbook Runbook { get; set; }
public object Parameters { get; set; }
}
public class Runbook
{
public string Name { get; set; }
}
public class Schedule
{
public string Name { get; set; }
[JsonIgnore]
public string JobScheduleId { get; set; }
[JsonPropertyName("properties")]
public ScheduleProperties ScheduleProperties { get; set; }
}
public class ScheduleProperties
{
public string Description { get; set; }
public DateTime StartTime { get; set; }
public DateTime ExpiryTime { get; set; }
public string Frequency { get; set; }
public object AdvancedSchedule => new { };
}
And the unit tests I use to test the logic:
public class ScheduleTests
{
private readonly string jobId = "d52004cc-b7ec-4b9b-99c1-3922492f6e1b-1";
private readonly string runbookName = "RunFunctionApp";
private readonly ScheduleLogic scheduleService;
public ScheduleTests()
{
scheduleService = new();
}
[Fact]
public async Task Should_CreateASchedule()
{
Schedule newScheduleSettings = new()
{
Name = jobId,
JobScheduleId = jobId,
ScheduleProperties = new()
{
Description = "Here is another example",
StartTime = new DateTime(2024, 03, 27, 9, 0, 0, 0),
ExpiryTime = new DateTime(2024, 03, 27, 10, 0, 0, 0),
Frequency = "OneTime"
}
};
await scheduleService.CreateSchedule(newScheduleSettings);
}
[Fact]
public async Task Should_DeleteSchedule()
{
await scheduleService.DeleteSchedule(jobId);
}
[Fact]
public async Task Should_ConnectScheduleAndRunbook()
{
await scheduleService.ConnectScheduleToRunbook(new RunbookSchedule
{
Properties = new()
{
Schedule = new() { Name = jobId, JobScheduleId = jobId },
Runbook = new() { Name = runbookName },
Parameters = new { id = 12 }
}
});
}
}
Somehow I think I am doing something stupid and the fix/answer is really simple.
I tried to mess around with the URL because I think there is a problem with that, not sure why... Just a feeling.
Google did show me some results, but those were for schedules of other services. Runbooks and automation don't seem to be very popular.
ChatGPT comes with older, obsolete solutions. Visual Studio gives a lot of green and even red lines when I try those suggestions.
I'm implementing asp.net core 3.1 project. I'm using a Worker service which should read every day some data from SQL server table and the call an api which should send an sms and the sms content should be those data which were read from sql server. Now I could call the sms api and send some static data by it to the user. The problem is although, I defined that the api should be called each 5 seconds but it just send the sms 1 time to the use. I appreciate if anyone tells me how I can fix this. Here below is what I have tried:
public class Worker : BackgroundService, IHostedService, ISendingSMS
{
private readonly ILogger<Worker> _logger;
private MarketsContext _context;
public IServiceScopeFactory _serviceScopeFactory;
//---------------------------------------------------
public Report report { get; set; }
private MsgContent msgContent;
//---------------------------------------------------
public Worker(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
//---------------------------------------------------------------------------------------
public async Task GetReport()
{
IQueryable<Report> reportData = _context.Report.Select(x => new Report
{
ReportDate = x.ReportDate,
TotalMarkets = x.TotalMarkets,
})/*.FirstOrDefault()*/;
report = await reportData.AsNoTracking().FirstOrDefaultAsync();
}
//-------------------------------------------------------------------------------
public override async Task StartAsync(CancellationToken cancellationToken)
{
// DO YOUR STUFF HERE
await base.StartAsync(cancellationToken);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
// DO YOUR STUFF HERE
await base.StopAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MarketsContext>();
var _smssend = scope.ServiceProvider.GetRequiredService<SendingSMSService>();
_context = dbContext;
await GetReport();
var message = new MsgContent
{
Username = "cse.etsg",
Password = "wrwe",
From = "4500000",
To = "+429124203243",
Message = report.ReportDate + "" + + report.TotalMarkets
};
_smssend.sendSMS(message);
await Task.Delay(120000, stoppingToken);
//Do your stuff
}
}
}
}
SendingSMSService
public class SendingSMSService /*: ISendingSMS*/
{
public Report report { get; set; }
// Here this method calls sms api sccessfully
public async void sendSMS(MsgContent message)
{
using (var client = new HttpClient())
{
var myContent = JsonConvert.SerializeObject(message);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var result = client.PostAsync("https://api/SendSMS", byteContent).Result)
{
string Response = await result.Content.ReadAsStringAsync();
}
}
}
}
Report
public partial class Report
{
public int Id { get; set; }
public string ReportDate { get; set; }
public int? TotalMarkets { get; set; }
}
Program
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
var configuration = hostContext.Configuration;
services.AddHostedService<Worker>();
//------------------------------------------------------------------------------
services.AddScoped<SendingSMSService>();
var connection = hostContext.Configuration.GetConnectionString("Markets");
var optionsBuilder = new DbContextOptionsBuilder<MarketsContext>();
optionsBuilder.UseSqlServer(connection);
services.AddScoped<MarketsContext>(s => new MarketsContext(optionsBuilder.Options));
});
}
In this scenario you can use a Timed background task see timed-background-tasks.
In your case you would need two of them. They are sheduled according to your parameters of the Timer in the StartAsyncmethod, you not have to call them explicit.
The created services are then injected in IHostBuilder.ConfigureServices (Program.cs) with: services.AddHostedService<TimedHostedService>();
I have been following some tutorials (e.g., https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client) and trying to retrieve a list of jobs and print them out in my .Net console app.
The test data I am using is located at https://boards-api.greenhouse.io/v1/boards/vaulttec/jobs and supplied it to client.BaseAddress.
Since I am able to compile and run the tutorial succesfully, I simply used the same code and changed some of it to run the above test data (see below).
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace GreenhouseJobs
{
public class Job
{
public string Id { get; set; }
public string Title { get; set; }
public string Location { get; set; }
public DateTime LastUpdated { get; set; }
}
class GreenhouseJobsClient
{
static HttpClient client = new HttpClient();
static void ShowJob(Job job)
{
Console.WriteLine($"Id: {job.Id}\tTitle: " +
$"{job.Title}\tLocation: {job.Location}\tLast Updated: {job.LastUpdated}");
}
static async Task<Uri> CreateJobAsync(Job job)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"vaulttec/jobs", job);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
static async Task<Job> GetJobAsync(string path)
{
Job job = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
job = await response.Content.ReadAsAsync<Job>();
}
return job;
}
//static async Task<Product> UpdateProductAsync(Product product)
//{
// HttpResponseMessage response = await client.PutAsJsonAsync(
// $"api/products/{product.Id}", product);
// response.EnsureSuccessStatusCode();
// // Deserialize the updated product from the response body.
// product = await response.Content.ReadAsAsync<Product>();
// return product;
//}
//static async Task<HttpStatusCode> DeleteProductAsync(string id)
//{
// HttpResponseMessage response = await client.DeleteAsync(
// $"api/products/{id}");
// return response.StatusCode;
//}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
Console.ReadLine();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("https://boards-api.greenhouse.io/v1/boards/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
Job job = new Job
{
Id = "323232",
Title = "Test",
Location = "Test",
LastUpdated = DateTime.Now
};
var url = await CreateJobAsync(job);
Console.WriteLine($"Created at {url}");
// Get the product
job = await GetJobAsync(url.PathAndQuery);
ShowJob(job);
// Update the product
//Console.WriteLine("Updating price...");
//product.Price = 80;
//await UpdateProductAsync(product);
// Get the updated product
//product = await GetProductAsync(url.PathAndQuery);
//ShowProduct(product);
// Delete the product
//var statusCode = await DeleteProductAsync(product.Id);
//Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
The problem is, when I run the app, it does not return anything. Error message: "Response status code does not indicate success: 404 (Not Found)".
There are couple issues here
You are not just trying to retrieve but also create first. I am not sure if this api supports a POST or not, it probably doesn't, hence a 404
The Job entity is incorrect. Location needs to be an object itself and not a mere string if you need it deserialized correctly. Here is a working version of get call
https://pastebin.com/85NnAQY9
namespace ConsoleApp3
{
public class JobsJson
{
public List<Job> Jobs { get; set; }
}
public class Job
{
public string Id { get; set; }
public string Title { get; set; }
public Location Location { get; set; }
public DateTime LastUpdated { get; set; }
}
public class Location
{
public string Name { get; set; }
}
class GreenhouseJobsClient
{
static HttpClient client = new HttpClient();
static void ShowJobs(List<Job> jobs)
{
foreach (var job in jobs)
{
Console.WriteLine($"Id: {job.Id}\tTitle: " +
$"{job.Title}\tLocation: {job.Location}\tLast Updated: {job.LastUpdated}");
}
}
static async Task<List<Job>> GetJobAsync(string path)
{
var jobs = new List<Job>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
var stringResponse = await response.Content.ReadAsStringAsync();
var re = JsonConvert.DeserializeObject<JobsJson>(stringResponse);
jobs = re.Jobs;
}
return jobs;
}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
Console.ReadLine();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("https://boards-api.greenhouse.io/v1/boards/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Get the product
var jobs = await GetJobAsync("vaulttec/jobs");
ShowJobs(jobs);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
The 404 is being returned by this line of code:
HttpResponseMessage response = await client.PostAsJsonAsync("vaulttec/jobs", job);
This is because the URL https://boards-api.greenhouse.io/v1/boards/vaulttec/jobs returns 404 when you try and make a POST request. Probably you need to be authorised to create jobs.
You can however make a GET request to this URL just fine.
I have web app project and an angular 2 project.
I would like use SignalR to send message from the server.
Then I found this article about implementing it.
But I don't know how to send message to the current user.
Code for send message C#:
public class EventHub : Hub
{
public async Task Subscribe(string channel)
{
await Groups.Add(Context.ConnectionId, channel);
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} subscribed",
Data = new
{
Context.ConnectionId,
ChannelName = channel
}
};
await Publish(#event);
}
public async Task Unsubscribe(string channel)
{
await Groups.Remove(Context.ConnectionId, channel);
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} unsubscribed",
Data = new
{
Context.ConnectionId,
ChannelName = channel
}
};
await Publish(#event);
}
public Task Publish(ChannelEvent channelEvent)
{
Clients.Caller.OnEvent(Constants.AdminChannel, channelEvent);
return Task.FromResult(0);
}
public override Task OnConnected()
{
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} connected",
Data = new
{
Context.ConnectionId,
}
};
Publish(#event);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var #event = new ChannelEvent
{
Name = $"{Context.ConnectionId} disconnected",
Data = new
{
Context.ConnectionId,
}
};
Publish(#event);
return base.OnDisconnected(stopCalled);
}
}
public static class Constants
{
public const string AdminChannel = "admin";
public const string TaskChannel = "tasks";
}
public class ChannelEvent
{
public string Name { get; set; }
public string ChannelName { get; set; }
public DateTimeOffset Timestamp { get; set; }
public object Data
{
get { return _data; }
set
{
_data = value;
Json = JsonConvert.SerializeObject(_data);
}
}
private object _data;
public string Json { get; private set; }
public ChannelEvent()
{
Timestamp = DateTimeOffset.Now;
}
}
Then in my controller I create IhubContent
private readonly IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<EventHub>();
and invoke my publish event:
private void PublishEvent(string eventName, StatusModel status)
{
_context.Clients.Group(Constants.TaskChannel).OnEvent(Constants.TaskChannel, new ChannelEvent
{
ChannelName = Constants.TaskChannel,
Name = eventName,
Data = status
});
}
But this message sent to all users. Help me to fix this issue and implement code to the send message to the current user.
The IHubContext object your are using has multiple methods, one of which is Clients, of type IHubConnectionContext.
In there you have Groups, Group, Clients & Client methods which abstract what you want to target.
In your case using:
_context.Clients.Client(connectionId).send(message);
should be working fine (you still need to propagate the connectionId though).
N.B.: send is a JS method that should be implemented on client-side.