Help with Exception Handling in ASP.NET C# Application - c#

yesterday i posted a question regarding the Exception Handling technique, but i did'nt quite get a precise answer, partly because my question must not have been precise.
So i will ask it more precisely.
There is a method in my BLL for authenticating user. If a user is authenticated it returns me the instance of the User class which i store in the session object for further references.
the method looks something like this...
public static UsersEnt LoadUserInfo(string email)
{
SqlDataReader reader = null;
UsersEnt user = null;
using (ConnectionManager cm = new ConnectionManager())
{
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#Email", email);
try
{
reader = SQLHelper.ExecuteReader(cm.Connection,
"sp_LoadUserInfo", parameters);
}
catch (SqlException ex)
{
//this gives me a error object
}
if (reader.Read())
user = new UsersDF(reader);
}
return user;
}
now my problem is suppose if the SP does not exist, then it will throw me an error or any other SQLException for that matter. Since this method is being called from my aspx.cs page i want to return some meaning full message as to what could have gone wrong so that the user understands that there was some problem and that he/she should retry logging-in again.
but i can't because the method returns an instance of the User class, so how can i return a message instead ??
i hope i made it clear !
thank you.

There are a lot of approaches you could take here, an easy one would be to return null if you can't find an appropriate user object (because of the exception). then in the calling code you just test for null and if it is null display an error message.
for example
User u = LoadUserInfo(email);
if(u == null)
{
ErrorLabel.Text = "Could not log in.";
ErrorLabel.Visible = true;
//.... or some other notification
}
else
{
//... normal load
}
that would be a basic way to go about it.

There are lot of approaches you have but in your method you declared "SqlDataReader reader = null;" some times you will get error at if condition "if (reader.Read())" because you declared as null.
public static UsersEnt LoadUserInfo(string email)
{
SqlDataReader reader = new SqlDataReader();
UsersEnt user = null;
using (ConnectionManager cm = new ConnectionManager())
{
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#Email", email);
try
{
reader = SQLHelper.ExecuteReader(cm.Connection,
"sp_LoadUserInfo", parameters);
if (reader.Read())
user = new UsersDF(reader);
}
catch (SqlException ex)
{
user.Exception=ex.Message;
}
}
return user;
}
and then
User us = LoadUserInfo(email);
if(us.Exception != null)
{
ErrorLabel.Text = "Could not log in.";
ErrorLabel.Visible = true;
//.... or some other notification
}
else
{
//... normal load
}
i think it will work.

Related

c# How to create a Method on a Method, or Method Chain

