ASP.NET MVC validate query from controller to ajax - mysql

Currently I have an insert query to my table and I don't have any idea how I can validate it, if the data is already exists in the table.
So for now I just used try-catch to handle the duplicate entry. I just wondering if I could return some text If value goes to catch then display it to my view as alert or something.
Here is my query from controller:
public ActionResult AddUser(int id, string name, string age)
{
string constr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (MySqlConnection con = new MySqlConnection(constr))
{
string sqlQuery = "INSERT INTO myTable (id, name, age) VALUES (#id, #name, #age)";
MySqlCommand cmd = new MySqlCommand(sqlQuery, con);
cmd.Parameters.AddWithValue("#id", id);
cmd.Parameters.AddWithValue("#name", name);
cmd.Parameters.AddWithValue("#age", age);
con.Open();
try {
cmd.ExecuteNonQuery();
con.Close();
return RedirectToAction("Index");
}
catch (Exception )
{
con.Close();
return this.Json("This Data already exist on table");
}
}
}
And this is my script to read my controller query:
function add(id, name, age) {
var result = confirm("Are you want to add " + name + " to list?");
if (result == true) {
$.ajax({
url: '/Home/AddUser',
type: 'POST',
data: {
'id': id,
'name': name,
'age': age,
},
success: function (data) {
alert('Data has been successfully added');
},
error: function (jqXhr, textStatus, errorThrown) {
alert(errorThrown);
}
});
}
}
So far I can successfully insert it to my table but if there is already exist and it's a duplicate it will do nothing so the user will not have a prompt if they already added it or already exist or not.
Any suggestions or comments. TIA.

Consider creating a constraint in your database table (unique constraint on that column(s)). That will prevent any duplicate records to be saved even if it passes your C# code which is talking to the database.
Add a check to see whether the record exist, just before executing the INSERT statement. You can write a SQL statement which checks before the insertion step. May be a stored procedure which has this SQL scripts which does this and you may call the stored procedure from your c# method.
There is no point in returning a redirect response if you are calling this from Ajax code. Consider returning a JSON structure which can tell the client side code whether the record was successfully inserted or duplicate found or code crashed.
Here is a quick and simple example, where I am calling the UserExist method, which checks whether there is a record with the specified name. If it returns false, I continue to execute my code where I will try to insert. If you are using a stored procedure, you can add the record exist check inside that as well.
[HttpPost]
public ActionResult AddUser(int id, string name, string age)
{
try
{
// your existing code to insert record to db
// Check record exist
if(UserExist(name))
{
return Json(new { status="failed", message = "Name exist"});
}
// name does not exist. Try to call the Insert code now.
return Json(new { status="success", message = "Successfully saved"});
}
catch (SqlException exs)
{
// Check exs to see whether this was caused by unique constraint violation
return Json(new { status="error", message = "User exist"});
}
catch (Exception ex)
{
// to do : log the exception
return Json(new { status="error", message = "Error in saving"});
}
}
private bool UserExist(string name)
{
// to do: check record exist in db
// You may use ExecuteScalar method if you use raw ADO.NET
// to do : return boolean value.
}
and in your success handler, check the status property of the response json and show the user the appropriate message
success:function(data)
{
if(data.status==="success")
{
alert("Saved successfully");
}
else if(data.status==="failed")
{
alert(data.message);
}
}
You can set the status property of your JSON object to failed when you are trying to insert duplicate record.
You can use specific exceptions for your exception handling part which can catch the exception when the unique key constraint is violated in the database level. Send a message to user in that catch block as well.

