LINQ query not showing results - LEFT join with WHERE clause - c#

This is my first foray into LINQ.
I still have to wrap my head around the results part, but I can't seem to get any results from this.
var institutions = from lots in lotsdb.NEWinstitution
join webs in webbitdb.tblinstitution
on lots.institutionid equals webs.dispenseinstid into newinsts
from webs2 in newinsts.DefaultIfEmpty()
where webs2 == null
select new
{
instid = lots.institutionid,
instname = lots.institutionname
};
foreach(var instfound in institutions)
{
MessageBox.Show(instfound.instid.ToString() + " " + instfound.instname.ToString());
}
I'm using Datasets created by Visual Studio in the DATASources list.
Below is my original SQL string that i have "tried" to adapt to LINQ
string strgetloc = #"
SELECT NEWinstitution.institutionid, NEWinstitution.institutionname
FROM NEWinstitution
LEFT JOIN tblinstitution
ON NEWinstitution.institutionid = tblinstitution.dispenseinstid
WHERE (((tblinstitution.institutionid) Is Null));"

You probably need something like this:
var institutions =
from lots in lotsdb.NEWinstitution
join webs in webbitdb.tblinstitution on lots.institutionid equals webs.dispenseinstid
where webs.IsInstitutionIdNull()
select new
{
instid = lots.institutionid,
instname = lots.institutionname
};
The IsInstitutionIdNull() method is generated by the MSDataSetGenerator when the columns allows DBNull. Because you cannot compare it directly to DBNull or to null.
(fixed a typo)

So I ended up using the following code:
var idsNotInB = dtLotsInst.AsEnumerable().Select(r => r.Field<int>("institutionid"))
.Except(dtWebbitInst.AsEnumerable().Select(r => r.Field<int>("institutionid")));
count = idsNotInB.Count();
if (count != 0)
{
DataTable dtOnlyLots = (from row in dtLotsInst.AsEnumerable()
join id in idsNotInB
on row.Field<int>("institutionid") equals id
select row).CopyToDataTable();
using (OleDbConnection con = new OleDbConnection(PackChecker.Properties.Settings.Default["WebbitConnectionString"].ToString()))
{
string strgetloc = #"INSERT INTO tblinstitution ( dispenseinstid, institutionname ) VALUES (?,?)";
using (OleDbCommand cmd = new OleDbCommand(strgetloc, con))
{
con.Open();
foreach (DataRow dr in dtOnlyLots.Rows)
{
cmd.Parameters.Add("?", OleDbType.Integer).Value = Convert.ToInt32(dr["institutionid"]);
cmd.Parameters.Add("?", OleDbType.VarWChar).Value = dr["institutionname"].ToString();
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
}
con.Close();
}
}
}
This uses LINQ and works well by using "EXCEPT" in the first LINQ section to find values not in one table. Then it uses this list to generate the rows from the table I want.

Related

Select conditional in C #, it is not visible

