Get exception details - c#

I would like to have a function that when an exception is given to it, it will extract all the information about that exception and then write it to a database.
Before going to .NET Core 2, I was able to do this in the following manner:
var details = new ErrorDetails();
if (ex == null) return details;
var st = new StackTrace(ex, true);
var frames = st.GetFrames();
if (frames != null && frames.Length > 0)
{
var errorDetails = frames.Select(frame => new ErrorDetails
{
FileName = frame.GetFileName(),
LineNumber = frame.GetFileLineNumber().ToString(),
MethodName = frame.GetMethod().Name,
ClassName = frame.GetMethod().DeclaringType.FullName
});
return errorDetails.FirstOrDefault();
}
return details;
Ever since switching my project to .NET Core 2, this code comes back with most of this information being null/default; I took a look at the frames that I extract, and they don't have the information anymore. For things like FileName and LineNumber, the values are null. For things like MethodName and ClassName, the value is there but wrong.
I would crash my project with code such as this:
public class TestController : Controller
{
[HttpGet]
[AllowAnonymous]
public string ErrorHandling()
{
var a = int.Parse("fail parsing on purpose");
return a.ToString();
}
}
The value for MethodName ends up being StringToNumber and for ClassName is System.Number
I can't seem to find any information on the web as to why this is and how I can go about retrieving the details for the exception.
EDIT:
I thought it might also be useful to list how I am handling exceptions. I have written a custom middleware error exception handler very similar to the one in this SO post:
https://stackoverflow.com/a/48625298/2371128
EDIT 2:
This is being run in DEBUG mode.

Add the following to your Startup.cs
app.UseExceptionHandler(
options =>
{
options.Run(
async context =>
{
var ex = context.Features.Get<IExceptionHandlerFeature>();
if (ex != null)
{
try
{
await System.Threading.Tasks.Task.Run(async () =>
{
var builder = new DbContextOptionsBuilder<DBContext>();
builder.UseSqlServer(_config["ConnectionStrings:ContextConnection"]);
var _context = new DBContext(_config, builder.Options, httpContextAccessor);
//Log to DB
await repository.LogError(_context, ex.Error.Message, $"{ex.Error.InnerException?.Message}<br/>{ex.Error.StackTrace}");
});
}
finally
{
//Optional
await repository.SendMailToAdmin(ex.Error.Message, $"{ex.Error.InnerException?.Message}<br/>{ex.Error.StackTrace}");
}
context.Response.Redirect("/app/Errors/500");
}
});
}
);
//ErrorLog.cs
public class ErrorLog
{
public int Id { get; set; }
[Required]
[StringLength(500)]
public string Error { get; set; }
[Required]
[StringLength(4000)]
public string Details { get; set; }
public int? UserId { get; set; }
public int CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
}

Here is my suggestion:
public class ExceptionDetail
{
public string Message { get; set; }
public string InnerExceptionMessage { get; set; }
public string StackTrace { get; set; }
public IEnumerable<string> StackTraceLines { get; set; }
public string Target { get; set; }
public string Source { get; set; }
}
var exDetail = new ExceptionDetail
{
Message = exception.Message,
InnerExceptionMessage = exception.InnerException?.Message,
Source = exception.Source,
StackTrace = exception.StackTrace,
StackTraceLines = exception.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(),
Target = exception.TargetSite.ToString()
};

Related

System.ArgumentNullException: Value cannot be null Parameter name: value - How to fix this error?

