Dynamically select data runtime from entity framework 6.0 throws exception - c#

I would like to selected dynamic data from entity framework as per below.
if I select "Select * from TableName" in queryString it works fine but if I select only selected column/columns it does not work and throws an exception.
Working ok
string queryString = #"SELECT * FROM context.TableName ";
DbSqlQuery<SampleTable> result = context.SampleTable.SqlQuery(queryString);
Throws Exception
columnList is generated run time from SampleTable which may consists one or more columns.
string queryString = #"SELECT " + String.Join(",", columnList) + " FROM context.TableName ";
DbSqlQuery<SampleTable> result = context.SampleTable.SqlQuery(queryString);
Exception:
The data reader is incompatible with the specified 'context.TableName ... A member of the type, does not have a corresponding column in the data reader with the same name.
As per my understanding EF try to map all columns but above query does not have same number of columns as set in code and so it throws exception.

You can check the below steps :
Use the alias names of columns in your columnList (ex: tablename.Id instead of Id).
That all the column names in your columnList are mapped in the Entity.
Check that the column names in your columnList are same as of the Entity.
Hope this will help.

Related

SqlDataReader's Read() function returns empty row, same query returns value from database browser

I have a table in SQLite with the following structure:
+-------+------+------+
| id |name |value |
|int |text |int |
+-------+------+------+
I am attempting to follow this guide on using sqlite databases in Unity. I am able to query the sqlite_master table to retrieve the only database table name using the following code:
_dbConnection = (IDbConnection)new SqliteConnection(_dbURI);
_dbConnection.Open();
string sqlStatement = "SELECT name FROM sqlite_master WHERE type='table' AND name='" + tableName + "';";
_dbCommand = _dbConnection.CreateCommand();
_dbCommand.CommandText = sqlStatement;
_dbReader = _dbCommand.ExecuteReader();
while (_dbReader.Read())
{
Debug.Log("Table: " + _dbReader.GetString(0));
}
However, when I attempt to query from the table itself to get the maximum ID using similar code my DataReader returns a null row:
_dbConnection = (IDbConnection)new SqliteConnection(_dbURI);
_dbConnection.Open();
string sqlStatement = "SELECT max(id) FROM " + tableName + ";";
_dbCommand = _dbConnection.CreateCommand();
_dbCommand.CommandText = sqlStatement;
_dbReader = _dbCommand.ExecuteReader();
while (_dbReader.Read())
{
Debug.Log("MaxId = " + _dbReader.GetInt32(0));
}
The null causes a conversion error with the call to _dbReader.GetInt32(0). When I write the sql statement to the log and paste it into my database browser it yields a number, so the query should be returning a value.
I'm not clear on why similar code works when querying sqlite_master but not my other table.
I've also tried ExecuteScalar with no success.
Thanks!
EDIT:
tableName is equal to "unit_def"
error message is
InvalidCastException: Cannot cast from source type to destination type.
Mono.Data.Sqlite.SqliteDataReader.VerifyType (Int32 i, DbType typ)
using count(*) yields a 0, so it seems that unity can't see the data in the table?
Also, oddly, changing the code messed up the GUI elements in my editor for some reason.
I solved the issue, hopefully this answer is helpful to others:
The issue was tricky to troubleshoot since
the correct database was being referenced
all code was correct
the sql query did return a result when run in my database browser
The problem is that I am using the DB Browser for Sqlite, which I am new to. I had written rows of data to the database so that my query would return results within the browser, but apparently these changes are not written to the database file for other applications to see until the "Write Changes" button is pushed.
Hopefully this helps anyone else who might be new to the db browser and stumbles into this error!

Copy a database table to another database table with a different structure

