Formatting a WQL Output in C# - c#

I'm trying to build a query, to list all the known computers in SCCM with a specific name.
The query looks like this:
string query = string.Format("Select Name From SMS_R_System Where Name like '" + "%" + computerName + "%" + "'");
If results are found, it puts the result(s) in a dropdown box.
My problem in these case, the output looks like this:
"instance of SMS_R_System{Name = "DC01";};"
But of course, for our use case we only need DC01 as output.
Any tips?
The full Code for the ButtonEvent:
private void ChkBtn_Click(object sender, EventArgs e)
{
string computerName = PCDropDown.Text;
lBox.Items.Clear();
SmsNamedValuesDictionary namedValues = new SmsNamedValuesDictionary();
WqlConnectionManager connection = new WqlConnectionManager(namedValues);
// Connect to remote computer.
try
{
connection.Connect(PrimarySiteServer.ToString());
// Set the query.
string query1 = string.Format("Select Name From SMS_R_System Where Name like '" + "%" + computerName + "%" + "'");
string query2 = string.Format("Select * From SMS_UserMachineRelationship WHERE ResourceName like '" + "%" + computerName + "%" + "' AND IsActive = '1' AND Types = '1'");
// Get the query results
IResultObject queryResults = connection.QueryProcessor.ExecuteQuery(query1);
// Check for results and display in infobox
bool resultsFound = false;
foreach (IResultObject queryResult in queryResults)
{
resultsFound = true;
lBox.Items.Add("Rechner ist vorhanden");
PCDropDown.Items.Add(queryResult.ToString());
}
if (resultsFound == false)
{
lBox.Items.Add("Rechnername nicht gefunden");
}
}
catch
{
MessageBox.Show("No Connection to Config-Manager - Als ZZA ausgeführt? SCCM-Servername richtig?");
}
}

