passing array of parameters in ASP .NET Web API - asp.net

Creating a Web API through which array of id is passed and returns the result from the OracleDB.
public class SampleController : ApiController
{
public string Getdetails([FromUri] int []id)
{
string inconditons = "";
for (int i = 0; i < id.Length; i++)
{
if (i == id.Length - 1)
{
inconditons = inconditons + id[i];
}
else
{
inconditons = inconditons + id[i] + ", ";
}
}
using (var dbConn = new OracleConnection("DATA SOURCE=X;PASSWORD=03JD;PERSIST SECURITY INFO=True;USER ID=IN"))
{
dbConn.Open();
var strQuery = #"Select PRIO_CATEGORY_ID as PRIO,LANG_ID as LANG, REC_DATE as REC, REC_USER as RECUSER, DESCR,COL_DESCR AS COL,ROW_DESCR as DROW,ABBR from STCD_PRIO_CATEGORY_DESCR where REC_USER IN (" + inconditons + ");";
var queryResult = dbConn.Query<SamModel>(strQuery);
return JsonConvert.SerializeObject(queryResult);
}
}
}
And called the API as http://localhost:35432/api/Sample?id=1&id=83 it throws an error saying on var queryResult = dbConn.Query(strQuery);
But if I just give one parameter as below it works
var strQuery = #"Select PRIO_CATEGORY_ID as PRIO,LANG_ID as LANG, REC_DATE as REC, REC_USER as RECUSER, DESCR,COL_DESCR AS COL,ROW_DESCR as DROW,ABBR from STCD_PRIO_CATEGORY_DESCR where REC_USER =" +id ;
Can anyone please suggest me what is the issue here as a single parameter works. Thanks

Check to make sure your don't have any stray characters in your query.
As stated in the comments
Use parameterized queries, otherwise you're vulnerable to errors like
this and SQL Injection attacks.
So pass the id array into the parameterized query when executing.
Here is a refactored version of your example.
public class SampleController : ApiController {
public string Getdetails([FromUri] int[] id) {
var inconditions = id.Distinct().ToArray();
using (var dbConn = new OracleConnection("DATA SOURCE=h;PASSWORD=C;PERSIST SECURITY INFO=True;USER ID=T")) {
dbConn.Open();
var strQuery = "SELECT PRIO_CATEGORY_ID AS PRIO, LANG_ID AS LANG, REC_DATE AS REC, REC_USER AS RECUSER, DESCR, COL_DESCR AS COL, ROW_DESCR AS DROW, ABBR FROM STCD_PRIO_CATEGORY_DESCR WHERE REC_USER IN (:p)";
var queryResult = dbConn.Query<SamModel>(strQuery, new { p = inconditions });
return JsonConvert.SerializeObject(queryResult);
}
}
}

Your code looks fine to me. It might fail if your id array parameter is empty (but it will be a different error than what you see now). Put a breakpoint in your code and inspect the value of that.
Also for converting your array to string, You may use the String.Join method.
var ids = String.Join(",",id);
This will give the result like "1,3,5", assuming your int array has 3 items ,1,3 and 5
Now you can use this string variable in your query. Also you may consider passing this data as a parameter.
var q= " ... where REC_USER IN (#ids);" //Please fill the missing part of query
var result = con.Query<SomeModel>(q,new { ids });

Related

Optimize code for reduce Reflection impact on performance

