LINQ to SQL in Visual Studio, InvalidCastException with no call stack - sql

I'm having a really hard time debugging some LINQ to SQL code because there is no call stack and I have no idea where the error is occurring. It doesn't seem to be a SQL error - it's in C#, but through searching around and everything else, I can't find any way to figure out which calculation is giving the error.
The source code is below and you can see the error that is thrown when I try to insert some new rows. The call stack is empty and the exception contains no information about what went wrong.
var groups =
from record in startTimes
group record by record.startTime
into g
select new
{
startTime = g.Key.GetValueOrDefault(),
totalTasks = g.Count(),
totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(),
minDwell = g.Min(o => o.record.dwellTime).GetValueOrDefault(),
maxDwell = g.Max(o => o.record.dwellTime).GetValueOrDefault(),
avgDwell = g.Average(o => o.record.dwellTime).GetValueOrDefault(),
stdevDwell = g.Select(o => Convert.ToDouble(o.record.dwellTime)).StdDev(),
correct80 = g.Sum( o => o.record.correct80.GetValueOrDefault() ? 1 : 0),
wrong80 = g.Sum(o => o.record.wrong80.GetValueOrDefault() ? 1 : 0)
};
var statistics = groups.AsEnumerable().Select(
g => new e_activeSession()
{
workerId = wcopy,
startTime = g.startTime,
totalTasks = g.totalTasks,
totalTime = g.totalTime,
minDwell = g.minDwell,
maxDwell = g.maxDwell,
avgDwell = g.avgDwell,
stdevDwell = g.stdevDwell,
total80 = g.correct80 + g.wrong80,
correct80 = g.correct80,
percent80 = g.correct80 / (g.correct80 + g.wrong80)
}
);
// Put these rows into the table
_gzClasses.e_activeSessions.InsertAllOnSubmit(statistics);
_gzClasses.SubmitChanges();
Here's the stack trace for the exception. If anyone can decipher it, please tell me, because I have no clue how to read these anonymous types...
at System.Data.SqlClient.SqlBuffer.get_Double()
at System.Data.SqlClient.SqlDataReader.GetDouble(Int32 i)
at Read_<>f__AnonymousType2`9(ObjectMaterializer`1 )
at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at System.Data.Linq.Table`1.InsertAllOnSubmit[TSubEntity](IEnumerable`1 entities)
Also, I've run this query in LINQPad and got the same error and stack trace. Finally, here's the database query generated by LINQ to SQL that was executed right before the error. I don't see any double conversions in it at all.
SELECT COALESCE([t5].[value22],'1/1/0001 12:00:00 AM') AS [startTime], [t5].[value] AS [totalTasks], COALESCE([t5].[value2],0) AS [totalTime], COALESCE([t5].[value3],0) AS [minDwell], COALESCE([t5].[value4],0) AS [maxDwell], COALESCE([t5].[value5],0) AS [avgDwell], [t5].[value6] AS [correct80], [t5].[value7] AS [wrong80], [t5].[value22]
FROM (
SELECT COUNT(*) AS [value], MAX([t4].[timeInSession]) AS [value2], MIN([t4].[dwellTime]) AS [value3], MAX([t4].[dwellTime]) AS [value4], AVG([t4].[dwellTime]) AS [value5], SUM([t4].[value3]) AS [value6], SUM([t4].[value]) AS [value7], [t4].[value2] AS [value22]
FROM (
SELECT
(CASE
WHEN (COALESCE([t3].[wrong80],0)) = 1 THEN #p4
ELSE #p5
END) AS [value], [t3].[workerID], [t3].[value2], [t3].[timeInSession], [t3].[dwellTime], [t3].[value] AS [value3]
FROM (
SELECT
(CASE
WHEN (COALESCE([t2].[correct80],0)) = 1 THEN #p2
ELSE #p3
END) AS [value], [t2].[wrong80], [t2].[workerID], [t2].[value] AS [value2], [t2].[timeInSession], [t2].[dwellTime]
FROM (
SELECT (
SELECT MAX([t1].[timeStamp])
FROM [dbo].[workerLog] AS [t1]
WHERE ([t1].[dwellTime] IS NULL) AND ([t1].[timeInSession] = #p0) AND ([t1].[workerID] = #p1) AND ([t1].[timeStamp] <= [t0].[timeStamp])
) AS [value], [t0].[workerID], [t0].[dwellTime], [t0].[timeInSession], [t0].[correct80], [t0].[wrong80]
FROM [dbo].[workerLog] AS [t0]
) AS [t2]
) AS [t3]
) AS [t4]
WHERE [t4].[workerID] = #p6
GROUP BY [t4].[value2]
) AS [t5]

Let's read that call stack:
at System.Data.SqlClient.SqlBuffer.get_Double()
at System.Data.SqlClient.SqlDataReader.GetDouble(Int32 i)
at Read_<>f__AnonymousType2`9(ObjectMaterializer`1 )
at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at System.Data.Linq.Table`1.InsertAllOnSubmit[TSubEntity](IEnumerable`1 entities)
InsertAllOnSubmit is the top of the stack. It called ToList, which called List's "ctor" which means constructor.
That List constructor enumerates its parameter (a deferred query), causing the query to be resolved.
During the enumeration in the List constructor, we try to move to the next element in the query (WhereSelectEnumerableIterator.MoveNext) - this is an enumerator created by your call to Select after AsEnumerable. In order to move to the new element, what we actually need to do is drill deeper into the query (ObjectReader.MoveNext), which is going to create some object from the database results.
At this point, I'd like to point out that you didn't say the type for startTimes. I'm guessing startTimes is a LinqToSql query
So, that database query was turned into a DataReader (not captured on the stack), and we are extracting a row from the DataReader (Read_<>f__AnonymousType29(ObjectMaterializer1 )) so we can turn that row into the .net instance that is the result of the query.
In the process of extracting values from the row, there's a double value involved, so we try to extract that (.GetDouble(Int32 i) and .get_Double()), . Now, we know that a DataReader can think of its values as an object[] (reference SqlDataReader.GetValues). It seems reasonable that GetDouble might be implemented something like this:
//rubbish implementation, the point is - a cast from object to double is made
public double GetDouble(Int32 i) {
double result = (double) this.GetValues()[i];
return result;
}
So, the object in the data reader's array doesn't cast to double. The most likely posibility is that the object is null!
It is also remotely possible that some other type is present (string or byte[] or whatever), but if you used LinqToSql's designer to make a dbml file, and the tables in the database still agree with that dbml, this is unlikely.
I see that Convert.ToDouble is not in the call stack. This must have been done before - in the database. Convert.ToDouble is -never actually called-... instead it is translated to sql and run there. The rules for type conversion are different in the database.
If you try to convert null to a double in .net, you get an exception.
If you try to convert null to a double in sql, you get a null.