I am aware this was asked here. However it doesn't answer my question. I have 10 tables in a database called "merged". I am taking a bunch of other databases with an identical structure as "merged" except that "merged" has an extra column that is a combination of two columns in the table. I am trying to transfer all this data into merged but I believe the extra column I believe is preventing the transfer.
SqlCommand MergeDB = connDestination.CreateCommand();
string sqlMergeDB = "";
int a= 0;
for (a=0; a < tablenames.Length; a++){
string sqlMergeDB = "INSERT INTO sql_merged.dbo." + tablenames[a] + " SELECT * FROM sourceForMerged.dbo." + tablenames[a];
using (SqlDataReader reader = MergeDB.ExecuteReader()) {
while(reader.Read())
{
MessageBox.Show("Transfered a table");
}
}
}
The error occurs at the SqlDataReader row of the code above, which I believe means there is something wrong with the sql command. Please help. Thanks
If you name all the columns in both parts of the INSERT . . . SELECT statement you can map which source column gets inserted into which destination column.
If you imagine TargetTable (Name, ProductType, Date) and SourceTable (Date, Type, Name) then using:
INSERT INTO TargetTable (Name, ProductType, Date)
SELECT Name, Type, Date FROM SourceTable
would move the three columns into the appropriate columns even though the order doesn't match.
If you have "extra" columns in one table or the other you can either leave them out or provide expressions to fill them in:
INSERT INTO TargetTable (Name, ProductType, Date, CombinedValues)
SELECT Name, Type, Date, (ValCol1 + ' ' + ValCol2) FROM SourceTable
has four columns receiving data from four expressions, one of which concatenates two columns worth of data. (In real life, you may find that the concatenation expression is more complicated, but this is the basic idea).
You cannot use a:
Insert Into [Table] Select * From [Table2]
unless the tables schemas are identical. You would have to list out the columns for the Insert Statement.
If possible you could drop the column on the destination table and then add it back after the insert.
You could do something like this to build up you insert code if the table is very wide:
SELECT
'cmd.Parameter.Add("#' + column_name + '", SqlDbType.' + data_type + ');',
column_name 'Column Name',
data_type 'Data Type'
FROM information_schema.columns
WHERE table_name = 'TableName'

Column name is number, select it with oledb

Similar question: Pass number as a column name in select statement of Sql
I have a column in a csv file that has a name of 0.000. How do I select it with a oledb select statement? Currently I have:
StringBuilder sbSelectItems = new StringBuilder();
sbSelectItems.Append("location_c, ");
sbSelectItems.Append("impb_, ");
sbSelectItems.Append("order_id, ");
sbSelectItems.Append(" `0.000` as shipCost, ");
sbSelectItems.Append("transmitta, ");
sbSelectItems.Append("piecelb ");
string sSelectStatement = "SELECT " + sbSelectItems.ToString() + " FROM [" + sFileName + "]";
but I get an error that '' is not a valid column. I've tried the [0.000], '0.000', "0.000" and what I have currently and I get the literal values or an error thrown for an invalid column. The file is auto generated through a program I don't have access to so I can't change the column name.
UPDATE
Trying the example from the first answer I got an error that said No value given for one or more required parameters. So I was confused and did a SELECT * FROM... and the column name, when I did that the column name, was tr110308#csv.01. I tried to select the value then doing tr110308csv#csv.01 but I was not able to.
Also using 0#000 didn't work...
#62 is the column I want.
Try changing your column name in your "select" statement to "0#000". Here is a sample which attempts to reproduce and then fix your issue.
Given a CSV file with the following content:
Foo,Bar,100.0,200
Alpha,Happy,8,5
Beta,Sad,19,2
A Select statement of the form
"Select Foo, `100.0` From "
Receives an OleDbException with the message...
'' is not a valid name. Make sure that it does not include invalid characters or punctuation and that it is not too long.
..which matches the error you received (or, I assume it does, you abbreviated and altered the message).
Changing the select to "100#0" was able to sidestep the issue.
Full repro code:
string fileName = "C:\\Temp\\test.csv";
string connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"TEXT;HDR=YES;FMT=Delimited\";", Path.GetDirectoryName(fileName));
using (var connection = new System.Data.OleDb.OleDbConnection(connectionString))
{
string sql = "Select Foo, `100#0` From " + Path.GetFileName(fileName);
using (var adapter = new System.Data.OleDb.OleDbDataAdapter(sql, connection))
{
var table = new DataTable();
var result = adapter.Fill(table);
table.Dump(); // LinqPad method to display result for verification
}
}
You'll note the column name in the output matches the select statement (although you may still alias it, as your original SQL attempts to do). Indeed, the method of discovery here was to simply perform a "Select * From ..." and inspect the output. I found no source material beforehand, so interesting question!
Edit: With your update, the same approach is applicable. The name "tr110308csv#csv.01" is not legal for the select statement, but the name "tr110308csv#csv#01" is. Renaming the column (to match yours) in the test file and then using the altered version in the code produces the desired output, and I recommend you attempt it in your code, as well.