I'm building a web API Rest on .Net Core and ADO.Net
I use this code to populate my model object from DataRow:
public IEnumerable<TEntity> Map(DataTable table)
{
List<TEntity> entities = new List<TEntity>();
var columnNames = table.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToList();
var properties = (typeof(TEntity)).GetProperties().ToList();
foreach (DataRow row in table.Rows)
{
TEntity entity = new TEntity();
foreach (var prop in properties)
{
PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
}
entities.Add(entity);
}
return entities;
}
And use this other code for create the necesary SQL Update command:
protected void base_UpdateCommand(IDbCommand myCommand, TEntity entity, string sWhere)
{
var properties = (typeof(TEntity)).GetProperties().ToList();
string sProps = "";
string sCommand = "";
foreach (var prop in properties)
{
bool bIgnore = prop.GetCustomAttributes(true).Any(a => a is KeyAttribute);
if (prop.Name.ToUpper() != sKeyField.ToUpper() && !bIgnore)
{
sProps = sProps + prop.Name + "=#" + prop.Name + ", ";
var p = myCommand.CreateParameter();
p.ParameterName = prop.Name;
if (prop.GetValue(entity) == null)
p.Value = DBNull.Value;
else
p.Value = prop.GetValue(entity);
myCommand.Parameters.Add(p);
}
}
sProps = sProps.Substring(0, sProps.Length - 2);
sCommand = "UPDATE [" + sTable + "] SET " + sProps;
sCommand = sCommand + " WHERE " + sWhere;
myCommand.CommandText = sCommand;
}
I know that reflection has impact on performance, so i'm looking for suggestion on how to improve this code.
Thanks!
You might consider using Dapper. It is a wrapper around ADO.NET so technically it can't be faster than ADO.NET, but in most cases it uses better coding practices comparing to custom code you use to manupulate data via ADO.NET, so potentially it could give the better performance results.

How to move Resultset curser via button click, and display data in textfields?