Please help! I am getting error in the line :
details.NominalVoltage = String.Join(",", paneldetails?.NominalVoltage?.ToArray());
I have below code in my builder.
foreach (var panel in panelAddresses.Take(2))
{
var paneldetails = new SM_NFPA72ReportPage1();
details.batteryDetails = new List<Battery>();
var AssociatedPrimaryPowers = new Repository(new BuildConnection()).GetPanelPrimarypowerDevcies(reportInput.UseroId, panel, reportInput.BuildingId, reportInput.TestSessionId[0]).Result;
AssociatedPrimaryPowers.ForEach(x => paneldetails?.batteryDetails?.Add(new Battery
{
NominalVoltage = deviceDetailsList?.CustomProperty?.Where(y => y.fieldName == "nominalVoltage")?.FirstOrDefault()?.Value,
NominalAmps = deviceDetailsList?.CustomProperty?.Where(y => y.fieldName == "nominalAmps")?.FirstOrDefault()?.Value,
NominalLocation = deviceDetailsList?.CustomProperty?.Where(y => y.fieldName == "disconnectLocation")?.FirstOrDefault()?.Value,
Protection = deviceDetailsList?.CustomProperty?.Where(y => y.fieldName == "overCurrentType")?.FirstOrDefault()?.Value,
ProtectionAmps = deviceDetailsList?.CustomProperty?.Where(y => y.fieldName == "overCurrentAmps")?.FirstOrDefault()?.Value,
ProtectionLocation = deviceDetailsList?.CustomProperty?.Where(y => y.fieldName == "powerLocation")?.FirstOrDefault()?.Value,
}));
details.NominalVoltage = String.Join(",", paneldetails?.NominalVoltage?.ToArray());
details.NominalAmps = String.Join(",", paneldetails?.NominalAmps?.ToArray());
details.NominalLocation = String.Join(",", paneldetails?.NominalLocation?.ToArray());
details.Protection = String.Join(",", paneldetails?.Protection?.ToArray());
details.ProtectionAmps = String.Join(",", paneldetails?.ProtectionAmps?.ToArray());
details.ProtectionLocation = String.Join(",", paneldetails?.ProtectionLocation?.ToArray());
}
Below attached is my model for above builder:
public class SM_NFPA72ReportPage1 : IReportModel
{
public string NominalVoltage { get; set; }
public string NominalAmps { get; set; }
public string NominalLocation { get; set; }
public string Protection { get; set; }
public string ProtectionAmps { get; set; }
public string ProtectionLocation { get; set; }
public List<Battery> batteryDetails { get; set; }
public List<PanelDetailsInfo> panelInfo { get; set; }
}
I am reusing the Battery model to fetch the values from repository
public class Battery
{
public string NominalVoltage { get; set; }
public string NominalAmps { get; set; }
public string NominalLocation { get; set; }
public string Protection { get; set; }
public string ProtectionAmps { get; set; }
public string ProtectionLocation { get; set; }
}
The exception tells you that the parameter value is null, that should mean that:
paneldetails?.NominalVoltage?.ToArray()
...gives you a null result, and that the string.Join method does not accept it.
You need to make sure that you do not provide a null value to the method.
This can be achieved in multiple ways, for example by checking for null value before calling the method:
if (panelDetails?.NominalVoltage != null)
{
details.NominalVoltage = String.Join(",", paneldetails.NominalVoltage.ToArray());
}
or by returning a empty array by default if it is null:
details.NominalVoltage = String.Join(",", paneldetails?.NominalVoltage?.ToArray() ?? Array.Empty<string>());

Restsharp error on deserialzation when nested value is null

