Linq to SQL JOIN? - c#

I am trying to query names in one table and then use that result to pull the master records into a DataGridView. So what I need to do is get the names from the interest table that are like what is put into text boxes then use those results to pull the data from the CaseSelector table and set the bindingsource filter to those results. Why can't I seem to set results to the caseSelectorBindingSourceFilter
var results = from CaseSelector in db.CaseSelectors
from Interest in db.Interests
where SqlMethods.Like(Interest.First, txtFirst.Text) && SqlMethods.Like(Interest.Last, txtLast.Text)
select CaseSelector;
caseSelectorBindingSource.Filter = ("CaseNumberKey =" + results);

You can find examples for LINQ join queries here:
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
I don't know your DB schema but you're looking for something along the lines of:
from c in db.Cases
join i in db.Interest on i.CaseNumberKey equals c.CaseNumberKey
select c

What you've just retrieved is a SQL Set of the valid caseNumberKeys.
Your casefilter should be something like
caseSelectorBindingSource.Filter = "CaseNumberKey in " + interestsresultsAsSQLSET;
You'll have to iterate over the interestsresults and convert it to the string representation of a SQLSET. If you're feeling lucky, you could just try to .toString() it and see what happens.
string interestsResultsAsSQLSet = "(";
//write most of them
for(int i=0; i<interestsresults.size() - 1; i++) {
interestsResultAsSQLSet.append(interestsresults[i] + ",");
//write the last one, with right paren
interestsresults.append(interestsresults[interestsresults.size() -1] + ")");
I write Java all day, ignore my basic language errors. :P

Related

DataTable.Select with DateTime stored as String

I have a DataTable with a DateTime stored as a string like "20.12.2017".
I want to select all rows within the last 6 month.
I can do this with a foreach:
foreach (DataRow dr in dsErgebnisse.Tables[0].Rows)
{
if (Convert.ToDateTime(dr[6].ToString()) > DateTime.Now.AddMonths(-6))
{
dsTemp.Tables[0].ImportRow(dr);
}
}
This gives me 3.613 rows.
I try to do this with a select to check if it's faster:
DataRow[] foundRows = dsErgebnisse.Tables[0].Select("DATUM > '" + DateTime.Now.AddMonths(-6).ToShortDateString() + "'");
DATUM is my column where the DateTimeis stored as a string.
This gives me 2.624 rows.
Why is there a difference?
I tried to use convert in the select statement but I fail with System.Data.EvaluateException:
foundRows = dsErgebnisse.Tables[0].Select("Convert(DATUM, 'System.DateTime') > '" + DateTime.Now.AddMonths(-6).ToShortDateString() + "'");
rule 0: stop using DataTable... just ever*
rule 1: don't store date/time values as strings; store them as DateTime or similar
rule 2: if you ignore rules 0 and 1, make sure you store them in a sortable order like "2017-12-20"
since you've broken all of those rules, most bets are off, and you'll probably have to do the filter manually by iterating over the rows, fetching the values, and doing your own conversions. LINQ via .AsEnumerable() may help; it certainly can't make it much worse :)
*=there is a tiny category of problems where DataTable is appropriate; if you know the schema of the data in advance well enough to issue a Select query: this isn't one of them

Scanning my table c# mysql

So i'm making a library system, and i encounter a problem in my Borrowing and Returning of books.
When i borrow a book then return it changing the status to return, then borrowing that same book again success, then when i'm returning it my trapping for return books message show.
sql = "SELECT * FROM tbltransactionbooks WHERE fBarCodeNo LIKE '" + txtBARCODE_R.Text.Trim() + "%'";
cfgotcall.engageQuery(sql);
if (cfgotcall.tbl.Rows[0]["fStatus"].ToString().Equals("Borrowed"))
{
txtTITLE_R.Text = cfgotcall.tbl.Rows[0]["fBookTitle"].ToString();
txtAUTHOR_R.Text = cfgotcall.tbl.Rows[0]["fAuthor"].ToString();
txtYEAR_R.Text = cfgotcall.tbl.Rows[0]["fBookYr"].ToString();
txtACCNO_R.Text = cfgotcall.tbl.Rows[0]["fAccNo"].ToString();
txtCALLNO_R.Text = cfgotcall.tbl.Rows[0]["fCallNo"].ToString();
txtBARCODE_BR.Text = cfgotcall.tbl.Rows[0]["fBarcodeNo"].ToString();
txtSEARCH.Text = cfgotcall.tbl.Rows[0]["fStudent"].ToString();
txtReturnDate.Text = cfgotcall.tbl.Rows[0]["fBorrowDate"].ToString();
txtIDNO.Text = cfgotcall.tbl.Rows[0]["fIDStudent"].ToString();
txtLEVEL.Text = cfgotcall.tbl.Rows[0]["fLevel"].ToString();
}
else
{
MessageBox.Show("Book already returned.");
}
What i debug in my own code in the pic is it doesn't scan the whole rows in the tbltransactionbooks it only reads the first row of my table.
33 123 NAME IT 2/20/2017 2/20/2017 [HISTORY OF] COMPUTERS: THE MACHINES WE THINK WITH Returned
33 123 NAME IT 2/21/2017 2/21/2017 [HISTORY OF] COMPUTERS: THE MACHINES WE THINK WITH Borrowed
![2]: http://i66.tinypic.com/28ajgwg.jpg
How dow I scan the whole rows in my table? If my code above does not look good I'm open in suggestion of how I am able make it clean. thanks
cfgotcall.tbl.Rows[0] refers to the the first row only.
You should iterate each row:
sql = "SELECT * FROM tbltransactionbooks WHERE fBarCodeNo LIKE '" + txtBARCODE_R.Text.Trim() + "%'";
cfgotcall.engageQuery(sql);
foreach(var row in cfgotcall.tbl.Rows)
{
if (row["fStatus"].ToString().Equals("Borrowed"))
{
txtTITLE_R.Text = row["fBookTitle"].ToString();
txtAUTHOR_R.Text = row["fAuthor"].ToString();
txtYEAR_R.Text = row["fBookYr"].ToString();
txtACCNO_R.Text = row["fAccNo"].ToString();
txtCALLNO_R.Text = row["fCallNo"].ToString();
txtBARCODE_BR.Text = row["fBarcodeNo"].ToString();
txtSEARCH.Text = row["fStudent"].ToString();
txtReturnDate.Text = row["fBorrowDate"].ToString();
txtIDNO.Text = row["fIDStudent"].ToString();
txtLEVEL.Text = row["fLevel"].ToString();
}
else
{
MessageBox.Show("Book already returned.");
}
}
To avoid getting run time errors due to typos in the column names and the SQL query, you can use LINQ to SQL or Entity Framework. This way each row is converted to an object and you can access each column by accessing an object property\field.
The first row is checked because use access only the first row:
if (cfgotcall.tbl.Rows[0]...)
To check all the rows, iterate through the cfgotcall.tbl.Rows collection either using the for or foreach loop.
You are only accessing the first row [0] of the table. You need to itterate through all of them using for or foreach loop.
Side note: I guess this is a homework. It will not do you any good if you find complete solution in the internet. You should rather try to understand how collections/arrays work and how to traverse them using loops.
A couple of tutorials to help you with the task:
https://msdn.microsoft.com/en-us/library/aa288462(v=vs.71).aspx
http://csharp.net-informations.com/collection/csharp-collection-tutorial.htm

Pass the table and column dynamically in dynamic LINQ

I searched a lot, but couldn't find what I was looking exactly. This may be very simple. Normally in LINQ we write the select query like this:
var entityModel = new StudentEntities();
var dept = (from a in entityModel.STUDENT where a.NAME != null select a.DEPARTMENT).Distinct().OrderBy(w => w);
//STUDENT- table, NAME-column name, DEPARTMENT-column name
How to write the same query using dynamic LINQ? Here the table name and column name names will be selected from either some winForm controls (textbox/combobox) or string. Tried this:
var dept = "(from a in entityModel." + tableName + "where a." + cbo1.Text.ToString + "!= null select a."+cbo2.Text.ToString +").Distinct().OrderBy(w => w)";
This is not working. Can anyone point me to the right direction, please?
You can't do that. You need to generate your dynamic SQL and run it over your database.
There's this library: Dynamic LINQ, but it isn't as dynamic as you want.

check if values are in datatable

I have an array or string:
private static string[] dataNames = new string[] {"value1", "value2".... };
I have table in my SQL database with a column of varchar type. I want to check which values from the array of string exists in that column.
I tried this:
public static void testProducts() {
string query = "select * from my table"
var dataTable = from row in dt.AsEnumerable()
where String.Equals(row.Field<string>("columnName"), dataNames[0], StringComparison.OrdinalIgnoreCase)
select new {
Name = row.Field<string> ("columnName")
};
foreach(var oneName in dataTable){
Console.WriteLine(oneName.Name);
}
}
that code is not the actual code, I am just trying to show you the important part
That code as you see check according to dataNames[index]
It works fine, but I have to run that code 56 times because the array has 56 elements and in each time I change the index
is there a faster way please?
the Comparison is case insensitive
First, you should not filter records in memory but in the datatabase.
But if you already have a DataTable and you need to find rows where one of it's fields is in your string[], you can use Linq-To-DataTable.
For example Enumerable.Contains:
var matchingRows = dt.AsEnumerable()
.Where(row => dataNames.Contains(row.Field<string>("columnName"), StringComparer.OrdinalIgnoreCase));
foreach(DataRow row in matchingRows)
Console.WriteLine(row.Field<string>("columnName"));
Here is a more efficient (but less readable) approach using Enumerable.Join:
var matchingRows = dt.AsEnumerable().Join(dataNames,
row => row.Field<string>("columnName"),
name => name,
(row, name) => row,
StringComparer.OrdinalIgnoreCase);
try to use contains should return all value that you need
var data = from row in dt.AsEnumerable()
where dataNames.Contains(row.Field<string>("columnName"))
select new
{
Name = row.Field<string>("columnName")
};
Passing a list of values is surprisingly difficult. Passing a table-valued parameter requires creating a T-SQL data type on the server. You can pass an XML document containing the parameters and decode that using SQL Server's convoluted XML syntax.
Below is a relatively simple alternative that works for up to a thousand values. The goal is to to build an in query:
select col1 from YourTable where col1 in ('val1', 'val2', ...)
In C#, you should probably use parameters:
select col1 from YourTable where col1 in (#par1, #par2, ...)
Which you can pass like:
var com = yourConnection.CreateCommand();
com.CommandText = #"select col1 from YourTable where col1 in (";
for (var i=0; i< dataNames.Length; i++)
{
var parName = string.Format("par{0}", i+1);
com.Parameters.AddWithValue(parName, dataNames[i]);
com.CommandText += parName;
if (i+1 != dataNames.Length)
com.CommandText += ", ";
}
com.CommandText += ");";
var existingValues = new List<string>();
using (var reader = com.ExecuteReader())
{
while (read.Read())
existingValues.Add(read["col1"]);
}
Given the complexity of this solution I'd go for Max' or Tim's answer. You could consider this answer if the table is very large and you can't copy it into memory.
Sorry I don't have a lot of relevant code here, but I did a similar thing quite some time ago, so I will try to explain.
Essentially I had a long list of item IDs that I needed to return to the client, which then told the server which ones it wanted loaded at any particular time. The original query passed the values as a comma separated set of strings (they were actually GUIDs). Problem was that once the number of entries hit 100, there was a noticeable lag to the user, once it got to 1000 possible entries, the query took a minute and a half, and when we went to 10,000, lets just say you could boil the kettle and drink your tea/coffee before it came back.
The answer was to stick the values to check directly into a temporary table, where one row of the table represented one value to check against. The temporary table was keyed against the user who performed the search, so this meant other users searches wouldn't become corrupted with each other, and when the user logged out, then we knew which values in the search table could be removed.
Depending on where this data comes from will depend on the best way for you to load the reference table. But once it is there, then your new query will look something like:-
SELECT Count(t.*), rt.dataName
FROM table t
RIGHT JOIN referenceTable rt ON tr.dataName = t.columnName
WHERE rt.userRef = #UserIdValue
GROUP BY tr.dataName
The RIGHT JOIN here should give you a value for each of your reference table values, including 0 if the value did not appear in your table. If you don't care which one don't appear, then changing it to an INNER JOIN will eliminate the zeros.
The WHERE clause is to ensure that your search only returns the unique items that you are looking for at the moment - the design should consider that concurrent access will someday occur here (even if it doesn't at the moment), so writing something in to protect it is advisable.

Why does my DataGridView DataSource not drawing rows?

I have a problem with my DataGridView.DataSource and it has consumed me a lot of time solving this problem. Below is the code:
string[] queryWords = singleQuery.Split(' '); // Split the query words according the "space" character
// Compose the SQL select query
string selectClause = #"SELECT ID, CategoryID, Name, UnitID, Price, QuantityAgen, QuantityBanjer, QuantityMalalayang, QuantitySorong FROM Product WHERE ";
// Add the where clauses inside the SQL select query
for (int i = 0; i < queryWords.Length; i++)
{
selectClause += "(Name LIKE '%" + queryWords[i] + "%')";
if (i < queryWords.Length - 1)
selectClause += " AND ";
}
// Initiate the query and get the appropriate results
IEnumerable<SumberRejekiProgram.Code.Product> resultQuery = dbProduct.ExecuteQuery<SumberRejekiProgram.Code.Product>(selectClause);
var finalResult = from p in resultQuery
select new { Name = p.Name, Price = p.Price, Unit = p.Unit.Name };
// Bind the DataGridView according to the final query result in resultQuery variable
dgvSearch.DataSource = resultQuery;
When I debug the code, both "resultQuery" and "finalResult" contain the results that I want. However, when I set the "dgvSearch.DataSource", the results do not appear in the row even if I tried both dgvSearch.DataSource = resultQuery and dgvSearch.DataSource = finalResult. The DataGridView is just empty (except the column).
I debug "dgvSearch" after the code execution to make sure DataSource works properly and it does. All the results are inside DataSource, but somehow the DataGridView won't display it although I have called dgvSearch.show().
Can anyone help me on this? I feel like I want to kill myself T_T.
Thank you so much in advance.
The problem is that you've set your LINQ query up to run but you haven't actually executed it yet, even when you attempt to bind its results to your DataGridView. To execute your LINQ query you need to 'touch' the query results, such as in a for loop or by calling ToList(). This is called 'deferred execution.'
So change the line where you bind to this and it should work (assuming your code is otherwise correct):
dgvSearch.DataSource = resultQuery.ToList();
Are you calling dgvSearch.DataBind() after you set its DataSource property?
Also, using string concatenation directly in your SQL is very, very bad (do a quick search for "SQL Injection"). Either make your query parameterized or roll that into your Linq query. Depending on the specific provider you're using, this may or may not work:
// Initiate the query and get the appropriate results
var resultQuery = from p in dbProduct.Product
select new { Name = p.Name, Price = p.Price, Unit = p.Unit.Name };
// Add the where clauses
for (int i = 0; i < queryWords.Length; i++)
{
resultQuery = resultQuery.Where(p => p.Name.Contains(queryWords[i]));
}
// Bind the DataGridView according to the final query result in resultQuery variable
dgvSearch.DataSource = resultQuery;
dgvSearch.DataBind();
You should try calling the Page.DataBind method after you set the datasource.
You might also try using a bindingsource as documented here:
http://msdn.microsoft.com/en-us/library/fbk67b6z.aspx#Y210

Categories

Resources