I wrote this SQL statement, then my SQL server was running.
SELECT r.RentID,c.CustomerName,v.VehicleName,r.[Hours], r.[Hours] * v.Rent AS Total FROM Rent as r
INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID
INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID
Also when I write in C #, it does not display, although some SQL statements such as Add, Update, Delete are running normally.
I want to ask where I'm wrong and how to fix it. Thank you very much.
public IList getRents()
{
IList result = new List();
try
{
SqlConnection con = new SqlConnection(sCon);
String query = "SELECT r.RentID,c.CustomerName,v.VehicleName,r.[Hours], r.[Hours] * v.Rent AS Total FROM Rent as r INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID";
//String query = "SELECT * FROM Rent";
SqlCommand com = new SqlCommand(query, con);
con.Open();
SqlDataReader reader = com.ExecuteReader();
while (reader.Read())
{
Rent r = new Rent();
r.rentID = (int)reader[0];
r.customerID = (int)reader[1];
r.vehicleID = (int)reader[2];
r.hours = (int)reader[3];
//r.total = (int)reader[4];
result.Add(r);
}
con.Close();
}
catch (Exception ex)
{
ex.ToString();
}
return result;
}
It looks like you're trying to map the SQL column CustomerName, which is likely a VARCHAR or string-compatible type of some kind, to an int.
Rent r = new Rent();
r.rentID = (int)reader[0];
r.customerID = (int)reader[1]; // here's your problem: reader[1] is CustomerName
r.vehicleID = (int)reader[2];
r.hours = (int)reader[3];
//r.total = (int)reader[4];
result.Add(r);
How you resolve it depends on what you're trying to accomplish.
If you want CustomerName and not CustomerId, you'll need to add a property called CustomerName with type string to your Rent model and update your reader:
Rent r = new Rent();
r.rentID = (int)reader[0];
r.customerName = (string)reader[1]; // updated
r.vehicleID = (int)reader[2];
r.hours = (int)reader[3];
//r.total = (int)reader[4];
result.Add(r);
If you want CustomerId, then you need to change your SQL:
SELECT r.RentID, c.CustomerId, v.VehicleName, r.[Hours], r.[Hours] * v.Rent AS Total FROM Rent as r INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID
More Information
There are some issues with your code that prevented you from debugging what was wrong.
First, you're catching any exceptions that can possibly be thrown and swallowing them up. getRents() will always successfully return, but if there's any errors at all, it'll return an empty List and you'll never know why. My guess is that's why you're saying "it doesn't display".
For the purposes of debugging, remove the try catch block. You can add it later but only do so if you're planning on doing something with the exception, such as displaying friendly error messages and/or logging them.
When you run your code without the try catch block, it will throw an exception that should help you figure out what was wrong.
There are some other issues as well. It's generally a good idea to utilize using statements on objects that implement IDisposable. A quick and easy way to determine if they do is to initialize it, then see if there's a .Dispose method:
var conn = new SqlConnection();
conn.Dispose();
If conn.Dispose() doesn't have a compiler error, or Intellisense shows it as a valid method, then the object is disposable and you should use a using statement:
using(var conn = new SqlConnection())
{
// ...
}
Disposing disposable objects helps to prevent memory leaks and free up resources you no longer need. Further, it lets you do stuff like this:
public IList getRents()
{
using (var con = new SqlConnection(sCon))
{
var query = "SELECT r.RentID,c.CustomerName,v.VehicleName,r.[Hours], r.[Hours] * v.Rent AS Total FROM Rent as r INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID";
using (var com = new SqlCommand(query, con))
{
con.Open();
using (var reader = com.ExecuteReader())
{
IList result = new List<Rent>();
while (reader.Read())
{
Rent r = new Rent();
r.rentID = (int)reader[0];
r.customerID = (int)reader[1];
r.vehicleID = (int)reader[2];
r.hours = (int)reader[3];
//r.total = (int)reader[4];
result.Add(r);
}
return result;
}
}
}
}
I refactored your method a bit to be easier to read and debug. Note that return is being called right after the SqlDataReader completes instead of at the very end of the method. This enables you to keep variables closer to where they're being used, in this case result. This makes it easier to find potential problems and correct exceptions that do come up.
Finally, since getRents really only does mapping from SQL to C# objects, I think a micro-ORM like Dapper will help you greatly. This is more of a personal decision but if you're app is doing a lot of CRUD work, then an micro-ORM like Dapper would cut down considerably on the time it takes to write as well as lower potential problems.
Consider this refactored version using Dapper:
public IList GetRents()
{
using(var conn = new SqlConnection(sCon))
{
var query = "SELECT r.RentID,c.CustomerName,v.VehicleName,r.[Hours], r.[Hours] * v.Rent AS Total FROM Rent as r INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID";
return conn.Query<Rent>(sql).ToList();
}
}
As you can see, it's a lot shorter, simpler, and straight to the point. Just make sure your class properties match your SQL column names. Stack Overflow uses Dapper; in fact, they developed it. Read more about it here.
You are casting everything to int. Clarify if you want the name or the ID in your code. If you want the ID then change your query to read
String query = "SELECT r.RentID,c.CustomerID,v.VehicleID,r.[Hours], r.[Hours] * v.Rent AS Total FROM Rent as r INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID";
If it was the names you wanted then change the respective fields to cast to a string. Also check the data type produced by multiplying r.Hours * v.Rent as this could be rounding your results inadvertently for the line you commented out.
You never specified the exception you are getting. It's possible it fails because your are casting strings (the 2 names) to an integer. As it is, your code swallows any exception and doesn't report it or log it so finding the exact source of the problem won't be to easy.
Consider this in your exception handler
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
then check your Output window for what error you got. And place it here so we can better help.
Try it.
In your code you had a "*" the most
public IList getRents()
{
IList result = new List();
try
{
SqlConnection con = new SqlConnection(sCon);
String query = "SELECT r.RentID,c.CustomerName,v.VehicleName,r.[Hours], r.[Hours] , v.Rent AS Total FROM Rent as r INNER JOIN Customer AS c ON r.CustomerID = c.CustomerID INNER JOIN Vehicle AS v ON r.VehicleID = v.VehicleID";
//String query = "SELECT * FROM Rent";
SqlCommand com = new SqlCommand(query, con);
con.Open();
SqlDataReader reader = com.ExecuteReader();
while (reader.Read())
{
Rent r = new Rent();
r.rentID = (int)reader[0];
r.customerID = (int)reader[1];
r.vehicleID = (int)reader[2];
r.hours = (int)reader[3];
//r.total = (int)reader[4];
result.Add(r);
}
con.Close();
}
catch (Exception ex)
{
ex.ToString();
}
return result;
}