I have created a simplified SQL Data class, and a class method for returning a ready to use resultset:
public SQL_Data(string database) {
string ConnectionString = GetConnectionString(database);
cn = new SqlConnection(ConnectionString);
try {
cn.Open();
} catch (Exception e) {
Log.Write(e);
throw;
}
}
public SqlDataReader DBReader(string query) {
try {
using (SqlCommand cmd = new SqlCommand(query, this.cn)) {
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
} catch {
Log.Write("SQL Error with either Connection String:\n" + cn + " \nor Query:\n" + query);
throw;
}
}
(I catch any errors, log them, and then catch the error higher up the chain. Also, I did not include the ConnectionString() code for brevity. It just returns the requested connection string. That's all.)
This all works just fine, and with a single line of code, I'm ready to .Read() rows.
SqlDataReader rs = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees");
while (rs.Read()) {
// code
}
rs.Close();
I want to expand this and add a .ColumnReader() method that I want to chain to .DBReader() like this:
string empID = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees).ColumnReader("EmpID");
I attempted this by adding a .ColumnReader() method, but it ends up being a method of SQL_Data() class directly, not a member or extension of .DBReader(). I also tried adding the .ColumnReader() inside the .DBReader() (like a "closure"), but that didn't work either.
Can this be done?
This ended up working for me:
public static class SQLExtentions {
public static dynamic ColumnReader(this SqlDataReader rs, string colName) {
return rs[colName];
}
}
I will have to expand on it a bit to add some error checking, and perhaps return more than just the dynamic value - like return an object with the value and it's SQL data type. But Paul and Bagus' comments got me on the right track.

Is it bad practice to catch the "happy path" in an exception?

I have an SQL statement that checks to see if a value is in my database or not. I want to respond with the "happy path" if the value is not in the database.
I have found using DbDataReader (.NET) that if a SELECT query can't find the value it throws an exception - so my "happy path" ends up in the exception, not in the main try block.
I can always say "NOT IN" but I don't want to return all of the rows in the database that don't have the value - as this would return many thousands of results where as all I want is just a "no it is not here" type response.
public void wristbandScan(string barcode)
{
string query = "SELECT ticket FROM tickets WHERE
linked_barcode='" + barcode + "'";
ValidTicketEventArgs args = new ValidTicketEventArgs();
try
{
var queryResult = _dbRunner.queryThis(query);
args.Result = false;
args.Message = "WB already linked";
args.Barcode = barcode;
OnValidTicketEvent(args);
}
catch (Exception e)
{
this.updateWristband(barcode);
this.updateValid();
args.Result = true;
args.Message = "WB linked";
args.Barcode = barcode;
OnValidTicketEvent(args);
}
}
It feels wrong to me to catch the happy path in an error statement, but I do not want the lag associated with reading in all the rows with the NOT IN statement.
Is there a better way to do this or is this approach acceptable best practice?
Well, you don't have to fetch all the records to the client; let's extract a method for this. Assuming that you work with MS Sql:
public bool hasScanCode(string barcode) {
if (string.IsNullOrWhiteSpace(barcode))
return false;
//DONE: paramterize queries
string query =
#"SELECT ticket
FROM tickets
WHERE linked_barcode = #prm_BarCode";
using (var conn = new SqlConnection(connection_string_here)) {
conn.Open();
using (var q = new SqlCommand(conn, query)) {
//TODO: q.Parameters.Add is a better choice
q.Parameters.AddWithValue("#prm_BarCode", barcode.Trim());
using (var reader = q.ExecuteReader()) {
// we read (fetch) at most 1 record
// if empty cursor - no record with given barcode
return reader.Read();
}
}
}
}
then we can use it:
public void wristbandScan(string barcode) {
bool result = hasScanCode(barcode);
ValidTicketEventArgs args = new ValidTicketEventArgs() {
Result = result,
Message = result ? "WB linked" : "WB already linked",
Barcode = barcode,
};
OnValidTicketEvent(args);
}
Please, remember - exceptions are for exceptional situations. Exceptions are very slow (stack unrolling wants resources); they are not readable - catch, in fact, works as a notorious goto; they are dangerous - in your current code you catch too many exceptions: e.g. AccessViolationException if it's thrown somewhere within dbRunner.queryThis will be efficiently masked.
Create and call a StoredProcedure which can to handle the empty situation and return no rows instead of an exception.
Then handle the no rows scenario outside the try/catch.

Reading Null DateTime column from a Table in SqlServer

I am having an issue reading Calls from my Call Table. One of my columns is a DateTime column, and this column I have allowed to be null, if the call has not been closed. When I execute my C# code that is to fetch all calls from the Calls table, I get an error that the data is null, cannot read when no data is present. I thought If I had an If statement that checks if that column is null, that would solve the issue... but that doesn't work.
Here is the code where I am experiencing the problem:
public void GetAllCalls(Connection connectionObject)
{
try
{
if (memoryObject.GetCallCount() != 0)
{
memoryObject.DeleteAllCalls();
}
SqlCommand getCalls = new SqlCommand("sp_getAllCalls", connectionObject.getDatabaseConnection());
getCalls.CommandType = CommandType.StoredProcedure;
dataReader = getCalls.ExecuteReader();
if (dataReader.HasRows)
{
while (dataReader.Read())
{
if (dataReader.GetDateTime(8).Equals(DBNull.Value))
{
Call newCall = new Call(dataReader.GetString(0), dataReader.GetString(1), dataReader.GetString(2),
dataReader.GetString(3), dataReader.GetString(4), dataReader.GetString(5),
dataReader.GetString(6), dataReader.GetDateTime(7), Convert.ToDateTime(null),
dataReader.GetString(9), dataReader.GetString(10), dataReader.GetString(11),
dataReader.GetString(12));
memoryObject.AddCall(newCall);
}
else
{
Call newCall = new Call(dataReader.GetString(0), dataReader.GetString(1), dataReader.GetString(2),
dataReader.GetString(3), dataReader.GetString(4), dataReader.GetString(5),
dataReader.GetString(6), dataReader.GetDateTime(7), dataReader.GetDateTime(8),
dataReader.GetString(9), dataReader.GetString(10), dataReader.GetString(11),
dataReader.GetString(12));
memoryObject.AddCall(newCall);
}
}
dataReader.Close();
}
else
{
dataReader.Close();
functionsObject.displayMessage("No Calls Found", "No calls were found on the system");
}
}
catch (Exception error)
{
logNewError(error, "GetAllCalls", connectionObject);
}
}
The correct way of checking that is using the SqlDataReader.IsDBNull method
if (dataReader.IsDBNull(8))

Error inserting into database using entity framework

I am going to get mad. I have a so simple code but it doesn't work.
I have tested every thing and I can't understand the reason.
The code is like this:
FataDBEntities model = new FataDBEntities();
UserAccount nua = new UserAccount();
nua.username = username;
nua.pass = password;
nua.email = mailaddress;
nua.activated = false;
model.UserAccounts.Add(nua);
try
{
model.SaveChanges();
}
catch (Exception)
{
throw;
}
string activationCode;
try
{
activationCode = genActivationCodeAndMail(mailaddress, username);
}
catch (Exception)
{
throw;
}
ActivationCode ac = new ActivationCode();
ac.code = activationCode;
ac.expiration_date = DateTime.Now.AddDays(1);
nua = model.UserAccounts.Single(p => p.username == username);
ac.wichUser = nua.ID;
model.ActivationCodes.Add(ac);
try
{
model.SaveChanges();
}
catch (Exception)
{
throw;
}
and the model is like this :
The error is with second insertion ... the first one works fine.
Additional information: Unable to update the EntitySet 'ActivationCode' because it has a DefiningQuery and no element exists in the element to support the current operation.
Please help...
According to this SO article, this error can occur if the entity set is mapped from the database view, custom database query or if database table does not have primary key.
This has happened to me before, and I used that same article to fix the issue :)