auto SQL field's quotation marks by type in c#

Suppose that I want to create an SQL SELECT statement dynamically with reflection on primary key. I search in the table for primary keys and then, I make the statement.
Problem is, I don't know the type of fields that compose the primary key before getting them. So, if it's a string or date, I must add quotation marks but not if it's an int.
Atm, I am doing like that :
var type = field.GetType().Name;
if (type.ToLower().StartsWith("string") || type.ToLower().StartsWith("date"))
{
field = "\"" + field + "\"";
} else if (type.ToLower().StartsWith("char"))
{
field = "\'" + field + "\'";
}
With this code, I can handle some SQL types but there are a lot more.
My problem is that it's combined with LinQ. I got a DataContext object and a generic type table from the context. And context.ExecuteQuery only allows parameters to be passed has values. I also tried with Dynamic LinQ but I got the same problem
Does anyone know a better solution?
That is simply the wrong way to write SQL. Parameterize it and all these problems evaporate (as do problems with "which date format to use", etc. And of course the biggie: SQL injection.
Then it just becomes a case of adding #whatever into the TSQL, and using cmd.Parameters.AddWithValue("whatever", field) (or similar).
Update (from comments): since you mention you are using DataContext.ExecuteQuery, this becomes easier: that method is fully parameterized using the string.Format convention, i.e.
object field = ...;
var obj = db.ExecuteQuery<SomeType>(
"select * from SomeTable where Id = {0}", field).ToList(); // or Single etc
No string conversions necessary.
(the last parameter is a params object[], so you can either pass multiple discreet terms, or you can populate an object[] and pass that, if the number of terms is not fixed at compile-time; each term in the array maps by (zero-based) index to the {0}, {1}, {2}... etc token in the query)
Have you tried with parameters? For instance if you are using SQLServer as a database and you want to do this query:
"SELECT * FROM someTable WHERE id = " + field;
Then you should use sometething like this:
"SELECT * FROM someTable WHERE id = #field"
and add parameter to your command:
SqlParameter param1 = new SqlParameter("#field", field);
command.Parameters.Add(param1);
EDIT: Watch out that for different database providers the syntax for the SQL query is different, the same for the Access would be
"SELECT * FROM someTable WHERE id = ?";
command.Parameters.AddWithValue("field", field);

I have inserted a row, I want to get it's ID and plus it with an int and insert in that row

I have inserted a row into my table, and I want to get it's ID and plus it with an int and inserted in that row.
But I don't know how to get it's ID.
Here is the insert code:
objCommand.Connection = objConnection;
objCommand.CommandText = "INSERT INTO Moin " +
" (Title, TotalID, Code ) " +
"VALUES (#Title , #TotalID, #Code )";
objCommand.Connection = objConnection;
objCommand.CommandText = "INSERT INTO Moin " +
" (Title, TotalID, Code ) " +
"VALUES (#Title , #TotalID, #Code ) SELECT SCOPE_IDENTITY()";
object id = objCommand.ExecuteScalar();
Try using the OUTPUT clause of SQL Server in your query - it can return any of the just inserted value (here I'm assuming your column is called ID - adapt as needed):
objCommand.Connection = objConnection;
objCommand.CommandText = "INSERT INTO Moin(Title, TotalID, Code ) " +
"OUTPUT Inserted.ID " +
"VALUES (#Title , #TotalID, #Code ); "
and then execute it like this:
int result = (int)objCommand.ExecuteScalar();
Since you're returning just one row and one column (just the INT), you can use .ExecuteScalar() to retrieve that value back from the INSERT statement.
With the OUTPUT clause, you can return any values just inserted - not just the identity column. So you could also return values that are filled by the database with default values, or whatever you need. If you return multiple values, you need to use a data reader to read them all - ExecuteScalar() only works for a single value.
But, as Anders correctly mentioned - using an ORM like Entity Framework would do all of this automatically for you and you wouldn't have to deal with those raw SQL commands anymore....
Building SQL commands in strings should be considered a legacy technique. If you use Entity Framework or linq-to-sql the retrieval of the id is handled automatically for you.
With pure SQL, use the SCOPE_IDENTITY() function to retrieve the id of the inserted element.

Categories