Ok, here is what I see, the 2nd line in the exception says
at System.Data.SqlClient.SqlDataReader.GetDouble(Int32 i)...
In your code you only convert to double once:
stdevDwell = g.Select(o => Convert.ToDouble(o.record.dwellTime)).StdDev(),
Try commenting out the g.Select(...) on that line and see if you still get the exception.

Related

A remote access exception in DolphinDB,Can't find the object with name loadTable('dfs://zctestDB','trainInfoTable')

I want to remotely query the database in DolphinDB.The database is created on the server 38.124.2.173 with the following script in the server ,
tableSchema = table(100:0,`trainID`ts`tag01`tag02,`tag03,[INT,TIMESTAMP,FLOAT,FLOAT,FLOAT )
db1 = database("",VALUE,(today()-92)..(today()+60))
db2 = database("",RANGE,0..80*10+1)
db = database("dfs://zctestDB",COMPO,[db1,db2])
dfsTable = db.createPartitionedTable(tableSchema,"trainInfoTable",`ts`trainID)
My query code as below,
def testParallelQuery( connVector,trainIDs,startTime, endTime ){
cols=`trainID`ts`tag01
whereConditions=[<trainID in trainIDs>,expr(sqlCol(`ts),between,startTime:endTime)]
script=sql(sqlCol(cols),"loadTable('dfs://zctestDB','trainInfoTable')",whereConditions)
return ploop(remoteRun{,script}, connVector)
}
host="38.124.2.173"
port=30599
connVector = loop(xdb, take(host, 10), port, "admin", "123456")
testParallelQuery( connVector,1..5,2019.06.14T00:00:00.000, 2019.06.14T01:00:00.000 )
The following exception occurred after I ran it,
Error was raised when execution : Can't find the object with name loadTable('dfs://zctestDB','trainInfoTable')
How can I solve this problem?
Function sql helps one construct a sql statement dynamically. The parameter from accepts three types of data: (1) a table object, (2) an expression representing a table or a table join, (3) a variable associated with a table object.
In your particular case, please pass an expression to from as follows.
def runSQL(trainIDs, startTime, endTime){
cols = `trainID`ts`tag01
whereConditions = [<trainID in trainIDs>, expr(sqlCol(`ts), between, startTime:endTime)]
return sql(sqlCol(cols), loadTable('dfs://zctestDB','trainInfoTable'), whereConditions).eval()
}
def testParallelQuery( connVector,trainIDs,startTime, endTime ){
return ploop(remoteRun{,runSQL{trainIDs, startTime, endTime}}, connVector)
}
host="38.124.2.173"
port=30599
connVector = loop(xdb, take(host, 10), port, "admin", "123456")
testParallelQuery( connVector,1..5,2019.06.14T00:00:00.000, 2019.06.14T01:00:00.000 )

LINQ Does not contain a definition for 'union'

what is wrong with this linq query that show me the error of Does not contain a definition for 'union'
(from rev in db.vM29s
where Years.Contains(rev.FinancialYear) && rev.Exclude=="No"
group rev by new { rev.RevenueCode, rev.FinancialYear } into g
select new
{
Revenuecode = g.Key.RevenueCode,
totalRevenue = g.Sum(x => x.RevenueAmount),
RevenueEnglish = (from a in db.RevenueCodes where a._RevenueCode == g.Key.RevenueCode select a.RevenueEng).FirstOrDefault(),
// MinorCode = (from a in db.MinorCodes where a._MinorCode == g.Key.MinorCode select a.MinorEng),
RevenueDari = (from a in db.RevenueCodes where a._RevenueCode == g.Key.RevenueCode select a.RevenueDari).FirstOrDefault(),
Yearss = g.Key.FinancialYear
}
)
.Union
(from u in db.rtastable
where Years.Contains(u.Year)
group u by new { u.objectcode, u.Year } into g
select new
{
Revenuecode = g.Key.objectcode,
totalRevenue = g.Sum(x => x.amount),
RevenueEnglish = (from a in db.RevenueCodes where a._RevenueCode == g.Key.objectcode select a.RevenueEng).FirstOrDefault(),
// MinorCode = (from a in db.MinorCodes where a._MinorCode == g.Key.MinorCode select a.MinorEng),
RevenueDari = (from a in db.RevenueCodes where a._RevenueCode == g.Key.objectcode select a.RevenueDari).FirstOrDefault(),
Yearss = g.Key.Year
}
)
.ToList();
If you included using System.Linq; and both Anonymous Types have exactly the same property names + property types, then what you did should work.
Yet it does not work. The solution is to check your Anonymous Types for subtle property name differences and/or subtle property type differences.
E.g. even an int vs a smallint or double or decimal will cause this build error:
'System.Collections.Generic.IEnumerable<AnonymousType#1>' does not contain a definition for 'Union' and the best extension method overload 'System.Linq.Queryable.Union(System.Linq.IQueryable, System.Collections.Generic.IEnumerable)' has some invalid arguments
Switching to .Concat() will not fix this: it has the same (obvious) restriction that the types on both sides must be compatible.
After you fix the naming or typing problem, I would recommend that you consider switching to .Concat(). The reason: .Union() will call .Equals() on all objects to eliminate duplicates, but that is pointless because no two Anonymous Objects that were created independently will ever be the same object (even if their contents would be the same).
If it was your specific intention to eliminate duplicates, then you need to create a class that holds your data and that implements .Equals() in a way that makes sense.
You should use Concat or using addRange if the data is allready in memory.

MS Access ##identity query issue

Can someone please let me know the issue with the below query? I am running on MS Access and its giving
Syntax error in query expression 'id = ##IDENTITY'
Code:
public DosageBO SaveDosage(DosageBO dosage)
{
try
{
using (IDbConnection connection = OpenConnection())
{
StringBuilder sql = new StringBuilder();
sql.AppendLine("INSERT INTO dosage_master ( medicine_type, dosage, remarks, updateby, updatedate )");
sql.AppendLine("VALUES (#type, #dose, #remarks, #updateby, NOW());");
var parameters = new
{
type = dosage.MedicineType,
dose = dosage.Dosage,
remarks = dosage.Remarks,
updateby = Environment.UserName
};
connection.Execute(sql.ToString(), parameters);
return connection.Query<DosageBO>("SELECT medicine_type as MedicineType, dosage, remarks FROM dosage_master WHERE id = ##IDENTITY").FirstOrDefault();
}
}
catch
{
throw;
}
}
SELECT ##Identity is a specialized query. And ##Identity is only valid in that context. If you attempt to use ##Identity elsewhere, as in a WHERE clause, the db engine will throw an error.
You will have to retrieve the value from SELECT ##Identity, save it, and then use that saved value in your other query.
Remove the ) at the end
WHERE id = ##IDENTITY)
^---here
Are you inserting a row in this batch prior to the select query?
To my knowledge ##IDENTITY is only available directly after inserting a row causing an identity value to be generated i.e an insert to an autoincremental identity column.
Edit again:
Try enclosing it in a subquery e.g id = (SELECT ##IDENTITY)

How can I preempt a "Specified cast is not valid" exception?

In querying an MS Access database in a Web API app, I get a "Specified cast is not valid" exception if I'm trying to assign an empty string to a variable. IOW, when this code:
var accountID = oleDbD8aReader.GetInt16(0);
var platypusName = oleDbD8aReader.GetString(1);
...reaches a record where the second column in the result set contains an empty/null string, it bombs.
So, I thought I could head that off at the pass like this:
string platypusName;
var accountID = oleDbD8aReader.GetInt16(0);
if (!string.IsNullOrEmpty(oleDbD8aReader.GetString(1)))
{
platypusName = oleDbD8aReader.GetString(1);
}
else
{
platypusName = string.Empty;
}
...but it doesn't work - I still get a "Specified cast is not valid" exception.
How can I safely check for an empty string/null value there and ignore that pass through the result set so that it will get subsequent records?
Or can I preclude this by changing the SQL statement to exclude empty/null strings from the result set? If so, how? The query is in this format:
SELECT td_duckbill_accounts.platypus_no, t_accounts.name
FROM t_accounts
INNER JOIN td_duckbill_accounts ON t_accounts.account_no = td_duckbill_accounts.account_no
ORDER BY td_duckbill_accounts.platypus_no
I think having the query return empty string is easy solution:
SELECT td_duckbill_accounts.platypus_no,
IIF(ISNULL(t_accounts.name),'',t_accounts.name) AS name
FROM t_accounts
INNER JOIN td_duckbill_accounts ON t_accounts.account_no = td_duckbill_accounts.account_no
ORDER BY td_duckbill_accounts.platypus_no
This should also work but I can't test it right now:
SELECT td_duckbill_accounts.platypus_no,
Nz(t_accounts.name,'') AS name
FROM t_accounts
INNER JOIN td_duckbill_accounts ON t_accounts.account_no = td_duckbill_accounts.account_no
ORDER BY td_duckbill_accounts.platypus_no
Sometimes what is needed is to change the data type method used in OleDbDataReader, as this code and the comments show:
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
string accountId = oleDbD8aReader.GetString(0);
string accountName = oleDbD8aReader.GetString(1);
//int useOnItems = oleDbD8aReader.GetInt32(2); <= get "specified cast not valid" with Int32
int useOnItems = oleDbD8aReader.GetInt16(2); // This works
expenses.Add(new Expense { account_id = accountId, name = accountName, use_on_items = useOnItems });
}
The data type in the underlying table is Integer, so I guess for LongInteger (another Access integer type) GetInt32() would be the OleDbDataReader method to use.

How can I create a DateTime value in a Linq Query?

I am trying to create a query in Linq to Entities. I want it to return objects that include a DateTime property derived from strings in the table I am querying. The data (in SQL Server) has a string date field (in the database it appears as VARCHAR(8)) called date_occurred. It has a String time field (varchar(6)) called time_occurred.
An example of the contents of date_occurred is "20131007" to represent Oct. 7, 2013. An example of the contents of time_occurred is "145710" to mean 10 seconds after 2:57pm.
I have tried two methods that don't work:
Dim ATQuery = From e In EntityContext.vwSecAppAuditToday
Order By e.date_occurred, e.time_occurred
Select New AuditEntry With {
.EventTime = DateTime.ParseExact(Trim(e.date_occurred) & Trim(e.time_occurred), "yyyyMMddHHmmss", CultureInfo.InvariantCulture),
.ServerName = e.server_name
}
This throws a NotSupportedException with a message stating: "LINQ to Entities does not recognize the method 'System.DateTime ParseExact(System.String, System.String, System.IFormatProvider)' method, and this method cannot be translated into a store expression."
Before that, I tried:
Dim ATQuery = From e In EntityContext.vwSecAppAuditToday
Order By e.date_occurred, e.time_occurred
Select New AuditEntry With {
.EventTime = New DateTime(Integer.Parse(e.date_occurred.Substring(0, 4)),
Integer.Parse(e.date_occurred.Substring(4, 2)),
Integer.Parse(e.date_occurred.Substring(6, 2)),
Integer.Parse(e.time_occurred.Substring(0, 2)),
Integer.Parse(e.time_occurred.Substring(2, 2)),
Integer.Parse(e.time_occurred.Substring(4, 2))),
.ServerName = e.server_name
}
This also throws a NotSupportedException. In this case, the message states: "Only parameterless constructors and initializers are supported in LINQ to Entities."
Is what I am trying to do possible using Linq to Entities?
Edit: Comment Alert
For those who read this post later, Moho and Matt Johnson have made especially helpful comments. I have marked these with +1.
Select an anonymous class that contains the fields of interest (called a projection), then create DateTime struct per item after the IQueryable has been enumerated:
Dim ATQuery = From e In EntityContext.vwSecAppAuditToday
Order By e.date_occurred, e.time_occurred
Select New With {
.DateOccurred = e.date_occurred,
.TimeOccurred = e.time_occurred,
.ServerName = e.server_name
}
Dim q2 = From e In ATQuery.ToArray()
Select New AuditEntry With {
.EventTime = DateTime.ParseExact(Trim(e.DateOccurred) & Trim(e.TimeOccurred), "yyyyMMddHHmmss", CultureInfo.InvariantCulture),
.ServerName = e.ServerName
}
Your New DateTime contains only integer, so it looks like 20131008011300 (for 2013/10/08 01:13:00)
/ between date, : between time and a space between date and time are missed