I'm running into an error when a nested value is null. If the value is not null everything works as expected. This does not happen if the value is not nested.
The error is:
InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
The error happens when I'm checking response.ErrorException != null on the List Contract
Json returned: Contract administrator is nested and blank error: ends is not nested blank and no error:
"result": [
{
"sys_id": "06dc3133db1747808c47499e0b96192e",
"number": "CNTR001234",
"short_description": "Contract 123",
"u_internal_contact": {
"link": "https://website",
"value": "5b4080490a0a3c9e016cb2a9f4eb57b1"
},
"vendor": {
"link": "https://website",
"value": "b7e7c073c0a801690143e7b7d29eb408"
},
"ends": "",
"payment_amount": "60000",
"u_status": "Active",
"starts": "2018-01-01",
"contract_administrator": ""
}
]
}
Code
public class Results
{
public List<Contract> items { get; set; }
}
public class Contract
{
public string sys_id { get; set; }
public string number { get; set; }
public string short_description { get; set; }
public string ends { get; set; }
public string payment_amount { get; set; }
public string u_status { get; set; }
public string starts { get; set; }
public Vendor vendor { get; set; }
public ContractAdmin contract_administrator { get; set; }
public InternalContact u_internal_contact { get; set; }
}
public class Vendor
{
public string link { get; set; }
public string value { get; set; }
}
public class ContractAdmin
{
public string link { get; set; }
public string value { get; set; }
}
public class InternalContact
{
public string link { get; set; }
public string value { get; set; }
}
public class refResults
{
public List<refName> itemName { get; set; }
}
public class refName
{
public string name { get; set; }
}
class ImportContracts
{
public static void ProcessImport()
{
RestClient contractsRequest = new RestClient(Properties.Settings.Default.RestURL);
contractsRequest.Authenticator = new HttpBasicAuthenticator(Properties.Settings.Default.userName, Properties.Settings.Default.password);
contractsRequest.AddHandler("application/json", new RestSharp.Deserializers.JsonDeserializer());
RestRequest request = new RestRequest();
request.RootElement = "result";
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
IRestResponse<List<Contract>> response = contractsRequest.Execute<List<Contract>>(request);
Console.WriteLine(response.Content);
if (response.ErrorException != null)
{
const string message = "Error retrieving response. Check inner details for more info.";
var ex = new ApplicationException(message, response.ErrorException);
throw ex;
}
foreach (Contract contract in response.Data)
{
//Console.WriteLine(contract.sys_id);
string strVendor = GetName(contract.vendor.link.ToString());
string strInternalContact = GetName(contract.u_internal_contact.link.ToString());
string strContractAdmin = GetName(contract.contract_administrator.ToString());
}
}
static public string GetName (string link)
{
RestClient nameRequest = new RestClient(link);
nameRequest.Authenticator = new HttpBasicAuthenticator(Properties.Settings.Default.userName, Properties.Settings.Default.password);
nameRequest.AddHandler("application/json", new RestSharp.Deserializers.JsonDeserializer());
RestRequest requestedName = new RestRequest();
requestedName.RootElement = "result";
requestedName.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
IRestResponse<List<refName>> response = nameRequest.Execute<List<refName>>(requestedName);
if (response.ErrorException != null)
{
const string message = "Error retrieving response. Check inner details for more info.";
var ex = new ApplicationException(message, response.ErrorException);
throw ex;
}
foreach (refName refname in response.Data)
{
return refname.name;
}
return "name not found";
}
}
Any help would be appreciated!
Looking at your JSON, "contract_administrator" is not null, it's an empty string. Your contract requires a ContractAdmin object, so what it's likely doing is attempting to cast an empty string to a ContractAdmin.
If you change "contract_administrator" to be null instead of an empty string, I'm willing to bet that it will parse correctly.

Use of UpdateAsync method ASP.NET Entity Framework