Instead of adding queryResult.ToString() like you do here:
PCDropDown.Items.Add(queryResult.ToString());
you need to add the correct field of queryResult, so in this case:
PCDropDown.Items.Add(queryResult["Name"].StringValue);
Also a quick note. I don't know for who you are writing this and what the next step would be but if this is a read only application that is only used by SCCM Admins I would consider ignoring WMI and going to the SCCM DB via SQL instead. It is a lot faster, SQL has far more powerful options for queries and it does not need the integration of those strange sccm console Dlls (although that is not 100% necessary for WMI either).
If you need write access to create devices or collections etc., or you need to work with the roles the sccm access rights systems implements however WMI is the better or only choice. (And in this case I'd rather really use those strange dlls because all of the MS examples rely on them and it can be hard to translate those tutorials to the vanilla WMI solution C# offers.

Related

System.Data.SqlClient.SqlException: 'Incorrect syntax near the keyword 'where'.' ||||who can help me with this error?

the code is below and the error starting from sqlCommand cmd the 13th line of this code
private void button2_Click(object sender, EventArgs e)
{
if (StudenUsn.Text == "" )
{
MessageBox.Show("Enter The Student Number");
} else {
Con.Open();
String query = "update Student_tbl set StdName='" + StudName.Text + "',where FatherName='" + FtName.Text + "',where MotherName='" + MtName.Text + "',where StdAddress='" + Address.Text + "',where Collage ='" + Collage.Text + "'set StdRoom = " + StRmNum.SelectedValue.ToString()+",StdStatus = '"+ StudSt.SelectedItem.ToString() + "' where StdUsn ='"+StudenUsn+ "')";
SqlCommand cmd = new SqlCommand(query, Con);
cmd.ExecuteNonQuery();
MessageBox.Show("Room Successfully Updates");
Con.Close();
FillStudentDGV();
}
}
Your code should look more like:
private void button2_Click(object sender, EventArgs e)
{
if (StudenUsn.Text == "" )
{
MessageBox.Show("Enter The Student Number");
} else {
var query = #"
update Student_tbl
set
StdName=#sn,
FatherName=#fn,
MotherName=#mn,
StdAddress=#sa,
Collage=#c,
StdRoom=#sr,
StdStatus=#ss
where
StdUsn=#su";
using var con = new SqlConnection(YOUR_CONN_STR_HERE);
using var cmd = new SqlCommand(query, con);
cmd.Parameters.AddWithValue(#sn, StudName.Text);
cmd.Parameters.AddWithValue(#fn, FtName.Text);
cmd.Parameters.AddWithValue(#mn, MtName.Text);
cmd.Parameters.AddWithValue(#sa, Address.Text);
cmd.Parameters.AddWithValue(#c, Collage.Text);
cmd.Parameters.AddWithValue(#sr, StRmNum.SelectedValue);
cmd.Parameters.AddWithValue(#ss, StudSt.SelectedItem);
cmd.Parameters.AddWithValue(#su, StudenUsn);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
MessageBox.Show("Room Successfully Updates");
FillStudentDGV();
}
}
There are good reasons to avoid using AddWithValue if you use SQLServer which you can get into at a later date if you want, but it's convenient for me (who doesn't know the types and widths of your columns) dealing with the current massive elephant in the room which is your SQL is massively vulnerable to a hacking technique known as sql injection (and to a lesser extent it would blow up with an error for any student whose name included an apostrophe) - using AddWithValue might make your query slightly slower, but better that than it be the cause of the next data breach; learn how to write SQLs right, right now
Never ever take data supplied by a user and concatenate it into an SQL string. Doing so essentially, in most cases, gives the user access to your database. So many big companies whose developers should know better, put up expensive firewalls and security and then let anyone in via this back door anyway; sql injection prone systems are one of the leading causes of hacks in the world today
Always use #parameter placeholders in the SQL for user data and add a parameter to the command's parameters collection, containing the data
Now on the topic of your actual error; the pattern for an update is
update table
set col1=#param1, col2=#param2 ...
where (some conditions)
You have one where and one set. If there is some conditional aspect to your set, like you only want to update the student name/address if it is currently null then you can do like:
update table
set
name=case when name is null then #n else name end,
address=case when address is null then #a else address end
where (some conditions)
Or more simply
update table
set
name=coalesce(name, #n)
address=coalesce(address, #a)
where (some conditions)
You can't mix n match and say "where this=that where this2=that2 set this3=that3" - that's a syntax error. Where is for picking the row you want to update and set is for starting a block of commas separated columns and values the row data is to be updated to.
Strive to write your sql nicely formatted inside an #string; it's a programming language all of its own, and will be easier to debug if it's laid out nicely
Can u try with it ?
String query = "update Student_tbl set StdName='" + StudName.Text + "',StdRoom = '" + StRmNum.SelectedValue.ToString()+"',StdStatus = '"+ StudSt.SelectedItem.ToString() + "' where FatherName='" + FtName.Text + "' and MotherName='" + MtName.Text + "' and StdAddress='" + Address.Text + "' and Collage ='" + Collage.Text + "' and StdUsn ='"+StudenUsn+ "'";

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 + "'";

Can't Add Parameter To Query String

I need to have a second copy of a MS Access table that will be saved in a network drive. And since splitting is not an option because it drastically slows the application down, I decided to just manually "merge" the data after a certain user action like clicking an exit button.
I have this query string
public const string MERGETOMAIN = #"INSERT INTO tbl_name (UserID, ...)" +
" IN 'C:\Users\nathan\Desktop\copy.accdb' SELECT TOP 1 UserId, ... " +
" FROM tbl_name WHERE UserID = #currentUser ORDER BY ROWID DESC";
...and it works when I do this:
using (OleDbCommand cmd = new OleDbCommand(Helpers.Queries.MERGETOMAIN, mergeConn))
{
cmd.Parameters.AddWithValue("#currentUser", currentUserID);
cmd.ExecuteNonQuery();
}
But, as you notice, the path to the copy is hardcoded. I wanted it to be dynamic so I tried using a parameter like I always do, so I replaced it with a question mark:
public const string MERGETOMAIN = #"INSERT INTO tbl_name (UserID, ...)" +
" IN ? SELECT TOP 1 UserId, ... " +
" FROM tbl_name WHERE UserID = #currentUser ORDER BY ROWID DESC";
But this results in the following error:
Your network access was interrupted. To continue close the database and then open it again.
So instead of a question mark, I used #parameterName. But, when I do, I get the following error:
The file "foo\foo\#parameterName" cannot be found.
The weird thing is, the other paremeter, #currentUser, is working just fine.
I'm not really sure what is happening here, but the only thing that works right now is to hardcode the path. I tried looking through similar questions but no dice.
Any idea will be greatly appreciated.
Thank you.
You can write a method which creates the string for you.
public string getMergeToMain(string path)
{
string strRet;
if (path != null)
{
strRet = #"INSERT INTO FSO_LogSheet (UserID, ...)" +
" IN '" + path + "' SELECT TOP 1 UserId, ... " +
" FROM FSO_LogSheet WHERE UserID = #currentUser ORDER BY ROWID DESC";
return strRet;
}
else
{ //Error, not agood habit to return null...
return null;
}
}
With the method parameter "path", you can set the path dynamically.
You can call it for example by
OleDbCommand cmd = new OleDbCommand(fooClass.getMergeToMain(#"C:\users\bar.accdb"), mergeConn)
I assume that you know the difference between public, private and static methods, so please do not forget to set it right for your own needs.

LINQ to SQL custom query builder

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.

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