Making throwing exceptions work - c#

I've been trying to write a small program with these instructions:
In this assignment you should write a simple web application with one link on the front page of the web. If the link is clicked, the user will simply be routed to the front page again (using RedirectToAction). However, occasionally, the action method might throw an exception (but not always). Occasionally (one in every 5 occasions) the method should throw an ArgumentException, and occasionally (again, in maybe 1 in a 5), it should throw a custom Exception object you should declare yourself, called MyApplicationException.
In HomeController I have:
public class HomeController : Controller
{
List<Logger> m_loggers = new List<Logger>();
protected override void OnException(ExceptionContext fc)
{
base.OnException(fc);
Exception ex = fc.Exception;
Logger.Instance.Log(ex);
}
public ActionResult Index()
{
string strLogFile = System.Configuration.ConfigurationManager.AppSettings["LogFile"];
string strEmail = System.Configuration.ConfigurationManager.AppSettings["Email"];
try
{
RedirectToAction("Index");
using(MailMessage message = new MailMessage())
{
message.To.Add(strEmail);
message.Subject = "Villuskilaboð";
message.Body = "Upp hefur komið villa frá Skilaverkefni 4!";
using(SmtpClient client = new SmtpClient())
{
client.EnableSsl = true;
client.Send(message);
}
}
}
catch(Exception ex)
{
Random r = new Random();
int rand = r.Next(1000);
if(rand % 5 == 0)
{
throw new System.ArgumentException("Randon villuskilaboð handa þér!");
}
Debug.WriteLine(ex.Message +
Environment.NewLine +
ex.StackTrace);
}
return View();
}
Logger class:
public class Logger
{
List<LogMedia>m_loggers = new List<LogMedia>();
private static Logger theInstance = null;
public static Logger Instance
{
get
{
if (theInstance == null)
{
theInstance = new Logger();
}
return theInstance;
}
}
private Logger()
{
m_loggers = new List<LogMedia>();
//m_loggers.Add(new TextFileLogMedia { });
//m_loggers.Add(new EmailLogMedia { });
}
public void Log(Exception ex)
{
foreach(LogMedia log in m_loggers)
{
log.LogMessage(ex.Message + Environment.NewLine);
}
}
}
LogMedia
public class LogMedia
{
public virtual void LogMessage(string Message)
{
}
public class OutputWindowLogMedia: LogMedia
{
public override void LogMessage(string Message)
{
System.Diagnostics.Debug.WriteLine(Message);
}
}
public class TextFileLogMedia: LogMedia
{
public override void LogMessage(string Message)
{
//File.AppendAllText("c:\\Temp.Log.txt", Message);
}
}
public class EmailLogMedia: LogMedia
{
public override void LogMessage(string Message)
{
}
}
}
I´m stuck for now and seems not getting it to work, my Visual Studio crash and I get error up, don't think that is the exception, I´m so new to it so maybe it´s the box that should come up :) But the email never get to my account.
What am I still missing to make everything work? I know the file-thing isn't in this code, trying to make the other things to work first.
I've added information about my eMail in web.config.

You really need to rework your Index() method. I'm not in front of my computer with Visual Studio, but I'm surprised you code gets past the first line in your try. Having the RedirectToAction("Index") should throw a warning that the rest of the method will never be reached, and create an infinite loop when you try to access the method. The RedirectToAction("Index")` you have in your code does nothing as you don't return the results of that. Thank you Erik Noren
This would be how I'd structure your method instead:
public ActionResult Index() {
// No need to go higher, as it's always just as random with a modulo
int rnd = (new Random()).Next(5);
try {
switch (rnd) {
case 1: // Or any of the 5 numbers you want.
throw new ArgumentException();
case 4: // Again, any of the 5 numbers
throw new MyApplicationException();
default:
return RedirectToAction("Index");
}
}
catch (Exception ex) {
// Do your error logging here.
}
}

Related

How to get details of error from .NET catch to React.JS?

My Api Service is in .NET and my client side is in React.js. I use axios.post to send parameters and retrieve datas from .NET. I want to see error details on react.js side when something happened in service side. Example codes are below;
[HttpPost]
public ConcreteAccrument CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
ConcreteApplication application = depositAmountParameters.application;
int multiplier = depositAmountParameters.multiplier;
bool forceCalculation = depositAmountParameters.forceCalculation;
long registryInfoOid = depositAmountParameters.registryInfoOid;
long subscriberRegistryOid = depositAmountParameters.subscriberRegistryOid;
try
{
Com.BS.WaterSupplyAndSeverage.Services.WaterSupplyAndSewerage wssService = new Com.BS.WaterSupplyAndSeverage.Services.WaterSupplyAndSewerage();
return wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
}
catch (BSException e)
{
FileLogger.Error(CLASS_NAME, "CalculateDepositAmount", e.Message, e.StackTrace, application, multiplier, forceCalculation);
BSCommunicationException commException = new BSCommunicationException();
commException.Id = e.Id;
commException.ExceptionMessage = e.ExceptionMessage;
throw new FaultException<BSCommunicationException>(commException, new FaultReason(commException.ExceptionMessage));
}
catch (Exception e)
{
FileLogger.Error(CLASS_NAME, "CalculateDepositAmount", e.Message, e.StackTrace, application, multiplier, forceCalculation);
BSCommunicationException commException = PrepareCommunicationException(e);
throw new FaultException<BSCommunicationException>(commException, new FaultReason(commException.ExceptionMessage));
}
}
There are some details in throw new FaultException at first catch(BSException e). It's not a system error. For example, data is null or some value are missing when first catch works. And second catch is system error. But in that code all catches return 500 error in React.Js side. All I want is to see all detail in first catch on React.js side. When I use "return error" in catch then I get convert error because my class return an object.
Here my react.js code;
export const CalculateDepositAmount = (APPLICATION,MULTIPLIER,FORCE_CALCULATION,REGISTRY_INFO_OID, SUBSCRIBER_REGISTRY_OID, SuccessOperation, FailedOperation) => {
return () => {
const body = { application:APPLICATION,multiplier:MULTIPLIER,forceCalculation:FORCE_CALCULATION,registryInfoOid:REGISTRY_INFO_OID, subscriberRegistryOid:SUBSCRIBER_REGISTRY_OID};
console.log("bodyFormData",body)
axios.post('https://localhost:44396/api/CalculateDepositAmount', body)
.then( async response => {
SuccessOperation({ CALCULATED_DEPOSIT_AMOUNT_DATA: await response.data });
})
.catch(() => {
FailedOperation({ CALCULATED_DEPOSIT_AMOUNT_DATA: null })
});
}
}
I am assuming that this is not asp.net core / 5 / 6, but vanilla 4.x
One thing you can do is change the method signature to IHttpActionResult, so you can return different status codes, with varying payloads back to the client:
public IHttpActionResult CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
try
{
var result = wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
return Ok(result);
}
catch (BSException e)
{
return BadRequest(e.Message)
//or
//return StatusCode(418)
}
catch (Exception e)
{
}
}
You can tailor the response to the client much better to your needs, instead of return either the object or an exception. You can find the full list of here:
https://learn.microsoft.com/en-us/previous-versions/aspnet/dn314678(v=vs.118)?redirectedfrom=MSDN
Another approach that will require some more refactoring, is to change the return type of your service to some sort of Result object, that indicates, whether it is a successfull operation or if a problem occured.
For example take this CommandResult example:
public class CommandResult<T>
{
private CommandResult(T payload) => Payload = payload;
private CommandResult(string failureReason)
{
FailureReason = failureReason;
}
public string FailureReason { get; }
public string Message { get; }
public bool IsSuccess => string.IsNullOrEmpty(FailureReason);
public T Payload { get; }
public static implicit operator bool(CommandResult<T> result) => result.IsSuccess;
public static CommandResult<T> Success(T payload)
=> new(payload);
public static CommandResult<T> Fail(string reason)
=> new(reason);
}
In your service you can now do the following:
public Commandresult<ConcreteAccrument> CalculateDepositAmount(DepositAmountParameters depositAmountParameters)
{
try
{
var result = // do the calculation
return CommandResult<ConcreteAccrument>.Success(result);
}
catch (BSException e)
{
return CommandResult<ConcreteAccrument>.Fail(e.Message);
}
catch (Exception e)
{
return CommandResult<ConcreteAccrument>.Fail(e.Message);
}
}
Now your controller simply has to decide, if it was successfull or not:
public IHttpActionResult CalculateDepositAmount([FromBody] DepositAmountParameters depositAmountParameters)
{
var result = wssService.CalculateDepositAmount(application, multiplier, forceCalculation, registryInfoOid, subscriberRegistryOid);
if(result.IsSuccess) // or simply if (result)
{
return Ok(result.Payload);
}
return Exception(result.FailureReason); //or whatever suits best.
}

Propagate exception message from service to controller .NET

I have a .NET appplication where there is a controller for receiving user requests, a service Service 1 which calls another service Service 2.
I have some code in the Service 2 where I query the database(DynamoDB) and get a 500 error in response when the user request values are incorrect. I want to handle this such that I catch this error/exception and send back the error message along with a 400 status code from the controller to the user. How should I modify the code to do this?
This is what I have tried. Currently, I'm just printing the error in Service 1 but I need to send it to the controller. Is sending the error message to the controller by throwing exceptions along the way the right way to do it?
The below code is similar to the actual code
Controller:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Service 1:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
Console.WriteLine(e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
Service 2:
private async Task<DynamoResult<TResponse>> GetPagedQueryResults(DynamoQuery query)
{
var results = new List<Document>();
try{
results = await search.GetNextSetAsync();
}
catch(Exception e){
throw new PaginationTokenException(e.Message);
}
return results;
}
[Serializable]
public class PaginationTokenException : Exception
{
public PaginationTokenException() { }
public PaginationTokenException(string message)
: base(message) {
throw new Exception(message);
}
public PaginationTokenException(string message, Exception inner)
: base(message, inner) { }
}
Assuming you want to hide implementation details from the controller (i.e. you don't want the controller to know/care that it's DynamoDB), I would create a custom exception and throw that from Service1.
Service1 would look something like this:
public async Task<DynamoResult<EntityResponse>> ListByParams(DynamoQuery entityQuery)
{
results = new DynamoResult<Entity>();
try {
results = await GetPagedQueryResults(entityQuery);
}
catch (Exception e) {
throw new MyCustomException('My error message', e);
}
return new DynamoResult<EntityResponse>
{
Data = results.Data.Select(_mapper.Map<EntityResponse>).ToList(),
};
}
In the controller you can then capture that exception explicitly:
[HttpGet]
[Authorize(Policy = "Read-Entity")]
[Route("byParams/{param1}/{param2}")]
[Produces(typeof(DynamoResult<EntityResponse>))]
public async Task<IActionResult> ListByParams([FromQuery] DynamoQuery entityQuery)
{
try
{
return await HandleRequest(async () =>
{
return Ok((await _entityStore.ListByParams(entityQuery)));
});
}
catch (MyCustomException e)
{
return BadRequest(e.Message);
}
}

Xamarin.Forms TextToSpeech

I'm trying to develop a mobile app with a SpeechToText feature, I found an example here Original Post and i tried to follow its steps, but when i run the application and i tap on the button to record, i get a message saying "Unhandled Exception occurr, No body on method..".
I tried to debug and what I get is that its something related to the DependecyService running the SpeechToTextAsync method from the ISpeecehToText interface.
Now I dont use interfaces too much so i'm a bit stuck understanding what is causing this error and how to solve it.
namespace LiveScoring {
public partial class MainPage : ContentPage {
public MainPage() {
InitializeComponent();
}
public void RecordBtn_Clicked(object sender, EventArgs e) {
WaitForSpeechToText();
}
private async void WaitForSpeechToText() {
Output_lbl.Text = await DependencyService.Get<ISpeechToText>().SpeechToTextAsync();
>> here I get the error
}
}
}
using System.Threading.Tasks;
namespace LiveScoring {
public interface ISpeechToText {
Task<string> SpeechToTextAsync();
}
}
namespace LiveScoring.Droid {
public class SpeechToText : ISpeechToText {
private const int VOICE = 10;
public static string SpeechText;
public static AutoResetEvent autoEvent = new AutoResetEvent(false);
public SpeechToText() { }
public async Task<string> SpeechToTextAsync() {
var tcs = new TaskCompletionSource<string>();
try {
var voiceIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
voiceIntent.PutExtra(RecognizerIntent.ExtraPrompt, "Talk now");
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 15000);
voiceIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguage, Java.Util.Locale.Default);
SpeechText = "";
autoEvent.Reset();
try {
((Activity)Forms.Context).StartActivityForResult(voiceIntent, VOICE);
} catch (ActivityNotFoundException a) {
tcs.SetResult("Device doesn't support speech to text");
}
await Task.Run(() => { autoEvent.WaitOne(new TimeSpan(0, 2, 0)); });
return SpeechText;
} catch (Exception ex) {
tcs.SetException(ex);
}
return "";
}
}
}
Try to add this above your namespace LiveScoring.Droid { line, ie:
[assembly: Dependency(typeof(SpeechToText))]
namespace LiveScoring.Droid {
...
}
This way it will register the dependency service, so it will be called when you use the DependencyService.Get<>() method.

Asp Mvc 5 Async/Await Issue

public async Task<ActionResult> Index()
{
var service = new CoreServiceFactory().GetImpersonatingService();
try
{
var data = new Impersonation()
{
ImpersonatingId = "dac733c3-01ad-447b-b0df-3a7c21fef90b",
UserId = "dac733c3-01ad-447b-b0df-3a7c21fef90b"
};
var imp = await service.Add(data);
}catch(Exception ex) { throw ex; }
return View();
}
Above is one of my controllers action method. And this works fine when the insertion is successful. This should fail if the data already exists in database(unique constraints). So when i intentionally try to make it fail(i manually add the same record in the db and then try to add it again via this action method) the action method goes into a loop or something, the exception is never thrown , chrome keeps me showing me the loading icon , looks like it went into some deadlock state. Can someone please help me understand why it goes into deadlock state when exception is thrown and how can i handle it?
Below are the reference methods
service.Add(data)
public async Task<Impersonation> Add(Impersonation t)
{
if (ValidateData(t))
{
using (var uow = GetUnitOfWork())
{
var r = GetRepository(uow);
var item = r.Add(t);
try
{
var ret = await uow.Save();
if (ret > 0)
{
return item;
}
else
{
return null;
}
}
catch (Exception ex)
{
throw ex;
}
}
}
else
{
throw new ValidationException(null, "error");
}
}
uow.Save()
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
throw ex;
}
}
}
Here is my suggestion: in uow.Save, log the error in the catch block and return zero (do not throw any exceptions).
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
// log the error here
return 0;
}
}
}
I'm not sure if returning the null in the Add service is a good idea or not, you might need to handle that differently too.

Capturing error if it occurs

How to do that if there is any error (anyone) to capture this error and write to a log ..
Recently a client changed the password webmaster (email used to authenticate messages sent)
And this meant that the system was no longer able to send emails.
The problem is that the error was not discovered days later.
Would you like a way to catch this error and save the message (email) and a log error, how?
In controller
new MailController(_subsidiaryService).PedidoOrcamentoEmail(model).DeliverAsync();
in MailController
public EmailResult PedidoOrcamentoEmail(BudgetViewModel model)
{
From = string.Format("{0} <{1}>", model.Name, model.Email);
To.Add("Site <" + ConfigurationManager.AppSettings["toEmail"] + ">");
var subInfo = (from s in _subsidiaryService.Repository.Query()
where s.ID == model.SubsidiaryID
select new
{
s.Title,
s.District
}).SingleOrDefault();
ViewData["SubsidiaryTitle"] = subInfo.Title;
ViewData["SubsidiaryDistrict"] = subInfo.District;
Subject = "[Pedido de Orçamento] " + model.Name;
return Email("PedidoOrcamentoEmail", model);
}
protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// This is not executed
base.OnException(filterContext);
}
As a test, I put in an invalid password email webmaster (used for shipping)
Create a custom MailSender that inherits from the default MailSender and wrap the calls to the base methods in try..catch blocks. Then you can do anything with the exception when it occurs.
public class CustomMailSender : SmtpMailSender
{
private readonly ILog _log = LogManager.GetCurrentClassLogger();
public CustomMailSender() { }
public CustomMailSender(SmtpClient client) : base(client) { }
public new void Send(MailMessage mail)
{
try
{
base.Send(mail);
//Do some logging if you like
_log.Info(....)
}
catch (SmtpException e)
{
//Do some logging
var message = mail.RawMessageString();
_log.Error(message, e);
// rethrow if you want...
}
}
public new void SendAsync(MailMessage mail, Action<MailMessage> callback)
{
... similar ...
}
}
In your MailController the CustomMailSender is used like this:
public MailController() {
MailSender = new MyCustomMailSender();
}

Categories