My entity looks as follows:
public class AddPatientReportDentalChartInput : IInputDto
{
[Required]
[MaxLength(PatientReportDentalChart.TeethDesc)]
public string Image { get; set; }
[Required]
public virtual int PatientID { get; set; }
[Required]
public virtual int TeethNO { get; set; }
public string SurfaceDefault1 { get; set; }
public string SurfaceDefault2 { get; set; }
public string SurfaceDefault3 { get; set; }
public string SurfaceDefault4 { get; set; }
public string SurfaceDefault5 { get; set; }
}
And the method by which i want to update is:
public async Task addPatientReportDentalChart(AddPatientReportDentalChartInput input)
{
var pid = input.PatientID;
var chartdetails = _chartReportRepository
.GetAll()
.WhereIf(!(pid.Equals(0)),
p => p.PatientID.Equals(pid)).ToList();
if (chartdetails.Count>0)
{
//Update should be apply here
//please suggest me the solution using updatesync
}
else
{
var patientinfo = input.MapTo<PatientReportDentalChart>();
await _chartReportRepository.InsertAsync(patientinfo);
}
}
What is the equivalent of InsertAsync when I want to update an existing entity? Is there an UpdateAsync equivalent method?
Updating an entity in Entity Framework requires you to retrieve the record, update it and then save changes. It will look roughly like this:
public async Task AddPatientReportDentalChartAsync(AddPatientReportDentalChartInput input)
{
var pid = input.PatientID;
var chartdetails = _chartReportRepository
.GetAll()
.WhereIf(!(pid.Equals(0)),
p => p.PatientID.Equals(pid)).ToList();
if (chartdetails.Count > 0)
{
var entity = await _chartReportRepository
.YourTableName
.FindAsync(entity => entity.SomeId == matchingId);
entity.PropertyA = "something"
entity.PropertyB = 1;
await _chartReportRepository.SaveChangesAsync();
}
else
{
var patientinfo = input.MapTo<PatientReportDentalChart>();
await _chartReportRepository.InsertAsync(patientinfo);
}
}
Try this if you're using .NET CORE 3.1
public async Task<int> UpdateChat(MChat mChat)
{
try
{
return await Task.Run(() =>
{
BDContext.Chat.Update(new Chat
{
Id = mChat.id,
UsuarioIdInicia = mChat.usuarioIdInicia,
UsuarioIdFinaliza = mChat.usuarioIdFinaliza,
EstadoChatId = mChat.estadoChatId
});
return BDContext.SaveChanges();
});
}
catch (Exception ex)
{
Console.WriteLine(Constantes.ERROR_DETECTADO + ex.InnerException.ToString());
return Constantes.ERROR_1;
}
}

Azure Mobile Service for Windows Phone 8.1 - Insert to existing DB

I am writing an app that has an Azure database. I've never did nything connected with Azure, so I am new to all the stuff. I've found on the internet and at microsoft documentation some tutorials, but I must have got sth wrong, cause it doesn't work. So I have a table at my database called Week, I've created a model in my code:
[DataContract]
public class Week
{
//[JsonProperty(PropertyName = "Id")]
//[DataMember]
public int Id { get; set; }
[JsonProperty(PropertyName = "Book")]
[DataMember]
public Book CurrentBook { get; set; }
[JsonProperty(PropertyName = "Is_Read")]
[DataMember]
public Boolean IsRead { get; set; }
[JsonProperty(PropertyName = "Pages_Read")]
[DataMember]
public int PagesRead { get; set; }
[JsonProperty(PropertyName = "Start_Date")]
[DataMember]
public DateTime StartDate { get; set; }
[JsonProperty(PropertyName = "User")]
[DataMember]
public User Reader { get; set; }
[JsonProperty(PropertyName = "Week_Number")]
[DataMember]
public int WeekNumber { get; set; }
public Week(Book currentBook, Boolean isRead, int pagesRead, DateTime startDate, User reader, int weekNumber)
{
CurrentBook = currentBook;
IsRead = isRead;
PagesRead = pagesRead;
StartDate = startDate;
Reader = reader;
WeekNumber = weekNumber;
}
public Week()
{
}
public int GetMonth()
{
//TODO: Implement the method.
return 0;
}
}
Then I created the WeekRepository for CRUD operations:
public class WeekRepository : BaseRepository<Week>
{
private IMobileServiceTable<Week> weekTable;
public string errorMesage = string.Empty;
public WeekRepository()
{
weekTable = MobileService.GetTable<Week>();
}
public async override Task<int> Save(Week entity)
{
try
{
await weekTable.InsertAsync(entity);
// .ContinueWith(t =>
//{
// if (t.IsFaulted)
// {
// errorMesage = "Insert failed";
// }
// else
// {
// errorMesage = "Inserted a new item with id " + entity.Id;
// }
//});
}
catch (WebException ex)
{
errorMesage = ex.Message;
}
return entity.Id;
}
public override void Update(Week entity)
{
return;
}
public override Week Load(int bookId)
{
var week = weekTable.Where(w => w.IsRead == false).ToListAsync();
return week.Result.Single();
}
public override List<Week> LoadByUserId(int userId)
{
return new List<Week>();
}
public Week LoadCurrentWeek(int userId)
{
return new Week();
}
}
To test if it works, I wrote a simple test:
[TestMethod]
public void ShouldSaveWeekToTheDB()
{
//ARANGE
Week weekTestEntity = new Week(null, false, 10, new DateTime(), null, 1);
//ACT
int id = weekRepository.Save(weekTestEntity).Result;
//ASSERT
var savedItem = weekRepository.Load(1);
Assert.AreEqual(false, savedItem.IsRead);
}
However, InsertAsync() throws an exception - Not Found. I've no idea what I am doing wrong, cause it seems a simple thing as far as I can see from the material on the Internet.
If You could help me, I would be really grateful!
Thank You in advance!
Best Regards,
Roman.

