I have an invoice importer hub like so:
public class ImporterHub : Hub, IDisconnect, IConnected
{
public void InvoiceImported(InvoiceImportedMessage message)
{
Clients["importer"].InvoiceImported(message);
}
public void FileImported(FileImportedMessage message)
{
Clients["importer"].FileImported(message);
}
public System.Threading.Tasks.Task Disconnect()
{
return Clients["importer"].leave(Context.ConnectionId, DateTime.Now.ToString());
}
public System.Threading.Tasks.Task Connect()
{
return Clients["importer"].joined(Context.ConnectionId, DateTime.Now.ToString());
}
public System.Threading.Tasks.Task Reconnect(IEnumerable<string> groups)
{
return Clients["importer"].rejoined(Context.ConnectionId, DateTime.Now.ToString());
}
}
In my controller, I'm capturing events for a long-running import process like so:
[HttpPost]
public ActionResult Index(IndexModel model)
{
if (ModelState.IsValid)
{
try
{
model.NotificationRecipient = model.NotificationRecipient.Replace(';', ',');
ImportConfiguration config = new ImportConfiguration()
{
BatchId = model.BatchId,
ReportRecipients = model.NotificationRecipient.Split(',').Select(c => c.Trim())
};
var context = GlobalHost.ConnectionManager.GetHubContext<ImporterHub>();
context.Groups.Add(this.Session.SessionID, "importer");
System.Threading.ThreadPool.QueueUserWorkItem(foo => LaunchFileImporter(config));
Log.InfoFormat("Queued the ImportProcessor to process invoices. Send Notification: {0} Email Recipient: {1}",
model.SendNotification, model.NotificationRecipient);
TempData["message"] = "The import processor job has been started.";
//return RedirectToAction("Index", "Home");
}
catch (Exception ex)
{
Log.Error("Failed to properly queue the invoice import job.", ex);
ModelState.AddModelError("", ex.Message);
}
}
private void LaunchFileImporter(ImportConfiguration config)
{
using (var processor = new ImportProcessor())
{
processor.OnFileProcessed += new InvoiceFileProcessing(InvoiceFileProcessingHandler);
processor.OnInvoiceProcessed += new InvoiceSubmitted(InvoiceSubmittedHandler);
processor.Execute(config);
}
}
private void InvoiceSubmittedHandler(object sender, InvoiceSubmittedEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<ImporterHub>();
var message = new InvoiceImportedMessage()
{
FileName = e.FileName,
TotalErrorsInFileProcessed = e.TotalErrors,
TotalInvoicesInFileProcessed = e.TotalInvoices
};
context.Clients["importer"].InvoiceImported(message);
}
private void InvoiceCollectionSubmittedHandler(object sender, InvoiceCollectionSubmittedEventArgs e)
{
}
private void InvoiceFileProcessingHandler(object sender, InvoiceFileProcessingEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<ImporterHub>();
var message = new FileImportedMessage()
{
FileName = e.FileName
};
context.Clients["importer"].FileImported(message);
}
I have the following script in my view for the importer:
<script type="text/javascript">
jQuery.connection.hub.logging = true;
var importerHub = jQuery.connection.importerHub;
importerHub.InvoiceImported = function (message) {
jQuery('#' + message.FileName + '_Invoices').text(message.TotalInvoicesInFileProcessed);
jQuery('#' + message.FileName + '_Errors').text(message.TotalErrorsInFileProcessed);
};
importerHub.FileImported = function (message) {
jQuery('#' + message.FileName + '_Processed').text('Done');
};
jQuery.connection.hub.start();
</script>
What I expected to happen:
I was expecting the server side events to trigger, which would send messages to the client,
which would, in turn, fire events to update the status of the import process.
What seems to be happening:
All server-side events trigger, all is well. The signalR library seems to initialize properly, but the events never fire, and I never get the updates to appear on the screen.
What am I doing wrong? This is my first attempt to use the signalR library, so it's entirely possible I'm doing everything wrong.
I believe your problem is that your client side hub events are named with init-caps and the default behavior of SignalR is to translate those to init-lower when publishing to the client to align with common JavaScript conventions. Try changing your hub event registrations to this:
importerHub.invoiceImported = function (message) {
AND
importerHub.fileImported = function (message) {
Related
I am trying to set up a WebSocket connection using the .net SignalR and React app as a client to be able to send private messages.
Here is my code on the client side:
const setUpSignalRConnection = async () => {
const connection = new HubConnectionBuilder()
.withUrl("http://localhost:5000/messaginghub")
.build();
setConnection(connection);
connection.on("ReceiveMessage", (message: string) => {
console.log("Recieved Message", message);
setChatMessages((oldArray) => [...oldArray, message]);
});
try {
await connection.start();
} catch (err) {
console.log("Errors", err);
}
return connection;
};
const SendMessage = async () => {
if (connection) {
try {
console.log("sending message");
await connection.send("SendPrivateMessage", user.user.email, message);
} catch (e) {
console.log("Errors sending message", e);
}
} else {
alert("No connection to server yet.");
}
};
and my server side code
public async Task SendPrivateMessage(string userEmail, string message)
{
var RecivingMessageUser = _unitOfWork.UserRepository.GetByEmail(userEmail);
var currUserEmail = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
var sender = _unitOfWork.UserRepository.GetByEmail(currUserEmail);
var newMessage = new MessagesDto
{
FromId = sender.UserId,
ToId = RecivingMessageUser.UserId,
MessageBody = message,
SentAt = DateTime.UtcNow,
};
await Clients.Group(userEmail).SendAsync("ReceiveMessage", message);
_unitOfWork.MessagingRepository.Insert(_mapper.Map<MessagesDto, Messages>(newMessage));
_unitOfWork.SaveChanges();
}
public override Task OnConnectedAsync()
{
var groupName = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
Groups.AddToGroupAsync(Context.ConnectionId, groupName);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception ex)
{
Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.FindFirstValue(ClaimTypes.NameIdentifier));
return base.OnDisconnectedAsync(ex);
}
With console.logs I see that I am sending a message once and the message is stored in DB once but somehow on the other end, I am getting two received messages.
I am testing it on my local machine in two separate browsers.
What am I doing wrong?
Which method on your back-end is calling twice?
You are telling your message saved in to the DB once so it shouldn't be the SendPrivateMessage method which is calling towice.
I've implemented ng-chat https://github.com/rpaschoal/ng-chat (SignalR).
I have 3 users: User1, User2 and User3
If I send a message from User1 to User2 it works well User2 receives the message, but if I create a group (with User1 I open User2's chat and then Add the User3) a new group is created with Users (User2 and User3).
So, when I send a message from this new chat, the users (User2 and User3) doesn't receive any message
Here is my SingalR Hub:
using AdvansysOficina.Api._Core.Infraestructura;
using AdvansysOficina.Api.Generales.Servicios.UsuarioNs;
using Microsoft.AspNetCore.SignalR;
using NgChatSignalR.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AdvansysOficina.Api.Desarrollo.Servicios.ConversacionPuntoNs.HubNs
{
public class ConversacionHub : Hub
{
private static List<ParticipantResponseViewModel> AllConnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
private static List<ParticipantResponseViewModel> DisconnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
private readonly object ParticipantsConnectionLock = new object();
private ISesion _sesion;
private IUsuarioServicio _usuarioServicio;
public ConversacionHub(ISesion sesion, IUsuarioServicio usuarioServicio)
{
_sesion = sesion;
_usuarioServicio = usuarioServicio;
}
public static IEnumerable<ParticipantResponseViewModel> ConnectedParticipants(string currentUserId)
{
return AllConnectedParticipants
.Where(x => x.Participant.Id != currentUserId);
}
public void Join(string userName, dynamic grupo)
{
lock (ParticipantsConnectionLock)
{
AllConnectedParticipants.Add(new ParticipantResponseViewModel()
{
Metadata = new ParticipantMetadataViewModel()
{
TotalUnreadMessages = 0
},
Participant = new ChatParticipantViewModel()
{
DisplayName = userName,
Id = Context.ConnectionId,
}
});
// This will be used as the user's unique ID to be used on ng-chat as the connected user.
// You should most likely use another ID on your application
//Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);
Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
}
public void SendMessage(MessageViewModel message)
{
var sender = AllConnectedParticipants.Find(x => x.Participant.Id == message.FromId);
if (sender != null)
{
Clients.Client(message.ToId).SendAsync("messageReceived", sender.Participant, message);
}
}
public override Task OnDisconnectedAsync(Exception exception)
{
lock (ParticipantsConnectionLock)
{
var connectionIndex = AllConnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);
if (connectionIndex >= 0)
{
var participant = AllConnectedParticipants.ElementAt(connectionIndex);
AllConnectedParticipants.Remove(participant);
DisconnectedParticipants.Add(participant);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
return base.OnDisconnectedAsync(exception);
}
}
public override Task OnConnectedAsync()
{
lock (ParticipantsConnectionLock)
{
var connectionIndex = DisconnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);
if (connectionIndex >= 0)
{
var participant = DisconnectedParticipants.ElementAt(connectionIndex);
DisconnectedParticipants.Remove(participant);
AllConnectedParticipants.Add(participant);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
return base.OnConnectedAsync();
}
}
}
}
My signalR Adapter (Angular)
import { ChatAdapter, Message, ParticipantResponse, Group, IChatController } from 'ng-chat';
import { map, catchError } from 'rxjs/operators';
import { HttpClient } from '#angular/common/http';
import * as signalR from '#aspnet/signalr';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { AlertasHelper } from '../../../shared/helpers/alertas.helper';
import { PushNotificationHelper } from './notifications/push-notification';
export class SignalRAdapter extends ChatAdapter {
public static serverBaseUrl = 'http://192.168.16.51:5021/'; // if running locally
public userId: string;
private grrupo;
private hubConnection: signalR.HubConnection;
constructor(private username: string, private http: HttpClient, private notification: PushNotificationHelper
) {
super();
this.initializeConnection();
}
private initializeConnection(): void {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`${SignalRAdapter.serverBaseUrl}chat`, { transport: signalR.HttpTransportType.LongPolling })
.build();
this.hubConnection
.start()
.then(() => {
this.joinRoom();
this.initializeListeners();
})
.catch(err => console.log(`Error while starting SignalR connection: ${err}`));
}
private initializeListeners(): void {
this.hubConnection.on('generatedUserId', (userId) => {
// With the userId set the chat will be rendered
this.userId = userId;
});
this.hubConnection.on('messageReceived', (participant, message) => {
// Handle the received message to ng-chat
console.log(message);
this.notification.notify('Nuevo mensaje de: ' + participant.displayName, message);
this.onMessageReceived(participant, message);
});
this.hubConnection.on('friendsListChanged', (participantsResponse: Array<ParticipantResponse>) => {
// Handle the received response to ng-chat
this.onFriendsListChanged(participantsResponse.filter(x => x.participant.id !== this.userId));
});
}
joinRoom(): void {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.send('join', this.username, '');
}
}
listFriends(): Observable<ParticipantResponse[]> {
// List connected users to show in the friends list
// Sending the userId from the request body as this is just a demo
// return this.http
// .post(`${SignalRAdapter.serverBaseUrl}listFriends`, { currentUserId: this.userId })
// .pipe(
// map((res: any) => res),
// catchError((error: any) => Observable.throw(error.error || 'Server error'))
// );
return of([]);
}
getMessageHistory(destinataryId: any): Observable<Message[]> {
// This could be an API call to your web application that would go to the database
// and retrieve a N amount of history messages between the users.
return of([]);
}
sendMessage(message: Message): void {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
console.log(message);
this.hubConnection.send('sendMessage', message);
}
}
groupCreated(group: Group): void {
console.log( group);
}
}
Use of component
<ng-chat #chat *ngIf="signalRAdapter && signalRAdapter.userId"
[adapter]="signalRAdapter"
[userId]="signalRAdapter.userId"
[groupAdapter]="signalRAdapter"
(onParticipantChatOpened)="chatOpened($event)"
[historyEnabled]="false">
</ng-chat>
I've downloaded the example of github's creator page, but he doesn't have an example with signalr using groups, I hope you can help me.
ng-chat treats groups as individual participants. You will have to join your room when this event gets invoked:
groupCreated(group: Group): void {
console.log( group);
// Invoke your SignalR hub and send the details of the newly created group
}
ng-chat will generate unique ids every time a group is created so you can track which group is which whenever one gets created from a running ng-chat instance. How you will handle the persistence of these groups is up to your application.
You might want to push a notification to involved users from your SignalR adapter that their friends list has changed (They'll be able to see the group at this stage). You could also decide not to do so and only push a notification if the user who has created the group send an initial message (Once again, up to your application requirements and needs).
You might also want to implement IChatGroupAdapter on your adapter to make the contract more explicit.
Hope this helps!
I want to create Live Chat application using SignalR in ASP.NET MVC. I have created this but the problem is it is sending message to all the users who are connected to that server. I only want to have private chat between two users. So please help me out. Here is my Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
//this is ChatHub.cs file
namespace LiveChat
{
public class ChatHub : Hub
{
public override System.Threading.Tasks.Task OnConnected()
{
Clients.Caller.user(Context.User.Identity.Name);
return base.OnConnected();
}
public void send(string message)
{
Clients.Caller.message("You:" + message);
Clients.Others.message(Context.User.Identity.Name + ": " + message);
}
}}
//This is Startup class
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(LiveChat.Startup))]
namespace LiveChat
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
}
//This is my js
<script>
var hub = $.connection.chatHub;
hub.client.message = function (msg) {
$("#message").append("<li>" + msg + "</li>")
}
hub.client.user = function (msg) {
$("#user").append("<li>" + msg + "</li>")
}
$.connection.hub.start(function () {
$("#send").click(function () {
if ($("#txt").val() == "") {
return;
}
hub.server.send($("#txt").val());
$("#txt").val(" ");
});
$("#txt").keyup(function (event) {
if ($("#txt").val() == "") {
return;
}
if (event.keyCode == 13) {
hub.server.send($("#txt").val());
}
});
});
</script>
you can use a static list , the list contains all client that connected to the hub and their connection id,
on send message, you have to the hub the text and the destination member. and than send to the specific memeber.
add the member to the list in the function OnConnected, the client id :
Context.clientID
send to specific client in function send
var clientId = ""; // get from the static list by id you got
var scbscriber = Clients.Client(clientId);
scbscriber.message(text);
public class ChatHub : Hub
{
private static List<Users> ConnectedUsers;
public ChatHub()
{
ConnectedUsers = new List<Users>();
}
public override System.Threading.Tasks.Task OnConnected()
{
Clients.Caller.user(Context.User.Identity.Name);
ConnectedUsers.Add(new Users(){
UserName = Context.User.Identity.Name,
ClientId = Context.clientID;
});
return base.OnConnected();
}
public void send(string message, string UserName)
{
//Clients.Caller.message("You:" + message);
var clientId = ConnectedUsers.FirstOrDefulat(x=>x.UserName == UserName).ClientId; // get from the static list by id you got
var scbscriber = Clients.Client(clientId);
scbscriber.message(Context.User.Identity.Name + ": "message);
//Clients.Others.message(Context.User.Identity.Name + ": " + message);
}
}
public class Users
{
public string UserName{get;set;}
public string ClientId {get;set;}
}
Here you go mate:
public void SendPrivateMessage(Messaging objMessaging)
{
var fromNurse = objConnectedUserList.FirstOrDefault(x => x.NurseId == objMessaging.FromNurseId);
var toNurse = objConnectedUserList.FirstOrDefault(x => x.NurseId == objMessaging.ToNurseId);
var chatObject = new { MessageThreadId = 0, Name = fromNurse.NurseName, Message = objMessaging.Message, DTStmp = DateTime.Now, frmNurseId = fromNurse.NurseId };
Result objResult = objMessagingDAL.InsertMessage(objMessaging);
if (toNurse != null)
{
Clients.Client(toNurse.ConnectionId).ReceivePrivateMessage(chatObject);
}
Clients.Caller.ReceivePrivateMessage(chatObject);
}
i'm having problem using events in my servicestack application.
I'm creating an SOA applicatin based on ServiceStack. I've had no problem creating a simple GET/POST manager within the host.
Now i would like to add events
I'm trying using an example, but the event is not received by the client
Does someone have an idea about that?
This is my server:
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
ServerEventsFeature serverEventsFeature = new ServerEventsFeature()
{
LimitToAuthenticatedUsers = false,
NotifyChannelOfSubscriptions = true,
OnPublish = (res, msg) =>
{
//fired after ever message is published
res.Write("\n\n\n\n\n\n\n\n\n\n");
res.Flush();
},
OnConnect = (eventSubscription, dictionary) =>
{
},
OnSubscribe = (eventSubscription) =>
{
}
};
Plugins.Add(serverEventsFeature);
container.Register<IServerEvents>(c => new MemoryServerEvents());
container.Register(c => new FrontendMessages(c.Resolve<IServerEvents>()));
container.Register<IWebServiceEventManager>(c => new WebServiceEventManager(DeviceManager, macroManager));
SetConfig(new HostConfig
{
DefaultContentType = MimeTypes.Json,
EnableFeatures = Feature.All.Remove(Feature.Html),
});
public class FrontendMessage
{
public string Level { get; set; }
public string Message { get; set; }
}
public class FrontendMessages
{
private readonly IServerEvents _serverEvents;
private Timer _timer;
public FrontendMessages(IServerEvents serverEvents)
{
if (serverEvents == null) throw new ArgumentNullException(nameof(serverEvents));
_serverEvents = serverEvents;
}
public void Start()
{
var ticks = 0;
_timer = new Timer(_ => {
Info($"Tick {ticks++}");
_timer.Change(500, Timeout.Infinite);
}, null, 500, Timeout.Infinite);
}
public void Info(string message, params object[] parameters)
{
var frontendMessage = new FrontendMessage
{
Level = "success",
Message = message
};
Console.WriteLine("Sending message: " + frontendMessage.Message);
_serverEvents.NotifyChannel("messages", frontendMessage);
}
This is my client:
public async void Connect()
{
try
{
Task.Delay(2000).Wait();
clientEvents = new ServerEventsClient("http://127.0.0.1:20001/", "messages");
clientEvents.OnConnect = (msg) =>
{
};
clientEvents.OnHeartbeat = () =>
{
};
clientEvents.OnCommand = (msg) =>
{
};
clientEvents.OnException = (msg) =>
{
};
clientEvents.OnMessage = (msg) =>
{
};
Dictionary<string, ServerEventCallback> handlers = new Dictionary<string, ServerEventCallback>();
handlers.Add("messages", (client, msg) =>
{
});
clientEvents.RegisterHandlers(handlers);
await clientEvents.Connect();
client = (IServiceClient)(clientEvents.ServiceClient);
}
catch (Exception e)
{
}
}
I'd first recommend looking at ServerEvents Examples and the docs for the C# ServerEventsClient for examples of working configurations.
Your extra ServerEventsFeature configuration isn't useful as you're just specifying the defaults and the Publish() new-line hack is not needed when you disable buffering in ASP.NET. So I would change it to:
Plugins.Add(new ServerEventsFeature());
Second issue is that you're use of Message Event handlers is incorrect, your C# ServerEventsClient is already connected to the messages channel. Your handlers is used to listen for messages sent to the cmd.* selector (e.g. cmd.FrontendMessage).
Since you're publishing a DTO to a channel, i.e:
_serverEvents.NotifyChannel("messages", frontendMessage);
You can use a Global Receiver to handle it, e.g:
public class GlobalReceiver : ServerEventReceiver
{
public void Any(FrontendMessage request)
{
...
}
}
client.RegisterReceiver<GlobalReceiver>();
Thanks mythz!
It works correectly.
Next step is to replicate the same behaviour on javascript client (events and get/post request). Do you have something to suggest me?
Thanks a lot!
Leo
After reviewing and trying many of the suggestions surrounding the error message:
"An asynchronous module or handler completed while an asynchronous
operation was still pending."
I found myself in the situation where even though the call to the MVC accountController actually EXECUTED the desired code (an email was sent to the right place with the right content) and a Try/Catch in the controller method would not 'catch' the error, the AngularJS factory that was initiating the call would receive a server error "page".
Factory:(AngularJS)
InitiateResetRequest: function (email) {
var deferredObject = $q.defer();
$http.post(
'/Account/InitiateResetPassword', { email: email }
)
.success(function (data) {
deferredObject.resolve(data);
})
.error(function (data) {
//This is a stop-gap solution that needs to be fixed..!
if (data.indexOf("An asynchronous module or handler completed while an asynchronous operation was still pending.") > 0) {
deferredObject.resolve(true);
} else {
deferredObject.resolve(false);
}
});
return deferredObject.promise;
}
MVC Controller (C#):
[HttpPost]
[AllowAnonymous]
public async Task<int> InitiateResetPassword(string email)
{
try
{
_identityRepository = new IdentityRepository(UserManager);
string callbackUrl = Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, "/account/reset?id=");
await _identityRepository.InitiatePasswordReset(email, callbackUrl);
return 0;
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
return 1;
}
}
Identity Repository/InitiatePasswordReset:
public async Task InitiatePasswordReset(string email, string callbackUrl)
{
try
{
var u = await _applicationUserManager.FindByEmailAsync(email);
string passwordResetToken = await GetResetToken(u);
callbackUrl = callbackUrl + HttpUtility.UrlEncode(passwordResetToken);
await _applicationUserManager.SendEmailAsync(u.Id, RESET_SUBJECT, string.Format(RESET_BODY, u.FirstName, u.LastName, callbackUrl));
}
catch(Exception ex)
{ //another vain attempt to catch the exception...
Console.WriteLine(ex.ToString());
throw ex;
}
}
The EmailService injected into the ASP.NET Identity "ApplicationUserManager"
public class EmailService : IIdentityMessageService
{
XYZMailer xyzMailer;
public EmailService()
{
xyzMailer = XYZMailer.getCMRMailer();
}
public async Task SendAsync(IdentityMessage message)
{
//original code as posted:
//await Task.FromResult(xyzMailer.SendMailAsync(message));
//solution from #sirrocco-
await xyzMailer.SendMailAsync(message);
}
}
and finally...the XYZMailer class
class XYZMailer
{
#region"Constants"
private const string SMTP_SERVER = "XYZEXCHANGE.XYZ.local";
private const string NO_REPLY = "noReply#XYZCorp.com";
private const string USER_NAME = "noreply";
private const string PASSWORD = "theMagicP#55word"; //NO, that is not really the password :)
private const int SMTP_PORT = 587;
private const SmtpDeliveryMethod SMTP_DELIVERY_METHOD = SmtpDeliveryMethod.Network;
#endregion//Constants
internal XYZMailer()
{
//default c'tor
}
private static XYZMailer _XYZMailer = null;
public static XYZMailer getXYZMailer()
{
if (_XYZMailer == null)
{
_XYZMailer = new XYZMailer();
}
return _XYZMailer;
}
public async Task<int> SendMailAsync(IdentityMessage message)
{
#if DEBUG
message.Body += "<br/><br/>DEBUG Send To: " + message.Destination;
message.Destination = "me#XYZCorp.com";
#endif
// Create the message:
var mail =
new MailMessage(NO_REPLY, message.Destination)
{
Subject = message.Subject,
Body = message.Body,
IsBodyHtml = true
};
// Configure the client:
using (SmtpClient client = new SmtpClient(SMTP_SERVER, SMTP_PORT)
{
DeliveryMethod = SMTP_DELIVERY_METHOD,
UseDefaultCredentials = false,
Credentials = new System.Net.NetworkCredential(USER_NAME, PASSWORD),
EnableSsl = true
})
{
// Send:
await client.SendMailAsync(mail);
}
return 0;
}
}
(note: originally the controller method was simply "public async Task InitiateResetPassword, I added the return type as an attempt to trap the error on the server. At runtime, return 0; does hit (breakpoint) the catch does not get hit and at the client")
At the moment I am simply filtering for the expected error message and telling javascript to treat it as a success. This solution has the benefit of 'actually working'... but it is not 'ideal'.
How do I prevent the error on the server?
or alternately,
How do I catch the error on the server?
You need to remove await Task.FromResult from EmailService because that makes it so the code executes synchronously instead of async.
As to why the the exception was still raised and bubbled up outside the try/catch - I suspect the Task.FromResult was the culprit here too - if you now raise an exception in SendAsync (just to test it) you should catch in the controller.