Performance differences in c#

I have below code. Sometime it takes a bit longer time to get this code executed.
Need to improve the code.
//Get products from DB
DataTable dtProducts = GetAllProducts();
if(dtProducts !=null && dtProducts.Rows.Count >0)
{
dtProducts .DefaultView.RowFilter = null;
dtProducts .DefaultView.RowFilter = " product_id = '" +product_id.Trim() + "'";
DataTable dtProductCount = dtProducts .DefaultView.ToTable();
if (dtProductCount != null && dtProductCount.Rows.Count > 0)
{
object obj = dtProductCount.Compute("SUM(qty)", "");
if (!Convert.IsDBNull(obj))
{
int qtyProd = Convert.ToInt32(obj);
}
}
}
Is there any scope of performance improvement here ?
Like instead of Count, can we use Any(), in data table.
One definite improvement would be including the filter condition product_id = '" +product_id.Trim() + "' in SQL query itself in WHERE clause thus returning you only the required dataset instead of everything
select * from products
where product_id = #product_id
Per your comment Because this code run inside a for each loop from where I got product Id ... well then from your for loop collect all the product_id and create a inlist like 1,2,34,..... then use that solely in your query using a IN operator.
The database itself is specialized in data aggregation, so you could deliver that job for it:
public DataTable GetProductQuantitySummary(int? productId)
{
using (var cn = new SqlConnection("...."))
using (var cm = new SqlCommand ("", cn))
{
cn.Open();
cm.CommandText = #"
SELECT product_id, SUM(qty) SumQty
FROM Products
WHERE product_id = #product_id OR #product_id IS NULL
GROUP BY product_id";
cm.Parameters.AddWithValue("#product_id", (object) productId ?? DBNull.Value);
var table = new DataTable();
using (var reader = cm.ExecuteReader())
{
table.Load(reader);
}
return table;
}
}
This way, you'll only return the required data.
If you prefer to call this method always passing the productId, you don't need to return the DataTable at all, and you could return the result:
public int GetProductQuantitySummary(int productId)
{
using (var cn = new SqlConnection("..."))
using (var cm = new SqlCommand("", cn))
{
cn.Open();
cm.CommandText = #"
SELECT SUM(qty) SumQty
FROM Products
WHERE product_id = #product_id";
cm.Parameters.AddWithValue("#product_id", productId);
return cm.ExecuteScalar() as int? ?? 0;
}
}

Inserting multiple rows into MS SQL Server and retrieve all the new table ID's back

Looking at the example given here:
https://stackoverflow.com/a/452934
I understand that I will need to iterate through a loop and append value clauses, but what I am missing is how to amend the query to return all the ID's for the newly created records and retrieve them in C#?
For Example my current code can be seen below, I would like to change it to insert multiple rows in one query and retrieve the newly created Id's as a List of integers ideally.
in_new_id = -1;
String query = "INSERT INTO " + DB_Base.DBTable_Customer_Order_Table + "(" + DB_Base.DBTable_Customer_Order_Table_Customer_ID + "," + DB_Base.DBTable_Customer_Order_Table_ProductId+")";
query += " OUTPUT INSERTED." + DB_Base.DBTable_Customer_Order_Table_ID;
query += " VALUES ( #customerId, #productId);";
using (SqlConnection conn = new SqlConnection(GeneralConfig.DB_STR()))
{
SqlCommand sql_command = new SqlCommand(query, conn);
sql_command.Parameters.AddWithValue("#customerId", data_obj.customerId);
sql_command.Parameters.AddWithValue("#productId", data_obj.productId);
if (!String.IsNullOrEmpty(query) && sql_command != null && conn != null)
{
sql_command.Connection.Open();
if (sql_command.Connection.State == System.Data.ConnectionState.Open)
{
object out_new_id = sql_command.ExecuteScalar();
if (out_new_id != null)
{
in_new_id = (int)out_new_id;
}
sql_command.Connection.Close();
return ENUM_DB_Status.DB_SUCCESS;
}
else
{
in_new_id = -1;
return ENUM_DB_Status.DB_CONNECTION_COULD_NOT_OPEN;
}
}
}
return ENUM_DB_Status.DB_FAIL;
Use this:
List<int> ids = new List<int>();
using (SqlCommand command = new SqlCommand(#"declare #T TABLE(Id int)
INSERT INTO YourTableName(YourTableColumnNames)
OUTPUT Inserted.Id into #T VALUES
(YourValues1),
(YourValues2),
(YourValues3),
(etc...) select Id from #T ", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
int id = int.Parse(reader[0].ToString());
ids.Add(id);
}
}
}
Warning!!! This will work only if you're using SQLServer 2008 R2 or higher.
Edit: As Damien said in the comments : "There is no guarantee that the order in which the changes are applied to the table and the order in which the rows are inserted into the output table or table variable will correspond."