The first step you can do is creating a stored procedure which has output parameters to show insertion status, which must be different when duplicate data is found:
DELIMITER //
CREATE PROCEDURE AddUser(in #id int, #name varchar(50), #age int, out #status varchar(20))
AS
BEGIN
-- check if duplicate exists
IF (SELECT EXISTS (SELECT 1 FROM myTable WHERE name = #name))
BEGIN
-- duplicate exist, no insertion to table
SET #status = 'duplicate';
END
ELSE
BEGIN
INSERT INTO myTable (id, name, age) VALUES (#id, #name, #age)
SET #status = 'success';
END
END
END
//
DELIMITER ;
Then, use the stored procedure name inside MySqlCommand and use its output parameter to return the status string:
[HttpPost]
public ActionResult AddUser(int id, string name, string age)
{
string constr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (MySqlConnection con = new MySqlConnection(constr))
{
string sqlQuery = "AddUser";
MySqlCommand cmd = new MySqlCommand(sqlQuery, con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#id", id);
cmd.Parameters.AddWithValue("#name", name);
cmd.Parameters.AddWithValue("#age", age);
cmd.Parameters.Add("#status", MySqlDbType.VarChar).Direction = ParameterDirection.Output;
con.Open();
try
{
cmd.ExecuteNonQuery();
con.Close();
return Json(new { status = (string)cmd.Parameters["#status"].Value });
}
catch (MySqlException ex)
{
con.Close();
return Json(new { status = "error", message = ex.Message });
}
catch (Exception e)
{
con.Close();
return Json(new { status = "error", message = e.Message });
}
}
}
Then you can output messages depending on current status in AJAX callback:
$.ajax({
url: '/Home/AddUser',
type: 'POST',
data: {
'id': id,
'name': name,
'age': age,
},
success: function (data) {
if (data.status === "success")
{
alert("Data has been successfully added");
}
else if (data.status === "duplicate")
{
alert("This Data already exist on table");
}
else if (data.status === "error")
{
alert(data.message);
}
}
error: function (xhr, status, err) {
// error handling
}
}
If you don't want to check with SELECT query like above, consider altering the table by applying UNIQUE constraint and check against error code 1062 in MySqlException:
ALTER TABLE myTable ADD CONSTRAINT UNIQUE (name);

Related

DNN API Controller - A task was canceled Log

We have an JQuery Ajax call that will execute when a user is about to leave a page on a DNN module.
This error is being logged the whole time in the DNN logs.
How can I improve the error handling so that it doesn't log the whole time?
Here is the DNN log:
Here is the Front End Code on the Module :
$(window).on("beforeunload", function () {
ClearTempUserSessionWhenLeavePage();
});
function ClearTempUserSessionWhenLeavePage() {
if ($fromButtonEvent == false) {
var Url = $.fn.GetBaseURL() + 'DesktopModules/DNNCommon/API/Store/ClearTempUserSessionWhenLeavePage';
$.ajax({
url: Url,
type: 'GET',
async: true,
dataType: 'json',
success: function () {
},
error: function (x, y, z) {
}
}).promise().done(function () {
});
}
$fromButtonEvent = false;
}
We are inheriting the DNNApiController class on our DNNCommon class.
This is the C# method being called:
[AllowAnonymous]
[HttpGet]
public void ClearTempUserSessionWhenLeavePage()
{
if (SessionManager.GetSessionObject("NewCreatedWebUser") != null)
{
System.Web.HttpContext.Current.Session["DoNotRemoveSessionIfNotAuthenticated"] = false;
SessionManager.SetSessionObject("NewCreatedWebUser", null);
SessionManager.SetSessionObject("UserInfo", null);
SessionManager.SetSessionObject("NewCustomerCode", null);
}
}
I have attempted to add two different types of Try Catch clauses, but when I debug the code it won't hit the breakpoints and somehow it still logs the error in the DNN Admin logs. Is there perhaps a Try Catch in the DNNController class that is writing this error?
First attempt with Try Catch Clause with TaskCanceledException and TimeoutException:
[AllowAnonymous]
[HttpGet]
public void ClearTempUserSessionWhenLeavePage()
{
try
{
if (SessionManager.GetSessionObject("NewCreatedWebUser") != null)
{
System.Web.HttpContext.Current.Session["DoNotRemoveSessionIfNotAuthenticated"] = false;
SessionManager.SetSessionObject("NewCreatedWebUser", null);
SessionManager.SetSessionObject("UserInfo", null);
SessionManager.SetSessionObject("NewCustomerCode", null);
}
}
catch (Exception ex)
{
EventLogController logController = new EventLogController();
if (ex.InnerException is TimeoutException)
{
ex = ex.InnerException;
}
else if (ex is TaskCanceledException)
{
if ((ex as TaskCanceledException).CancellationToken == null || (ex as TaskCanceledException).CancellationToken.IsCancellationRequested == false)
{
ex = new TimeoutException("Timeout occurred");
logController.AddLog("Timout Occured - Clearing Temp User Session When Leave Page.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
}
}
logController.AddLog("Problem Clearing Temp User Session When Leave Page.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
}
}
Second attempt with a TaskCanceledException:
[AllowAnonymous]
[HttpGet]
public void ClearTempUserSessionWhenLeavePage()
{
try
{
if (SessionManager.GetSessionObject("NewCreatedWebUser") != null)
{
System.Web.HttpContext.Current.Session["DoNotRemoveSessionIfNotAuthenticated"] = false;
SessionManager.SetSessionObject("NewCreatedWebUser", null);
SessionManager.SetSessionObject("UserInfo", null);
SessionManager.SetSessionObject("NewCustomerCode", null);
}
}
catch (TaskCanceledException ex)
{
EventLogController logController = new EventLogController();
logController.AddLog("Task Cancelled Exception - Clearing Temp User Session When Leave Page.", ex.ToString(), EventLogController.EventLogType.ADMIN_ALERT);
}
}

Nullable Table Valued Parameter in Stored Procedure

I have this procedure
CREATE PROCEDURE dbo.spProcedure1
#intArray as dbo.intArray READONLY
AS
BEGIN
-- ...
END
which use user type as a parameter
CREATE TYPE dbo.IntArray AS TABLE (IntValue int NULL)
and I am calling the procedure from the C# ASP.NET MVC 4 project
// creating empty SQL #IntArray parameter
var emptyIntDataTable = new DataTable();
emptyIntDataTable.Columns.Add("IntValue");
// calling stored procedure
return Database.SqlQuery<int>(
#"spProcedure1 #p1",
new SqlParameter("p1", (object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();
// ToDataTable method which is returning null
public static DataTable ToDataTable<T>(this IList<T> data)
{
if (data == null)
return null;
... // code omitted because it is not working yet
}
the error which is throwed when calling stored procedure is
The table type parameter 'p1' must have a valid type name.
How to pass an empty table value?
Passing the list instead of datatable throw following error
var emptyIntDataTable = new List<int>;
No mapping exists from object type System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to a known managed provider native type.
In your code:
where it says:
return Database.SqlQuery<int>(
#"spProcedure1 #p1", new SqlParameter("p1",
(object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();
Change it to read:
return m.IntArray.Length > 0?
Database.SqlQuery<int>(#"spProcedure1 #p1",
new SqlParameter("p1",
(object)Utils.ToDataTable(m.IntArray))).ToList():
Database.SqlQuery<int>(#"spProcedure1")).ToList();
sample to show how to not pass table parameter
CREATE TYPE dbo.KeyIds]
AS TABLE(pkId int NOT NULL,
PRIMARY KEY CLUSTERED (pkId ASC)
WITH (IGNORE_DUP_KEY = OFF))
Go
-- ------------------------------
Create procedure testProc
#aIds dbo.keyIds readonly
as
Set NoCount On
if exists (select * from #aIds)
Select * from #aIds
else
Select 'No Aids passed in'
Go
-- ------------------------------
Exec dbo.testProc -- <--- Here I am NOT passing the #aids parameter
But, even though I am NOT passing the #aids parameter
it still works, and the subquery (select * from #aIds) still functions, and since it is an empty datatable the SP returns the empty message 'No Aids passed in'.
On the other hand, if you pass the parameter
Declare #MyIds dbo.keyIds
Insert #MyIds Values(1)
Insert #MyIds Values(2)
Insert #MyIds Values(3)
Insert #MyIds Values(4)
Insert #MyIds Values(5)
Exec dbo.testProc #MyIds -- <--- Here I AM passing the #aids parameter
it outputs the contents of the datatable parameter
C# code example...
public DataTable GetAccountTransactions(IEnumerable<int> accountIds)
{
const string procName = "FetchAccountTransactionData";
var acctIds = accountIds == null ?
new List<int>() : accountIds.ToList();
// -------------------------------------------------
var parms = DbParamList.Make();
// DbParamList is a List<IDbDataParameter>
// See here, ONLY ADD PARAMETER if list is NOT empty!
if (acctIds.Count > 0)
parms.AddSQLTableParm("aIds", acctIds);
try
{ // following constructs command obkect and calls SP
return Utilities.GetDataTable(schemaNm + "." +
procName, parms, copConn);
}
catch (SqlException dbX)
{
// Exception stuff
}
}
public class DbParamSortedList : SortedList<string,IDbDataParameter> { }
The alternative solution
prepare method for converting List<int> into dbo.IntArray type
public static DataTable IntArrayToDataTable(IEnumerable<int> ids)
{
if (ids == null)
return null;
DataTable table = new DataTable();
// datatable columns has to have same name as database type !
table.Columns.Add("IntValue", typeof(int));
foreach (int id in ids)
{
table.Rows.Add(id);
}
return table;
}
run sql stored procedure
var sqlParameters = new List<object>();
var parameter1 = Utils.IntArrayToDataTable(m.IntArray);
if (parameter1 != null)
sqlParameters.Add(new SqlParameter("intArray", parameter1)
// these variables are the key, without them it is not working
{
SqlDbType = SqlDbType.Structured,
TypeName = "dbo.IntArray"
});
else // parameter cannot be omitted !! even if all parameters are named !! otherwise parameter mismatch happens (in case of multiple parameters)
sqlParameters.Add(new SqlParameter("intArray", SqlDbType.Structured) { TypeName = "dbo.IntArray" });
var sqlQuery = "spProcedure1 #InArray";
return Database.SqlQuery<int>(sqlQuery, sqlParameters.ToArray()).ToList();

Unable to set return type in ajax call for json?

I am having small trouble in ajax call i.e,unable to set data type in ajax,For string and text data types i get ajax call as success,But when i returning list from servlrt ajax call fails,Please tell me how to fix it,For any help thanks in advance.
$
.ajax({
type: 'GET',
url : "attendancePercentage",
dataType: "json",
data : {"stdate": stdate, "endate": endate},
success : function(data, status) {
alert(status);
alert(data);
}
});
Servlet:
List<StudentMark> stList = new ArrayList<StudentMark>();
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test_schema", "root", "root");
String sql = "select * from attendance where attendance.date>=? and attendance.date<=?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, stDate);
pstmt.setString(2, enDate);
rs = pstmt.executeQuery();
while (rs.next()) {
StudentMark ge = new StudentMark();
ge.setStudentName(rs.getString(3));
ge.setDate(rs.getString(2));
ge.setStatus(rs.getString(4));
//System.out.println(("&&&&&..." + ge));
stList.add(ge);
//System.out.println("ge :" + ge.getStudentName()+","+ge.getDate()+","+ge.getStatus());
}
System.out.println("list>>"+stList);
System.out.println("list>>"+json);//[{"","",""}]
} catch (Exception e) {
System.err.println(e.getMessage());
}
I am getting output in s.o.p statement like above ,how to set data type for this json object.
You may override toString in StudentMarsk Class like
#Override
public String toString() {
return new Gson().toJson(this);
}

NodeJS mysql if null or empty

I have code, which should execute if the table is not in mysql or "NULL" or empty.
mysqlConnection.query('SELECT `something` FROM `here` WHERE `dog` = \'' +info+ '\'', function(err, row, fields) {
if(err) {
console.log('Error1');
return;
}
else if (!row.length) {
console.log('Error2');
return;
}
else if (row[0].something == 'NULL' || row[0].something == '') {
console.log('Error3');
return;
}
console.log('Works');
});
So the thing is, if "something" is not in mysql, console shows Error2, but if "something" is in mysql, but if its NULL, console shows Works, so whats the problem? Im checking if something is NULL, but it wont show Error3. If table is empty, it shows Error3. Thanks for help.
I would try something like this:
mysqlConnection.query('SELECT `something` FROM `here` WHERE `dog` = ?', [info] function(err, row, fields) {
if(err) {
return console.log('Error1');
} else if (!row.length) {
return console.log('Error2');
} else if (!row[0].something) {
return console.log('Error3');
}
console.log('Works');
});
It's using a "falsy" check for row[0].something which will return false if the value is undefined, null or an empty string. It also fixes the injection attack vector that t.niese mentioned.
I am aware that I am 5 years and 9 months late, but for those of you struggling with this,
here's a solution. The table's value when empty is not NULL. I was having a similar problem in which I wanted to reset AUTO_INCREMENT to 1 when the table is empty. To detect when it's empty, we have to see if it has any element with the index 0. If it has an element, it would return something like: RowDataPacket { // data }. If it doesn't, it would return undefined. See where I'm going with this? Just add a conditional to see if the result[0] is undefined or not. Want some code to better understand it? Sure! Here it is:
db.query("SELECT * FROM tablename", (err, result) => {
if (err) throw err;
else {
// If the first element does not exist
if (result[0] == undefined) {
db.query("yourquery", (err) => {
if (err) throw err;
});
} else {
res.send(result);
}
}
});
If you think in a scenario when you receive an Array<any> when you run a SQL like select name from employee there are three concerns you should have:
If your statement did return something
If the property you are looking for exist
If the content of the property is null and you are expecting a null
As these concerns will occur hundreds of time, I use the following approach (in TypeScript):
let ret: Array<any> = connection.query('select name from employee',...);
for (let r of ret) {
name = getValueColumn(r,'name','This will be thrown if content is null');
};
export function getValueColumn(obj: any, fieldName: string, messageIfNull: string = null): any {
fieldName = fieldName.toLocaleLowerCase();
if (!obj) {
throw new CustomError(errorCodes.connection.rowNull, 'Linha nula e sem campos');
} else if (!obj.hasOwnProperty(fieldName)) {
throw new CustomError(errorCodes.connection.fieldDoesNotExist, 'Campo não existe -> ' + fieldName);
} else {
if (!obj[fieldName]) {
if (messageIfNull) {
throw new CustomError(errorCodes.connection.fieldWithNullValue, messageIfNull + '\n' + fieldName +
' com valores nulos ou campo invalido\n' + obj);
};
return null;
};
return obj[fieldName];
};
};
If you were to check the results with just if (!ret) {...}, it would be always false as an empty array is not null. So you would have to check if(!ret[0]) {..}
So all three concerns are handled and you don't need to be worried every time you want to parse the query.

mysql statement WHERE as unknown

I have this sql statement:
selectAllUsersByCriteria = connection.prepareStatement(
"SELECT * FROM Users WHERE ? = ?" );
And the follow method running the statement:
public ArrayList<User> getUsersByCriteria(String 1criteria, String 2criteria)
{
ArrayList<User> results = null;
ResultSet resultSet = null;
try
{
selectAllUsersByCriteria.setString( 1, 1criteria);
selectAllUsersByCriteria.setString( 2, 2criteria);
// executeQuery returns ResultSet containing matching entries
resultSet = selectAllUsersByCriteria.executeQuery();
results = new ArrayList< User >();
while ( resultSet.next() )
{
results.add( new User( resultSet.getString( "userName" ),
resultSet.getString( "Password" ),
resultSet.getBoolean( "AdminRights" ),
resultSet.getDouble( "Balance" )
) );
} // end while
} // end try
catch ( SQLException sqlException )
{
sqlException.printStackTrace();
} // end catch
finally
{
try
{
resultSet.close();
} // end try
catch ( SQLException sqlException )
{
sqlException.printStackTrace();
close();
} // end catch
} // end finally
return results;
}
It doesn't work. I figure it is the first ? that is the issue. Isn't it possible to set the WHERE ? as a ?. Can it be solved in another way.
It is a table I want to show, but it should only be show the users follow it meet the two criteria.
You would need to inject the column name directly into the string. That would open you up to a SQL injection attack, so I'd recommend querying (and probably caching) the table's schema info (specifically found in INFORMATION_SCHEMA.COLUMNS).
This way you can make sure that your user-submitted column name matches one of the column names in your table before injecting it into the script by seeing if it's in your list of available columns.