Get value of first column without knowing name - c#

Similar to Column Number rather than Column Name but I believe it's different because that was for unnamed columns, whereas my columns are named, I just don't know the names ahead of time.
First, some background. I'm creating a method that takes a query and its arguments as parameters, then runs the query (using Dapper) and checks if the first row, first column value is a 1 or a 0. It's intended to work as a generic validator for databases.
For example, the user could enter a query that returns a 1 in the only row's only column if some value exists in some table, and a 0 otherwise. The use of this method is intentionally very broad and most of the effort is put on the user writing the query (I should also clarify that the "user" in this case is always another developer and the use is done within code calling this method, not on, say, form input).
I want to query my database and get the value of the first row, first column without knowing the name of the column ahead of time. Ideally (though I'm not sure if this is possible or not within the Dapper call), I'd also like to require that there only be one row and one column in the result. When I was searching around, I based my initial solution on this post's first answer, but found that for that to work I needed to know the name of the column:
var dict = connection.Query(query, parameters).ToDictionary(row => (string)row.VALUE);
if(dict.ElementAt(0).Value != 1){
//do stuff
}
I thought of just adding a requirement that the user add an alias that names the first column some constant value (in this case, VALUE), but I'd prefer not to put the burden on my user if at all possible.
Ultimately my core issue is: how can I get the value of a named column in a DB using Dapper without knowing the name of the column?
Does anybody have any ideas?

I've done this successfully by just using int as the type in the generic Query method as such:
int value = conn.Query<int>(sql,
commandType: CommandType.StoredProcedure).FirstOrDefault();
Notice the use of the FirstOrDefault method. So you get 0 or whatever the value is from the DB.

The easiest thing to do would be to make sure the SQL command ALWAYS returns the same column name, by way of an alias. Then your C# code can just use that alias.

Using Tortuga Chain
var result = DataSource.Sql(query, parameters).ToInt32().Execute();
I'd also like to require that there only be one row and one column in the result
By default, Chain uses DbCommand.ExecuteScalar. This returns the first column of the first row, ignoring everything else. To add that restriction I would do this:
var result = DataSource.Sql(query, parameters).ToInt32List().Execute().Single;
Another option is to change how you generate the SQL. For example,
var filterObject = new {ColumnA = 1, ColumnB = "X", ColumnC = 17};
var result = DataSource.From(tableName, filterObject).ToInt32(columnName).Execute();
This has the advantage of being immune to SQL inject attacks (which is probably a concern for you) and ensures that you SQL only returns one column before it is executed.

Related

Reading data from reader in C# with Sql Server

In C#, which one is more efficient way of reading reader object, through integer indexes or through named indexes ?
ad.Name = reader.GetString(0);
OR
ad.Name = reader["Name"].ToString();
The name overload needs to find the index first.
MSDN
a case-sensitive lookup is performed first. If it fails, a second
case-insensitive search is made (a case-insensitive comparison is done
using the database collation). Unexpected results can occur when
comparisons are affected by culture-specific casing rules. For
example, in Turkish, the following example yields the wrong results
because the file system in Turkish does not use linguistic casing
rules for the letter 'i' in "file".
From Getordinal (which is used therefore):
Because ordinal-based lookups are more efficient than named lookups,
it is inefficient to call GetOrdinal within a loop. Save time by
calling GetOrdinal once and assigning the results to an integer
variable for use within the loop.
so in a loop it might be more efficient to lookup the ordinal index once and reuse that in the loop body.
However, the name-lookup is backed by a class that is using a HashTable which is very efficient.
reader.GetString(index);
This will get the row value at that column index as string, The second solution is more ideal because it allows you to get the value at that index in your own prefered type.
Example:-
String name = reader["Name"].ToString();
int age = (int) reader["Age"]
ad.Name = reader["Name"].ToString();
This is most efficient way.
Because although you change database table structure afterwords, there will be no effect on this code since you have directly mentioned column name.
But with column index, it will change when you add any column to table before this column.

ExecuteReader.HasRows vs ExecuteScalar() is DBNull

In an area of my site, I need to control access to a specific set of users.
This is done by checking the user's ID against a table on a SQL server database. If the ID exists then they are granted access:
SELECT 1 FROM admin WHERE userID = #userID
I notice that there are a couple of ways in which I can check for the presence of a row within the database and was wondering if there was any benefit of using either one, or if there was a standard.
The first is by checking for the existence of rows in the SqlDataReader:
if (!SqlCommand.ExecuteReader().HasRows)
{
//redirect
}
The second is to check if the returned value is DBNull using ExecuteScalar():
if (SqlCommand.ExecuteScalar() is DBNull)
{
//redirect
}
Which should I use? Is there a better way? Does it really matter?
The second option because you have less overhead.
However please note
ExecuteScalar returns an object that is
The first column of the first row in the result set, or a null
reference (Nothing in Visual Basic) if the result set is empty
Your query may not return anything, so it is better to check for null instead of DBNull
if (SqlCommand.ExecuteScalar() == null)
{
//redirect
}
Both are same in terms of the performance.
ExecuteScalar only returns the first value from the first row of the dataset. Internal it is treated just like ExecuteReader(), a DataReader is opened, the value is picked and the DataReader gets destroyed afterwards.I also always wondered about that behavior, but it has one advantage: It takes place within the Framework...and you can't compete with the Framework in manners of speed.
Following are the difference between those two:
ExecuteReader():
1.will work with Action and Non-Action Queries (Select)
2.Returns the collection of rows selected by the Query.
3.Return type is DataReader.
4.Return value is compulsory and should be assigned to an another object DataReader.
ExecuteScalar():
1.will work with Non-Action Queries that contain aggregate functions.
2.Return the first row and first column value of the query result.
3.Return type is object.
4.Return value is compulsory and should be assigned to a variable of required type.
taken from
http://nareshkamuni.blogspot.in/2012/05/what-is-difference-between.html
in your case it will not affect much performance so either of them you can use .
ExecuteScalar is typically used when your query returns a single value. If it returns more, then the result is the first column of the first row. An example might be SELECT ##IDENTITY AS 'Identity'.
ExecuteReader is used for any result set with multiple rows/columns (e.g., SELECT col1, col2 from sometable).
SOURCE

How to get Indentity value back after insert

Given the following code (which is mostly irrelevant except for the last two lines), what would your method be to get the value of the identity field for the new record that was just created? Would you make a second call to the database to retrieve it based on the primary key of the object (which could be problematic if there's not one), or based on the last inserted record (which could be problematic with multithreaded apps) or is there maybe a more clever way to get the new value back at the same time you are making the insert?
Seems like there should be a way to get an Identity back based on the insert operation that was just made rather than having to query for it based on other means.
public void Insert(O obj)
{
var sqlCmd = new SqlCommand() { Connection = con.Conn };
var sqlParams = new SqlParameters(sqlCmd.Parameters, obj);
var props = obj.Properties.Where(o => !o.IsIdentity);
InsertQuery qry = new InsertQuery(this.TableAlias);
qry.FieldValuePairs = props.Select(o => new SqlValuePair(o.Alias, sqlParams.Add(o))).ToList();
sqlCmd.CommandText = qry.ToString();
sqlCmd.ExecuteNonQuery();
}
EDIT: While this question isn't a duplicate in the strictest manner, it's almost identical to this one which has some really good answers: Best way to get identity of inserted row?
It strongly depends on your database server. For example for Microsoft SQL Server you can get the value of the ##IDENTITY variable, that contains the last identity value assigned.
To prevent race conditions you must keep the insert query and the variable read inside a transaction.
Another solution could be to create a stored procedure for every type of insert you have to do and make it return the identity value and accept the insert arguments.
Otherwise, inside a transaction you can implement whatever ID assignment logic you want and be preserved from concurrency problems.
Afaik there is not finished way.
I solved by using client generated ids (guid) so that my method generated the id and returns it to the caller.
Perhaps you can analyse some SqlServer systables in order to see what has last changed. But you would get concurrency issues (What if someone else inserts a very similar record).
So I would recommend a strategy change and generate the id's on the clients
You can take a look at : this link.
I may add that to avoid the fact that multiple rows can exist, you can use "Transactions", make the Insert and the select methods in the same transaction.
Good luck.
The proper approach is to learn sql.
You can do a SQL command followed by a SELECT in one run, so you can go in and return the assigned identity.
See

strongly typed dataset and custom executescalr queries returning values

I have a strongly typed dataset, i must return some scalar values : the sum of values of column, the count of records with a specified column value and so on.
I' have added some custom queries to the dataset of the kind : select sum(mycolumn) as itsname from thetable [where anothercolumn = :myparameter] (last part is optional and i'm using oracle).
I've found that some queries return generics (i.e decimal? ) while other return object. I haven't found a rule for it, some get parameters others don't.
Does anybody knows why i get this different behaviour ?
Now i'm handling every query as if it where returning object, but i'd like to know if i'm doing somenthig wrong or what's the reason of this annoying behaviour
The type of value that is computed in database query will depend upon the database (it will decide that based upon type of operands and operation). For example, if the database computes the result as say currency then corresponding .NET value will be Decimal and so on. You can use database casting/type conversion operation in the query itself to ensure particular return type for the expression.

Query filter design for string field

A field in my table can have arbitrary strings. On the UI, there is a drop down having options like
All, Value1, Value2
And the results were filtered by the selected option value. So far this is easy and adding new filters to the UI is not a problem. Needs no changes in my stored procedure. Now I want to have an "Others" option here as well, which will return rows not having the column value as Value1 or Value2.
Apparently this will require a "not in" operator in my query, and will make maintenance difficult, as the list of values is likely to change
Any suggestions, design tips?
If your options table could have an additional column called IsOther then your query could simply be WHERE IsOther = 1 instead of using a NOT IN.
Make a separate stored procedure for others case and call it when the selected option is others. Pass the contents of the list as input to the procedure: by this way, you need to update the list only at one place.

Categories

Resources