Get column name from SQL Server

I'm trying to get the column names of a table I have stored in SQL Server 2008 R2.
I've literally tried everything but I can't seem to find how to do this.
Right now this is my code in C#
public string[] getColumnsName()
{
List<string> listacolumnas=new List<string>();
using (SqlConnection connection = new SqlConnection(Connection))
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT TOP 0 * FROM Usuarios";
connection.Open();
using (var reader = command.ExecuteReader(CommandBehavior.KeyInfo))
{
reader.Read();
var table = reader.GetSchemaTable();
foreach (DataColumn column in table.Columns)
{
listacolumnas.Add(column.ColumnName);
}
}
}
return listacolumnas.ToArray();
}
But this is returning me the following
<string>ColumnName</string>
<string>ColumnOrdinal</string>
<string>ColumnSize</string>
<string>NumericPrecision</string>
<string>NumericScale</string>
<string>IsUnique</string>
<string>IsKey</string>
<string>BaseServerName</string>
<string>BaseCatalogName</string>
<string>BaseColumnName</string>
<string>BaseSchemaName</string>
<string>BaseTableName</string>
<string>DataType</string>
<string>AllowDBNull</string>
<string>ProviderType</string>
<string>IsAliased</string>
<string>IsExpression</string>
<string>IsIdentity</string>
<string>IsAutoIncrement</string>
<string>IsRowVersion</string>
<string>IsHidden</string>
<string>IsLong</string>
<string>IsReadOnly</string>
<string>ProviderSpecificDataType</string>
<string>DataTypeName</string>
<string>XmlSchemaCollectionDatabase</string>
<string>XmlSchemaCollectionOwningSchema</string>
<string>XmlSchemaCollectionName</string>
<string>UdtAssemblyQualifiedName</string>
<string>NonVersionedProviderType</string>
<string>IsColumnSet</string>
Any ideas?
It shows the <string> tags as this is how my web service sends the data.
You can use the query below to get the column names for your table. The query below gets all the columns for a user table of a given name:
select c.name from sys.columns c
inner join sys.tables t
on t.object_id = c.object_id
and t.name = 'Usuarios' and t.type = 'U'
In your code, it will look like that:
public string[] getColumnsName()
{
List<string> listacolumnas=new List<string>();
using (SqlConnection connection = new SqlConnection(Connection))
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select c.name from sys.columns c inner join sys.tables t on t.object_id = c.object_id and t.name = 'Usuarios' and t.type = 'U'";
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
listacolumnas.Add(reader.GetString(0));
}
}
}
return listacolumnas.ToArray();
}
I typically use the GetSchema method to retrieve Column specific information, this snippet will return the column names in a string List:
using (SqlConnection conn = new SqlConnection("<ConnectionString>"))
{
string[] restrictions = new string[4] { null, null, "<TableName>", null };
conn.Open();
var columnList = conn.GetSchema("Columns", restrictions).AsEnumerable().Select(s => s.Field<String>("Column_Name")).ToList();
}
SELECT COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'YourTable'
public string[] getColumnsName()
{
List<string> listacolumnas=new List<string>();
using (SqlConnection connection = new SqlConnection(Connection))
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select column_name from information_schema.columns where table_name = 'Usuarios'";
connection.Open(;
using (var reader = command.ExecuteReader(CommandBehavior.KeyInfo))
{
reader.Read();
var table = reader.GetSchemaTable();
foreach (DataColumn column in table.Columns)
{
listacolumnas.Add(column.ColumnName);
}
}
}
return listacolumnas.ToArray();
}
The original post was close to the goal, Just some small changes and you got it.
Here is my solution.
public List<string> GetColumns(string tableName)
{
List<string> colList = new List<string>();
DataTable dataTable = new DataTable();
string cmdString = String.Format("SELECT TOP 0 * FROM {0}", tableName);
if (ConnectionManager != null)
{
try
{
using (SqlDataAdapter dataContent = new SqlDataAdapter(cmdString, ConnectionManager.ConnectionToSQL))
{
dataContent.Fill(dataTable);
foreach (DataColumn col in dataTable.Columns)
{
colList.Add(col.ColumnName);
}
}
}
catch (Exception ex)
{
InternalError = ex.Message;
}
}
return colList;
}
Currently, there are two ways I could think of doing this:
In pure SQL Server SQL you can use the views defined in INFORMATION_SCHEMA.COLUMNS. There, you would need to select the row for your table, matching on the column TABLE_NAME.
Since you are using C#, it's probably easier to obtain the names from the SqlDataReader instance that is returned by ExecuteReader. The class provides a property FieldCount, for the number of columns, and a method GetName(int), taking the column number as its argument and returning the name of the column.
sp_columns - http://technet.microsoft.com/en-us/library/ms176077.aspx
There are many built in stored procedures for this type of thing.