Insert with Linq-to-SQL sometimes fails

I have a project that inserts personal information to a table and details into another table. But sometimes personal information cannot be recorded, however details are recorded. As below code part, firstly personal information are inserted, then details. But sometimes personal information doesn't get saved and userId returns 0, So details are saved. I don't know why it doesn't work. Any idea?
public int ConferenceIdyeGoreKisiBilgileriniKaydet(string orderId)
{
KisiselBilgilerBal kisiBilgileri = (KisiselBilgilerBal)Session["kisiselBilgilerSession"];
registrationCode = GenerateGeristrationCode();
string toplamMaliyet = Session["toplamOdeme"].ToString();
PersonalInformation.SavePersonalInformations(kisiBilgileri, registrationCode,conferenceName);
int userId = AuthorPaperDetaylari.AdVeSoyadaGoreIdGetir(kisiBilgileri.f_name, kisiBilgileri.l_name);
AuthorPaperDetaylari.SaveAuthorPaperDetails(authorPaperDetay, userId); // save details via userId.
return userId;
}
This method saves personal information.
public static void SavePersonalInformations(KisiselBilgilerBal kisiBilgileri,string registrationCode,string conferenceName)
{
try
{
string cs = ConfigurationManager.AppSettings["SiteSqlServer"];
DBDataContext db = new DBDataContext(cs);
DBpersonalInformation personalInfo = new DBpersonalInformation();
personalInfo.f_name = kisiBilgileri.f_name;
personalInfo.l_name = kisiBilgileri.l_name;
personalInfo.university_affiliation = kisiBilgileri.university_affiliation;
personalInfo.department_name = kisiBilgileri.department_name;
personalInfo.address1 = kisiBilgileri.address1;
personalInfo.address2 = kisiBilgileri.address2;
personalInfo.city = kisiBilgileri.city;
personalInfo.state = kisiBilgileri.state;
personalInfo.zipCode = kisiBilgileri.zipCode;
personalInfo.country = kisiBilgileri.country;
personalInfo.phone = kisiBilgileri.phone;
personalInfo.email = kisiBilgileri.email;
personalInfo.orderId = kisiBilgileri.orderId;
personalInfo.registrationCode = registrationCode;
personalInfo.date = DateTime.Now;
personalInfo.conferenceName = conferenceName;
db.DBpersonalInformations.InsertOnSubmit(personalInfo);
db.SubmitChanges();
}
catch (Exception)
{
}
}
This method saves details
public static void SaveAuthorPaperDetails(AuthorPaperDetailsBal authorPaperDetay, int userId)
{
try
{
string cs = ConfigurationManager.AppSettings["SiteSqlServer"];
DBWebDataContext db = new DBWebDataContext(cs);
DBAuthorPaperDetail authorPaperDetail = new DBAuthorPaperDetail();
authorPaperDetail.paper_title = authorPaperDetay.paperTitleDetails;
authorPaperDetail.conference_maker_id = authorPaperDetay.confMakerId;
authorPaperDetail.additional_paper_title = authorPaperDetay.additionalPprTtle;
authorPaperDetail.areYouMainAuthor = authorPaperDetay.mainAuthor;
authorPaperDetail.feeForFirstAuthorPaper = authorPaperDetay.registerFeeForFirstAuthor;
authorPaperDetail.feeForAdditionalPaper = authorPaperDetay.regFeeForAdditionalPape;
authorPaperDetail.feeForParticipCoAuthors = authorPaperDetay.regFeeForCoAuthors;
authorPaperDetail.userId = userId;
authorPaperDetail.firstCoAuthorName = authorPaperDetay.firstCoAuthor;
authorPaperDetail.secondCoAuthorName = authorPaperDetay.secondCoAutho;
authorPaperDetail.thirdCoAuthorName = authorPaperDetay.thirdCoAuthor;
authorPaperDetail.toplamOdeme = authorPaperDetay.toplamMaliyet;
db.DBAuthorPaperDetails.InsertOnSubmit(authorPaperDetail);
db.SubmitChanges();
}
catch (Exception)
{
}
}
I don't know why it doesnt work. Any idea?
...
catch (Exception)
{
}
Well, that explains pretty much everything... don't do this. Ever. The database layer is trying to tell you what the problem is, and you are sticking your fingers in your ears, hoping that'll make it go away. If I had to guess: maybe an occasional timeout due to being blocked by another SPID.
If you can't do anything useful or appropriate with an exception, just let it bubble to the caller. If it gets to the UI, tell the user about it (or just log the issue internally and tell the user "There was a problem").
Also, a LINQ-to-SQL data-context is IDisposable; you should have using statement around db.
In addition to Marc's answer... You are calling SubmitChanges twice. If you want atomic data storage, you should call it once. You can use relational properties to create an object graph, and submit the whole graph at once.
public void SaveParentAndChildren()
{
using (CustomDataContext myDC = new CustomDataContext())
{
Parent p = new Parent();
Child c = new Child();
p.Children.Add(c);
myDC.Parents.InsertOnSubmit(p); //whole graph is now tracked by this data context
myDC.SubmitChanges(); // whole graph is now saved to database
// or nothing saved if an exception occurred.
} //myDC.Dispose is called for you here whether exception occurred or not
}

Categories

Resources