LINQ to SQL custom query builder - c#

It was easy to build a custom query like this with ADO.NET:
SqlCommand.CommandText = "SELECT Column" + variable1 + ", Column" + Variable2 + " FROM TABLE";
Is that able to do so in LINQ to SQL?
Thanks

No there is no common way to build a dynamic query.
A method has to have a known, specific return type. That type can be
System.Object but then you have to use a lot of ugly reflection code
to actually get the members. And in this case you'd also have to use a
lot of ugly reflection expression tree code to generate the return
value.
If you're trying to dynamically generate the columns on the UI side -
stop doing that. Define the columns at design time, then simply
show/hide the columns you actually need/want the user to see. Have
your query return all of the columns that might be visible.
Unless you're noticing a serious performance problem selecting all of
the data columns (in which case, you probably have non-covering index
issues at the database level) then you will be far better off with
this approach. It's perfectly fine to generate predicates and sort
orders dynamically but you really don't want to do this with the
output list.
More about this

Yes You can do something like that with Dynamic query with Linq.
This is an example that you can build a custom query with Dynamic query with Linq:
string strWhere = string.Empty;
string strOrderBy = string.Empty;
if (!string.IsNullOrEmpty(txtAddress.Text))
strWhere = "Address.StartsWith(\"" + txtAddress.Text + "\")";
if (!string.IsNullOrEmpty(txtEmpId.Text))
{
if(!string.IsNullOrEmpty(strWhere ))
strWhere = " And ";
strWhere = "Id = " + txtEmpId.Text;
}
if (!string.IsNullOrEmpty(txtDesc.Text))
{
if (!string.IsNullOrEmpty(strWhere))
strWhere = " And ";
strWhere = "Desc.StartsWith(\"" + txtDesc.Text + "\")";
}
if (!string.IsNullOrEmpty(txtName.Text))
{
if (!string.IsNullOrEmpty(strWhere))
strWhere = " And ";
strWhere = "Name.StartsWith(\"" + txtName.Text + "\")";
}
EmployeeDataContext edb = new EmployeeDataContext();
var emp = edb.Employees.Where(strWhere);
grdEmployee.DataSource = emp.ToList();
grdEmployee.DataBind();
For more information you can check this page.

Related

Creating reusable SQL commands in c# programing

I have following query that works.
string sqlCommandText = "SELECT * FROM Admin_T where AdminID =
'" + textBox.Text + "'";
It is a fix command and I cannot use it with user given Table names and Column names at run time.
What I am actually trying to make is command like
string sqlCommandText = "SELECT * FROM Admin_T where
'" + UserGivenColumnName + "' = '" + conditionTB.Text + "'";
"UserGivenColumnName" can be any column that is part of that specific table.
Trying to create flexibility so that same command can be used under different circumstances.
SqlCommand and none of related classes used by ADO.NET does not support such a functionality as far as I know.
Of course your should never build your sql queries with string concatenation. You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
But prepared statements only for values, not column names or table names. If you really wanna put your input string to your column name, create a whitelist and use it as a validation before you put it in your query.
http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables-culture/
I think an Object-Relational Mapper (ORM) is perhaps the droid you are looking for. Entity Framework might be a good place to start.
Please also do take the time to understand what SQL injection is, as the other users have also prompted you to.
It is not returning anything as it is just comparing two strings
With the 'UserGivenColumnName' it is a string comparison
And those two strings are not equal
You can do it (column) by just not including the '
But it is still a bad idea
SQLinjection is a very real and very bad thing
string sqlCommandText =
"SELECT * FROM Admin_T where " + UserGivenColumnName + " = '" + conditionTB.Text + "'";
or
string sqlCommandText =
"SELECT * FROM Admin_T where [" + UserGivenColumnName + "] = '" + conditionTB.Text + "'";

Trickly null validation for SQL Query