LINQ Select From One Generic List to Assign Value to Another

While looping through records in one list, I want to find a corresponding line in another list containing more information for that item. I am getting a conversion error when trying to use LINQ to get this information from the first list.
public class qtyAvail
{
public string itemNumber;
public string qtyAv;
}
public class variance
{
public string siteID;
public string itemNumber;
public string varQty;
}
public void saveVariance(Context context)
{
var settings = PreferenceManager.GetDefaultSharedPreferences(context);
var siteID = settings.GetString("siteID", null);
updateInv(siteID);
var inst = new InventoryApp();
List<variance> ilist = new List<variance>();
List<qtyAvail> avail = new List<qtyAvail>();
SqliteDataReader dr;
var connection = new SqliteConnection("Data Source=" + dbPath);
connection.Open();
var c = connection.CreateCommand();
c.CommandText = "Select ItemNmbr, OnHnd - Alloc From PartInfo";
dr = c.ExecuteReader();
while (dr.Read())
{
qtyAvail qa = new qtyAvail();
qa.itemNumber = dr[0].ToString();
qa.qtyAv = dr[1].ToString();
avail.Add(qa);
}
dr.Close();
c.CommandText = "Select * From Items";
dr = c.ExecuteReader();
while (dr.Read())
{
variance v = new variance();
v.siteID = siteID;
v.itemNumber = dr[0].ToString();
v.varQty = from q in avail where q.itemNumber == dr[1].ToString() select q.qtyAv;
ilist.Add(v);
}
dr.Close();
connection.Close();
inst.saveVariance(siteID, ilist.ToArray());
}
When I build the solution I get
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<string>' to 'string'
On the line where I use LINQ to assign v.varQty. I am sure there is some simple thing I am missing but just can't find the right cast, conversion, etc. I also realize that I could easily just perform the select on the Sqlite table to get the information rather than using lists, but I am trying to figure all this LINQ stuff out so I want to get it to work this way too.
v.varQty = from q in avail where q.itemNumber == dr[1].ToString() select q.qtyAv;
Your query returns an IEnumerable<string> and not a single string -since varQty is a string use FirstOrDefault() here:
v.varQty = (from q in avail where q.itemNumber == dr[1].ToString() select q.qtyAv).FirstOrDefault();
Or shorter in dot notation:
v.varQty = avail.Where(q => q.itemNumber == dr[1].ToString()).FirstOrDefault();
from q in avail where q.itemNumber == dr[1].ToString() select q.qtyAv;
above linq query returns a collection of string. You cannot assign a collection to your String type variable
You should instead use FirstOrDefault() which will return only one record from that enumerable collection
Try this
(from q in avail where q.itemNumber == dr[1].ToString() select q.qtyAv).FirstOrDefault();
The line you have specified:
v.varQty = from q in avail where q.itemNumber == dr[1].ToString() select q.qtyAv;
Tries to assign a list of strings to a single string, so it fails.
You need to get a single item and assign that. You have several options - using First() or FirstOrDefault() which work as you would expect. Or use Single() which would throw an exception if there isn't exactly one item in the list.
In this example, I have use FirstOrDefault:
v.varQty = (from q in avail where q.itemNumber == dr[1].ToString()
.FirstOrDefault();

Categories

Resources