So, I'm creating a desktop banking application. It's nothing too serious, I'm just trying to practice and get better.
// Method I use to get a connection. I know this works.
public static Connection getConnection() throws SQLException {
String sCon = "jdbc:sqlite:banking.sqlite";
Connection connection = DriverManager.getConnection(sCon);
return connection;
}
.
..
...
.....Other code
Method I attempt to use to create and manipulate the data in the result set.
The problem I believe starts here. With this code, I am only able to return one row of the result set and only the last row.
public static Customers getAccounts(Customers c) {
String query = "select RowCount, Customers.Account_Number, "
+ "Customers.First_Name, Last_Name, Address, "
+ "Phone_Number, Accounts.Balance "
+ "from Customers "
+ "join Accounts ";
try (Connection connection = getConnection();
PreparedStatement ps = connection.prepareStatement(query);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String fName = rs.getString("First_Name");
String lName = rs.getString("Last_Name");
String address = rs.getString("Address");
String phone = rs.getString("Phone_Number");
String accNum = rs.getString("Account_Number");
String balance = rs.getString("Balance");
c.setFirstName(fName);
c.setLastName(lName);
c.setAddress(address);
c.setPhoneNumber(phone);
c.setAccountNumber(accNum);
c.setBalance(balance);
}
return c;
} catch (SQLException e) {
System.err.println(e);
}
return null;
}
}
Here is the method that is linked to the button I use to perform what I'm trying to attempt. It's part of the Controller class. I believe this method is also a part of the problem. Any ideas? Thank for all you guys do. This website is a real benefit to the community.
public void next() {
Customers c = new Customers();
DBInterface.getAccounts(c);
firstNameF2.setText(c.getFirstName());
lastNameF2.setText(c.getLastName());
addressF2.setText(c.getAddress());
phoneNumberF2.setText(c.getPhoneNumber());
accNumF.setText(c.getAccountNumber());
balanceF.setText(c.getBalance());
}
If you are expecting to get multiple Customers objects, then you definitely should return a list of that.
public static List<Customers> getAccounts() {
// Whatever you originally had...
final List<Customers> ret = new ArrayList<>();
while (rs.next()) {
String fName = rs.getString("First_Name");
String lName = rs.getString("Last_Name");
String address = rs.getString("Address");
String phone = rs.getString("Phone_Number");
String accNum = rs.getString("Account_Number");
String balance = rs.getString("Balance");
final Customers cust = new Customers();
cust.setFirstName(fName);
cust.setLastName(lName);
cust.setAddress(address);
cust.setPhoneNumber(phone);
cust.setAccountNumber(accNum);
cust.setBalance(balance);
ret.add(cust);
}
return ret;
}
I have removed the part about passing in the instance of Customers (which would have ended up as passing in List<Customers>. If you really need to do that, you can add back in and do all the necessary checks.

Select query with multiple parameterized in-clauses

I have a form with 2 dropdown lists and 3 checkbox sections that users select from to search the database. Since all of the tables are small, I'd like to use a single SELECT statement to return the results. I found an extension on mikesdotnetting that works great with a single IN clause
using System;
using System.Collections.Generic;
using WebMatrix.Data;
using System.Linq;
public static class DatabaseExtensions
{
public static IEnumerable<dynamic> QueryIn(this Database db, string commandText, string values)
{
if (string.IsNullOrEmpty(values))
throw new ArgumentException("Value cannot be null or an empty string", "values");
var temp = values.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var temp2 = values.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var temp3 = values.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parms = temp.Select((s, i) => "#" + i.ToString()).ToArray();
var parms2 = temp2.Select((s, i) => "#" + i.ToString()).ToArray();
var parms3 = temp3.Select((s, i) => "#" + i.ToString()).ToArray();
var inclause = string.Join(",", parms, parms2, parms3);
return db.Query(string.Format(commandText, inclause), temp, temp2, temp3);
}
public static int ExecuteIn(this Database db, string commandText, string values)
{
if (string.IsNullOrEmpty(values))
throw new ArgumentException("Value cannot be null or an empty string", "values");
var temp = values.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var parms = temp.Select((s, i) => "#" + i.ToString()).ToArray();
var inclause = string.Join(",", parms);
return db.Execute(string.Format(commandText, inclause), temp);
}
}
Here is the page code showing how I envision the query:
var gender = Request["genderSvd"];
var person = Request["chkPerson"];
var spec = Request["chkSpec"];
var county = Request["ctyLoc"];
var crim = Request["chkCrim"];
var db = Database.Open("HBDatabase");
var sql = "SELECT DISTINCT tblProgram.* FROM lnkPrgPersonSvd INNER JOIN tblProgram ON lnkPrgPersonSvd.prgId = tblProgram.prgId";
sql += "INNER JOIN lnkPrgSpecial ON tblProgram.prgId = lnkPrgSpecial.prgId INNER JOIN tblProgram.prgId = lnkPrgCriminal.prgId ";
sql += "WHERE (lnkPrgPersonSvd.personId IN ({0})) AND (lnkPrgSpecial.specId IN ({1})) AND tblProgram.prgGenderSvdId = #2 ";
sql += "AND tblProgram.prgCounty = #3 AND (lnkPrgCriminal.criminalId IN ({4}))";
var prgList = db.QueryIn(sql, person, spec, gender, county, crim);
That, obviously, doesn't work, because the extension cannot take that many arguments. I'm looking for a way to modify the class to allow for the additional parameters so that the query can run with just the one execution. This may not be possible.
Also, I did find several threads that dealt with handling a single parameterized in clause, but no real mention of multiples with variable lists.
I am not adept with stored procedures or writing my own classes. Any suggestions?

LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression

LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
public ActionResult PopulateFromDB(string sidx, string sord, int page, int rows)
{
var context = new NerdDinnerEntities();
var jsonData = new
{
total = 1,
page = page,
sord =sord,
records = context.Authors.Count(),
rows = (from n in context.Authors
select new
{ AuthorId = n.AuthorId ,
cell = new string[] { n.AuthorId.ToString(), n.Name.ToString(), n.Location.ToString() }
}).ToList()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
I am writting ToList or Toarray is it not working the error comes :
public ActionResult PopulateFromDB(string sidx, string sord, int page, int rows)
{
var context = new NerdDinnerEntities();
var jsonData = new
{
total = 1,
page = page,
sord =sord,
records = context.Authors.Count(),
rows = (from n in context.Authors
select new
{ AuthorId = n.AuthorId ,
cell = new string[] { n.AuthorId.ToString(), n.Name.ToString(), n.Location.ToString() }
}).ToList()
};
return Json(jsonData,JsonRequestBehavior.AllowGet);
}
From your code I assume your adding a custom property cell for display/storage purposes on the client-side. I would avoid this as your essentially coupling your API call to one particular client. I would suggest you simply return the data required & deal with it at the client-side specifically e.g.
Server
...
select new
{
Id = n.AuthorId,
Name = n.Name,
Location = n.Location
}).ToList();
...
Client
var response = ...
foreach (var author in response)
{
var cell = new string[] { author.Id.ToString(), author.Name, author.Location };
// do something with cell
}
You should try SqlFunctions.StringConvert to convert this, There is no overload for int so you should cast your number to a double or a decimal.
public ActionResult PopulateFromDB(string sidx, string sord, int page, int rows)
{
var context = new NerdDinnerEntities();
var jsonData = new
{
total = 1,
page = page,
sord =sord,
records = context.Authors.Count(),
rows = (from n in context.Authors
select new
{ AuthorId = n.AuthorId ,
cell = new string[] { SqlFunctions.StringConvert((double)n.AuthorId), n.Name, n.Location }
}).ToList()
};
return Json(jsonData,JsonRequestBehavior.AllowGet);
}
You are not using LinqToSql Classes, if you were using that your code should work, but as you mention that you are using LinqToEntity then You should use SqlFunctions.StringConvert to convert to string.

How use custom class in WebMatrix v2?

I do first steps in the ASP.NET and I've a issue with classes. I would like to create a new custom class to support sessions and collect information about the number of users on my website.
I've created a class in file MySession.cs, which has been put in the dir called "App_data" by WebMatrix.
When I try to use this class in .cshtml file, it throws me information it couldn't found that class.
I found in the Web, the class should be placed in App_Code, so I've done it. However, in this moment it shows me an error that classes like "Request" couldn't been found.
How to use custom class in WebMatrix?
My c# code from .cshtml file looks like:
#{
MySession session = new MySession(60);
session.start();
var db = Database.Open("studia");
var data = db.Query("SELECT COUNT(*) as total FROM sessions");
}
and class file looks like:
using System;
using System.Collections.Generic;
using System.Web;
using System.Security.Cryptography;
using System.Data;
using System.Data.SqlClient;
/// <summary>
/// Summary description for sesss
/// </summary>
public class MySession
{
private String _session_id;
private int _session_time;
public MySession(int session_time = 60)
{
_session_time = session_time;
using (MD5 md5Hash = MD5.Create())
{
_session_id = (Request.Cookies["session_id"] != null) ? Server.HtmlEncode(Request.Cookies["sesion_id"].Value) : GetMd5Hash(md5Hash, DateTime.Now);
}
cleanup();
}
public bool isLogged()
{
if (Request.Cookies["session_id"] != null)
{
return true;
}
return false;
}
public void start(string username)
{
DateTime now = DateTime.Now;
var db = Database.Open("studia");
if (isLogged())
{
db.Query("UPDATE sessions SET start_time = " + now + " WHERE session_id = " + _session_id);
}
else
{
db.Query("INSERT INTO sessions (id, start_time, username) VALUES ('" + _session_id + "', '" + now + "', '" + username + "'");
}
HttpCookie session_cookie = new HttpCookie("session_id");
session_cookie.Value = DateTime.Now;
session_cookie.Expires = DateTime.Now.AddSeconds(_session_time);
Response.Cookies.Add(aCookie);
}
public void cleanup()
{
var db = Database.Open("studia");
db.Query("DELETE FROM sessions WHERE start_time < " + (DateTime.Now - _session_time));
}
static string GetMd5Hash(MD5 md5Hash, string input)
{
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
// Verify a hash against a string.
static bool VerifyMd5Hash(MD5 md5Hash, string input, string hash)
{
// Hash the input.
string hashOfInput = GetMd5Hash(md5Hash, input);
// Create a StringComparer an compare the hashes.
StringComparer comparer = StringComparer.OrdinalIgnoreCase;
if (0 == comparer.Compare(hashOfInput, hash))
{
return true;
}
else
{
return false;
}
}
}
If you want to refer to the Request object in a class (as opposed to from within a page file) you need to use HttpContext.Current e.g.
public bool isLogged()
{
return HttpContext.Current.Request.Cookies["session_id"] != null;
}

Resources