"AND TT.[_TYPE] = CASE WHEN " + String.IsNullOrEmpty(lstTypeSearch.SelectedItem) ?
DBNull.Value} +
" IS NULL THEN TT.[_TYPE] ELSE " + lstTypeSearch.SelectedItem + " END ";
Above is a pseudo aspect of my query.
I need to do a validation of listbox item for null and then set as DBNull.Value, which is to be passed into CASE WHEN within SQL Query.
Any better way to achieve this? I am getting tons of String to Bool, Null to String conversion errors...
Further, is there anyway to pass DBNull.Value as a Parameter across data access layer?
EDIT: original query is in a Static class.
public static readonly string SqlGetItemsBy_Number_Capacity_Type =
"SELECT TT.[_NUMBER], " +
"TT.[CAPACITY], " +
"TT.[_TYPE], " +
"TS.[SESSIONE] " +
"FROM [ITEMS] AS TT, //some code
"WHERE //some code
"AND TT.[_TYPE] = CASE WHEN #Type IS NULL THEN TT.[_TYPE] ELSE #Type END";
If I run the same query in SQL Server, it works fine.
SELECT //some code
AND [_TYPE] = CASE WHEN NULL IS NULL THEN [_TYPE] ELSE #TYPE END
Finally: I decided to go with SQL Append and following to validate/set parameter.
string paramAppend;
var bld = lstTypeSearch.SelectedItem;
if (bld != null)
{
paramAppend = "AND TT.[_TYPE] = " + lstTypeSearch.SelectedItem.ToString();
}
else
paramAppend = "";
It looks like lstTypeSearch here represents a column name (hence parameterization: not an option), so the first thing I'd say is: make sure you white-list that. Rather than trying to do everything in one go, separate the two cases:
if(string.IsNullOrEmpty(lstTypeSearch.SelectedItem))
{
// nothing to check?
}
else
{
CheckValidColumn(lstTypeSearch.SelectedItem); // throws if white-list fails
sql.Append(" AND TT.[_TYPE] = [") // should probably add table alias
.Append(lstTypeSearch.SelectedItem)
.Append("]");
}
If I have misunderstood, and this isn't a column, then just parameterize:
if(string.IsNullOrEmpty(lstTypeSearch.SelectedItem))
{
// no restriction?
}
else
{
sql.Append(" AND TT.[_TYPE] = #type");
cmd.Parameters.AddWithValue("type", lstTypeSearch.SelectedItem);
}
// ...
cmd.CommandText = sql.ToString();
First, you should work with parameters, unless want to be exposed to potential sql injection threats.
Second, if you are building the sql dynamically in the code, a better approach would be to add the sql condition only if lstTypeSearch.SelectedItem is not an empty string or null. something like that:
sSql = "your sql query";
if(!String.IsNullOrEmpty(lstTypeSearch.SelectedItem)) {
sSql += "TT.[_TYPE] = '" + lstTypeSearch.SelectedItem + "'";
}
sSql += ";"
btw, what if the SelectedItem has a string that only contains white spaces? consider replacing the String.IsNullOrEmpty to String.IsNullOrWhiteSpace.
With the edit, you should be able to replace the last line with:
AND (#Type IS NULL OR TT.[_TYPE] = #Type)
far clearer, although frankly it doesn't make for great query cache plan usage or optimization; it would still, IMO, be better to just compose the correct SQL.
As for passing in the value:
object value = string.IsNullOrEmpty(lstTypeSearch.SelectedItem)
? (object)DBNull.Value : (object)lstTypeSearch.SelectedItem;
// ...
cmd.Parameters.AddWithValue("Type", value);
If null then feed that into the case when within SQL, so it will return all the data.
I'm interpreting this to mean that if there's no value specified then return all the rows, otherwise filter by that value.
I'm assuming lstTypeSearch.SelectedItem is your value and it's a string. If not, cast it or dig out the string value.
command.CommandText =
"SELECT * FROM TT WHERE #SelectedItem IS NULL OR TT.[_TYPE] = #SelectedItem";
var selectedItem = (string) lstTypeSearch.SelectedItem;
command.Parameters.AddWithValue("#SelectedItem",
String.IsNullOrEmpty(selectedItem) ? (object) DBNull.Value : selectedItem);
using (var dataReader = command.ExecuteReader())
{
...
}

XML to database and vice-versa

I can insert my XML easily into my database table, but i follow this exhausting manner as seen in my down code using LINK, which is perfectly tested. But, I wonder if I can find a way to read all my XML descendants elements of "Level" node, using iteration of all child tagnames, because when I make any change to my XML file, I would have to change my LINK code once again,and usually i'll face some errors when i use this exhausting manner. Please help improve this code:
try
{
XElement d = XElement.Parse(richTextBox1.Text.ToString());
var people = (from Level in d.Descendants("Level")
select new
{
ID = Convert.ToInt32(Level.Element("ID").Value),
Day1 = Level.Element("Day1").Value,
Day2 = Level.Element("Day2").Value,
Day3 = Level.Element("Day3").Value,
Day4 = Level.Element("Day4").Value,
Day5 = Level.Element("Day5").Value,
Day6 = Level.Element("Day6").Value,
Day7 = Level.Element("Day7").Value
}).ToList();
foreach (var item in people)
{
//Insert and Update
datacommand1.CommandText = "Insert Into MyTable(ID,Day1,Day2,Day3,Day4,Day5,Day6,Day7) values(" + item.ID + "," + "','" + item.Day1 + "','" + item.Day2 + "','" + item.Day3 + "','" + item.Day4 + "','" + item.Day5 + "','" + item.Day6 + "','" + item.Day7 + "')";
datacommand1.ExecuteNonQuery();
}
}
my XML file seems like that:
<level>
<id> 101 </id>
<Day1> task 1</Day1>
<Day2> task 2</Day2>
<Day3> task 3</Day3>
<Day4> task 4</Day4>
<Day5> task 5</Day5>
<Day6> task 6</Day6>
<Day7> task7 </Day7>
</level>
Instead of declaring one variable for each tag in the file, you may want to try to iterate over all them, extracting the name and the value for each one and composing the SQL statement at runtime from those, no matter what they are. Try something like this:
IEnumerable<XElement> items = d.Descendants("level").Elements();
string names = string.Empty;
string values = string.Empty;
foreach (XElement item in items)
{
names += item.Name + ",";
values += "#" + item.Name + ",";
IDbDataParameter parameter = datacommand1.CreateParameter();
parameter.ParameterName = "#" + item.Name;
parameter.DbType = DbType.String;
parameter.Value = item.Value;
datacommand1.Parameters.Add(parameter);
}
datacommand1.CommandText = "INSERT INTO MyTable (" + names.Substring(names.Length - 1) + ") VALUES (" + values.Substring(values.Length - 1) + ");";
datacommand1.ExecuteNonQuery();
This builds the command on the fly, based on the XML structure, and fills its parameters with the data there. But doing so relies on the fact that the table structure will be exactly the same as in the file, and still needs manual schema updating when the structure changes, but as long as they're in sync it should be fine.
EDIT
About datatypes, there are 2 choices I can think of. Leave as it is, sending everything as strings no matter what, and rely on the DB engine to parse, validate and turn those into numbers (depending on what DB you're using, that may be possible or not, but I guess it's not rare to see). Or modify the code to separate the special fields apart from the loop (and excluding from it) and add those one by one, specifying their types accordingly. This is easy to do if the numeric columns are fixed, like the ID, and everything else is text.

RowFilter with special characters

I have a table named myMainTable and I want to operate a .RowFilter on my DataView. For the moment my code is as such (the value used for the RowFilter is an example value):
var mainDataView = maindataDataSet.Tables["myMainTable"].DefaultView;
mainDataView .RowFilter = "LastName= '" + "Gemma O'Neil" + "'";
var mainDataTable = mainDataView.ToTable();
the problem I have of course is that it thinks (because of the ' in O'Neil) that the value I am looking for ends at "Gemma O".
How can I have it successfully apply the RowFilter for the full value "Gemma O'Neil" ?
For circumstances such as this, you would simply need to escape the ' character; which for RowFilter is simply another ':
string surname = "Gemma O'Neil";
mainDataView .RowFilter = "LastName= '" + surname.Replace("'", "''") + "'";
(or, neater:
string surname = "Gemma O'Neil";
mainDataView.RowFilter =
String.Format("LastName='{0}', surname.Replace("'", "''"));
)
Incidentally, this is a very similar problem to that faced by people trying to avoid SQL Injection attacks. If this is a new development then I'd recommend investigating Entity Framework and LINQ, which will handle this for you automatically.

Best way to build a query with condition on code-behind?

How to make this code properly? I am not satisfied with this code, I'm lost.
I give you a simple example but the query is more complexe.
Thanks in advance.
string aValue;
string queryA;
string queryB;
string finalQuery;
string queryA = #"SELECT column1 FROM table1 WHERE column1=";
queryA += aValue;
string queryB = #"SELECT column1, column2,"
if (aValue == "all"){
queryB += #"column3";
}
queryB += #"FROM table1 WHERE column1=";
queryB += #"'" +aValue+ "'";
private void exportExcel(){
// change the value with a dropdownlist
if (ddlType.selectedIndex(1))
aValue = "typeA";
else if(ddlType.selectedIndex(2))
aValue = "typeB";
else
aValue = "all";
// select the query
if (aValue == "typeA")
finalQuery = queryA;
else if (aValue == "typeB")
finalQuery = queryB;
ExecQUery(finalQuery);
}
In both Java and C# (and pretty much any other platform) you should definitely not include the values directly in the SQL. That's opening up the way to SQL injection attacks, and also makes dealing with formatting for dates, times and numbers tricky.
Instead, you should use parameterized SQL, specifying the values in the parameters. How you do that varies between Java and C#, but the principle is the same.
Another approach on both platforms is to use an ORM of some description rather than building queries by hand. For example, in .NET you might want to use a LINQ provider of some description, and in Java you might want to use something like Hibernate. Either way you get to express your queries at a higher level of abstraction than just the raw SQL.
It's hard to give much more concrete advice without knowing what platform you're really using (or database) and without a real query to look at.
One small change you can do is set value attribute of dropdownlist to typeA,TypeB, etc.. and get rid of the initial if conditions and variables.
eg:
if(ddlType.selectedValue.toString()=="typeA")
finalQuery = queryA;
if(ddlType.selectedValue.toString()=="typeB")
finalQuery = queryB;
I usually load it from a resource file. This gives you some freedom to change the queries (this in case you don't need to generate it dynamically with if blocks). In source code I use formatting ending my line with a comment line in order to avoid my IDE to concatenate or put it all in one like like:
String sql = "select " + //
" * " + //
"from "+ //
" employee " + //
"where " + //
" salary > :minSal " + //
" and startDate > :minStartDate";
And in case of conditional part I just add it with a if block. But for where statements I just add one default "1=1" in order to proceed with additional limitations, so if there is no additional limitations the query will still be valid. Suppose both where statements in the SQL bellow were added conditionally:
String sql = "select " + //
" * " + //
"from "+ //
" employee " + //
"where 1 = 1 ";
Until here you have your base SQL, valid, that means if not condition is added it will still be valid.
Suppose you will add salary limitation just in case if it is informed:
if (salary != null) {
sql += "and salary > :minSalary";
parameters.put("minSalary", salary);
}
As you can see in the same condition I add a new expression to my SQL and a parameter to a map that will be used later in execute to set the parameters to the query, that avoids you to create a second if statement just to set this parameter.
Another approach that you could take is build the entire SQL and before the execution ask for the prepared statement which parameters it needs as input and provide them. In java you can do it with:
http://download.oracle.com/javase/1.4.2/docs/api/java/sql/PreparedStatement.html#getParameterMetaData%28%29
I know that is not the case but if ORM is used, is common to have Builders for queries and this turns this task much easier, for example in Hibernate you could have something like:
List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50)
.list();
As it is documented at:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querycriteria.html
That means you could do this:
Criteria c = sess.createCriteria(Cat.class)
.addOrder( Order.asc("name") )
.addOrder( Order.desc("age") )
.setMaxResults(50);
if (name != null) {
c.add( Restrictions.like("name", name);
}
List cats = c.list();

Categories