Error Message of ResponsStatus should not be null when error is thrown and message is provided

I am using ServiceStack and I am having trouble getting back the error message in the ResponseStatus when an error is thrown.
My service Requests/Responses are named according to the naming convention required by ServiceStack.
REQUEST:
namespace DataDictionary.ServiceModel
{
// Create the name of the Web Service (i.e. the Request DTO)
[RestService("/receivables/{id}", "GET")]
[RestService("/years/{year}/processes/{processname}/receivables/{name}", "GET")]
[DataContract]
public class ReceivableRequest
{
[DataMember]
public int id { get; set; }
[DataMember]
public int year { get; set; }
[DataMember]
public string processname { get; set; }
[DataMember]
public string name { get; set; }
}
}
RESPONSE:
namespace DataDictionary.ServiceModel
{
[DataContract]
public class ReceivableRequestResponse : IHasResponseStatus
{
public ReceivableRequestResponse()
{
this.ResponseStatus = new ResponseStatus();
}
[DataMember]
public int id { get; set; }
[DataMember]
public string name { get; set; }
[DataMember]
public Uri process { get; set; }
[DataMember]
public Uri year { get; set; }
#region IHasResponseStatus Members
[DataMember]
public ResponseStatus ResponseStatus {get; set;}
#endregion
}
}
SERVICE:
namespace DataDictionary.ServiceInterface
{
// Create the Web Service implementation
public class ReceivableService : RestServiceBase<ReceivableRequest>
{
private readonly IRepository<Receivable> m_receivableRepository;
public ReceivableService(IRepository<Receivable> receivableRepository)
{
m_receivableRepository = receivableRepository;
}
public override object OnGet(ReceivableRequest request)
{
if (request != null && request.id > 0)
{
return GetSpecificReceivable(request.id);
}
}
private object GetSpecificReceivable(int id)
{
Receivable receivable = m_receivableRepository.SingleOrDefault(rec => rec.Id == id);
if (receivable != null)
{
return receivable;
}
else
{
throw new HttpError(HttpStatusCode.NotFound, new ArgumentException(string.Format("Could not find receivable with id {0}.", id)));
}
}
}
}
The error message is returned when I unit test the service
[Test]
public void Can_GET_Receivable_Not_Found_Successfully()
{
//given
var year = new Year { Name = 2006 };
IRepository<Year> repository = Kernel.Get<IRepository<Year>>();
repository.Add(year);
var process = new Process { Name = "TEST", Year = year };
year.AddProcess(process);
var receivable = new Receivable { Name = "File1" };
process.AddReceivable(receivable);
repository.Update(year);
var service = Kernel.Get<ReceivableService>();
var request = new ReceivableRequest { year = 2011, processname = "TEST", name = "File1" };
//when
try
{
var entity = (Receivable)service.OnGet(request);
}
//then
catch (HttpError e)
{
e.Message.ShouldEqual("Could not find receivable File1 from process TEST in year 2011.");
e.StatusCode.ShouldEqual(HttpStatusCode.NotFound);
}
}
However, when I send requests using fiddler, I would expect the error message to be returned in the JSON,
The status code 404 is available but there is no error message.
How can I get access to the error message?

Categories

Resources