I am checking out v1.25 of Dapper with Sqlite via System.Data.Sqlite. If I run this query:
var rowCount = dbc.Query<int>("SELECT COUNT(*) AS RowCount FROM Data").Single();
I get the following error: System.InvalidCastException: Specified cast is not valid
This is because Sqlite returns the above value as an Int64, which I can verify with the following code. This will throw "Int64":
var row = dbc.Query("SELECT COUNT(*) AS RowCount FROM Data").Single();
Type t = row.RowCount.GetType();
throw new System.Exception(t.FullName);
Now, the following code will actually handle the downward conversion from Int64 to Int32:
public class QuerySummary
{
public int RecordCount { get; set; }
}
var qs = dbc.Query<QuerySummary>("SELECT COUNT(*) AS RecordCount FROM Data").Single();
rowCount = qs.RecordCount;
throw new System.Exception(rowCount.ToString());
When I throw this exception, it gives me the actual row count, indicating that Dapper handled the conversion for me.
My question is, why is it that dbc.Query<int> does not handle the downward conversion in a similar way to dbc.Query<QuerySummary>? Is this intended behavior?
No, that is not intentional. I've committed and pushed changes to github which make the following pass (it fails on 1.25); it should appear on NuGet at some point soon too:
// http://stackoverflow.com/q/23696254/23354
public void DownwardIntegerConversion()
{
const string sql = "select cast(42 as bigint) as Value";
int i = connection.Query<HasInt32>(sql).Single().Value;
Assert.IsEqualTo(42, i);
i = connection.Query<int>(sql).Single();
Assert.IsEqualTo(42, i);
}
Related
Just started learning Dapper. I have an ADO.NET background. Using a demo I downloaded, I can insert/delete data from a webform into a MySql table just fine. This, however, I have searched all morning on.
In retrieving a single row from the db by ID, it doesn't return a LIST<>, it seems to be just an object (using code from the demo I downloaded). The query works, I get the object back. It has the fields: "ProductID, Description and Price".
The only way I could get the values to those three fields was like this:
System.Reflection.PropertyInfo pi = Product.GetType().GetProperty("ProductID");
System.Reflection.PropertyInfo desc = Product.GetType().GetProperty("Description");
System.Reflection.PropertyInfo price = Product.GetType().GetProperty("Price");
int _ProductID = (int)(pi.GetValue(Product, null));
string _Description = (string)(desc.GetValue(Product, null));
decimal _Price = (decimal)(price.GetValue(Product, null));
This works and gets the correct values for the three fields.
I'm used to looping through DataTables, but I just think there is probably a better way to get those values.
Is this the correct way to do this or am I missing something? I did actually read documentation and mess with this all morning before asking, too.
Some of the things I looked at seem to be very complex. I thought Dapper was supposed to simplify things.
OK, Thanks Marc. It was difficult for me to see what was supposed to be in the Dapper class files and what was supposed to be in my code behind. The original demo way of getting a product by ID had the query as .FirstOrDefault();
I changed everything to return a List<> and it all worked. I'm sure my ADO.NET is showing, but this works. In Dapper class files:
public List<Product> ProductAsList(int Id)
{
return this._db.Query<Product>("SELECT * FROM Cart_product WHERE ProductID=#Id", new { Id = Id }).**ToList()**;
}
This is just getting one row that matched the ProductID.
In page codebehind:
protected void CartItemAdd(string ProductId) // passing it the selected ProductID
{
var results = cartservice.ProductAsList(Convert.ToInt32(ProductId));
// returns that one row using Dapper ProductAsList(ProductId)
int _ProductId = 0;
string Description = string.Empty;
decimal Price = 0;
// Loop through the list and get the value of each item:
foreach (Product obj in results)
{
_ProductId = obj.ProductID;
Description = obj.Description;
Price = obj.Price;
}
// Using Dapper to insert the selected product into the shopping cart (table):
String UserName = "jbanks";
cartitem = new CartItem();
cartitem.ProductID = _ProductId;
cartitem.Quantity = 1;
cartitem.Description = Description;
cartitem.Price = Price;
cartitem.Created = DateTime.Now;
cartitem.CreatedBy = UserName;
result = cartservice.AddCartItem(cartitem);
if (result)
{
lblMessage.Text = string.Empty;
lblMessage.Text = "Successfully added a cart item";
}
}
}
It does indeed look up the product from one table and insert a selected item into another table.
Thanks again!
The main Query<T> API returns an IEnumerable<T>, which often will be a List<T>; the AsList<T>() extension method can get it back to a list without a copy, but either way: they are just T, for whatever T you asked for. If you asked for Query<Product>, then: they should be Product instances:
var results = connection.Query<Product>(someSql, someArgs); // perhaps .AsList()
foreach (Product obj in results) { // "var obj" would be fine here too
// now just use obj.ProductID, obj.Description and obj.Price
}
If that didn't work: check that you used the <T> version of Query. There is a non-generic variant too, which returns dynamic. Frankly, you should almost always use the <T> version.
Note: I'm assuming that somewhere you have something like
class Product {
public int ProductID {get;set;}
public string Description {get;set;}
public decimal Price {get;set;}
}
I am trying to replace HashMap used in my code to TreeMap as I need to have the keys sorted.
But just replacing the HashMap declaration with TreeMap is running into various ClassCast exception which were not arising before. I am debugging the code but I cannot follow why is it failing? It fails in the treemap.put(key, value); statement.
private static void insertIntoIndexFile(String datatype, String value, String columnName, String tableName,
long offset) {
// TODO Auto-generated method stub
Map<Object, ArrayList<Long>> index = new TreeMap<Object, ArrayList<Long>>();
Map<Object, ArrayList<Long>> result = new TreeMap<Object, ArrayList<Long>>();
try{
String indexTableFileName = SCHEMA+"."+tableName+"."+columnName+".ndx";
File indexTableFileObject = new File(indexTableFileName);
long indexfileLength = indexTableFileObject.length();
RandomAccessFile indexTableFile = new RandomAccessFile(indexTableFileObject, "rw");
boolean isValuePresent = false;
if(indexfileLength>0){
// returns sucessfully
index = getIndexFileEntries(indexTableFileName, datatype);
// checking if the key exists
Set set1 = index.entrySet();
Iterator iterator1 = set1.iterator();
while(iterator1.hasNext()) {
Map.Entry me2 = (Map.Entry)iterator1.next();
Object key = me2.getKey();
ArrayList<Long> temp = new ArrayList<Long>();
if(key.toString().equals(value)){
System.out.println("Comparing the hashmap value to value " + key.toString());
isValuePresent = true;
temp = (ArrayList<Long>) me2.getValue();
long frequency = temp.get(0);
frequency++;
temp.set(0, frequency);
temp.add(offset);
index.put(key, temp);
break;
}
}
if(isValuePresent == false){
System.out.println("The file has values but this key was not found. Here is the updated hashmap");
ArrayList<Long> temp = new ArrayList<Long>();
temp.add(Long.parseLong("01"));
temp.add(offset);
Object key = (Object)value;
index.put(key,temp); // this line throws error
writeMapToIndexFile(tableName, columnName, datatype, index);
}else{
writeMapToIndexFile(tableName,columnName, datatype, index);
}
}catch(Exception e){
e.printStackTrace();
}
}
Here is the stacktrace :
java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
at java.lang.String.compareTo(Unknown Source)
at java.util.TreeMap.put(Unknown Source)
at Database.insertIntoIndexFile(Database.java:433)
at Database.insertIntoTable(Database.java:369)
at Database.main(Database.java:1340)
Similar stacktrace for each of short, integer etc. I expect the key to be these datatypes. Therefore I have used Object type to store the keys. But I am not trying to cast them String before inserting into the map.
Is there anything that I am overlooking while replacing a hashmap with treemap. I agree its a very broad question but any help would really be great.
Thanks for your time.
I need help from All Dapper master.
I have been learning using Dapper since one month ago, but I have error when executing query using ODBC SP.
The code originally was written by someone(DapperExample) but not using ODBC, thanks to the writer I forgot your name.
My SP:
CREATE PROCEDURE SP_GET_FIND_EMPLOYEES (#EmpID INT)
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM tblEmployee WHERE EmpID = #EmpID
END
GO
My Code
public class EmployeeDashBoard : IEmployeeDashBoard
{
private IDbConnection _db;
string connStr2 = WebConfigurationManager.ConnectionStrings["DapperExample"].ConnectionString;
public EmployeeDashBoard()
{
}
public Employee Find(int id)
{
//type b, by sp
using (IDbConnection connection = new OdbcConnection(connStr2))
{
var p = new DynamicParameters();
p.Add("#EmpID", id);
Employee result = this._db.Query<Employee>("dbo.SP_GET_FIND_EMPLOYEES", new { #EmpID = id }, commandType: CommandType.StoredProcedure).Single();
return result;
}
}
}
Error:
ERROR [42000] [Microsoft][SQL Server Native Client 11.0][SQL Server]Procedure or function 'SP_GET_FIND_EMPLOYEES' expects parameter '#EmpID', which was not supplied.
Thanks in Advance.
Masa Sih
I Solved by my self, I'm using Sybase ODBC SP, ( God Job Sam ), now I can avoid entity framework in the feature.
Here the tricks:
Solved: SP_GET_FIND_EMPLOYEES ?
using (IDbConnection connection = new OdbcConnection(connStr2))
{
var p = new DynamicParameters();
p.Add("?EmpID?", id.ToString());
Employee result = this._db.Query<Employee>("dbo.SP_GET_FIND_EMPLOYEES ?", p, commandType: CommandType.StoredProcedure).Single();
return result;
}
Your implementation for ODBC named parameters is incorrect. You encase the named parameter with question marks in your statement and create the named parameter without the question marks. The question marks are used by Dapper to parse the statement to find the names.
p.Add("EmpID", id.ToString());
Employee result = this._db.Query<Employee>("dbo.SP_GET_FIND_EMPLOYEES ?EmpID?", p, commandType: CommandType.StoredProcedure).Single();
See this answer for more information: https://stackoverflow.com/a/26484944/6490042
I am trying to query for a list of ids of type Long in GAE/JDO. And I'm getting the following exception when I call detachCopyAll() on the result set.
org.datanucleus.jdo.exceptions.ClassNotPersistenceCapableException: The class "The class "java.lang.Long" is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data/annotations for the class are not found." is not persistable. This means that it either hasnt been enhanced, or that the enhanced version of the file is not in the CLASSPATH (or is hidden by an unenhanced version), or the Meta-Data for the class is not found.
at org.datanucleus.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:241)
at org.datanucleus.jdo.JDOPersistenceManager.jdoDetachCopy(JDOPersistenceManager.java:1110)
at org.datanucleus.jdo.JDOPersistenceManager.detachCopyAll(JDOPersistenceManager.java:1183)
...
I can query for a list of User objects and detach them just fine. I expected all primitive wrapper classes like Long to be persistable. What am I doing wrong? Below is the code I'm working with.
#PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true")
public class User
{
#PrimaryKey
#Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
private Long id;
private String email;
}
#SuppressWarnings("unchecked")
public static List<Long> getUserKeys(String email)
{
assert email != null;
List<Long> keyList = null;
PersistenceManager pm = null;
Query query = null;
try {
pm = PMF.get().getPersistenceManager();
query = pm.newQuery("select id from " + User.class.getName());
query.declareParameters("String emailParam");
query.setFilter("email == emailParam");
List<Long> resultList = (List<Long>) query.execute(email);
// next line causes the ClassNotPersistenceCapableException
keyList = (List<Long>) pm.detachCopyAll(resultList);
}
finally {
if (query != null) query.closeAll();
if (pm != null) pm.close();
}
return keyList;
}
List<Long> resultList = (List<Long>) query.execute(email);
// next line causes the ClassNotPersistenceCapableException
keyList = (List<Long>) pm.detachCopyAll(resultList);
I don't understand what you are doing here. A List<Long> does not have to be detached. You'd want to detach instances of your User entity class, but a Long is a Long, and you can just do whatever you need to do with the resultList.
The error message is confusing, but just caused by Long not being an entity class.
My Question is how do I handle a null set returned from a linq query if I am loading it into a custom class.
example
queryResults = queryResults.Select(p => new specialItems(p.ID, p.SECTION, p.PROGRAM, p.EVENT).ToList<specialItems>();
...
public class specialItems
{
public string Id { get; set; }
public string Section { get; set; }
public string Program { get; set; }
public string Event { get; set; }
public courseItems(string id, string section, string program, string event)
{
this.Id = id;
this.Section = section;
this.Program = program;
this.Event = event;
}
}
Currently this query works great until the result set is empty, then I get:
"Object reference not set to an instance of an object."
I need the query to return an empty List if the result set is empty.
UPDATE - Asided from the invalid redeclaration of a variable (fixed) I did find that the issue was higher up in the initial construction of the linq query. This became apparent when I received several good suggestions and removed the error. Once I fixed the original query things worked swimmingly.
Use the null coalescing operator (??).
List<specialItems> queryResults = queryResults.Select(p => new specialItems(p.ID, p.SECTION, p.PROGRAM, p.EVENT).ToList<specialItems>() ?? new List<specialItems>();
EDIT: Yeah, looking at what you have there a little closer, it's the ToList that's blowing up when this happens. You might have to split it up a bit.
var temp = queryResults.Select(p => new specialItems(p.ID, p.SECTION, p.PROGRAM, p.EVENT);
List<specialItems> results = temp == null ? new List<specialItems>() : temp.ToList<SpecialItems>();
Have to do it this way, because there's no good spot to put the null coalescing operator in this case.
Robaticus is mostly right, use the null coalescing operator (??). Howerver, since you didn't include the stack trace, I assume your code is throwing because queryResults is initially null. By the time you get to the ?? operator, you've already thrown the exception, because you tried to dereference queryResults.
Also, the code you have doesn't make a ton of sense, because queryResults is already defined within that scope by the time you get to that line. You can't redefine a variable that has already been declared locally in that scope.
List<SpecialItems> queryResults = GetSomeResults();
queryResults = (queryResults ?? new List<SpecialItems>())
.Select(p => new SpecialItems(p.ID, p.SECTION, p.PROGRAM, p.EVENT))
.ToList<SpecialItems>();
If you can get the function or line that spits out the original version of queryResults to return an empty list instead of null, then try to do that, or coalesce the results on that line. That's probably better than having all that code on the query line :)
List<SpecialItems> queryResults = GetSomeResults() ?? new List<SpecialItems>();
queryResults = queryResults
.Select(p => new SpecialItems(p.ID, p.SECTION, p.PROGRAM, p.EVENT))
.ToList<SpecialItems>();
Linq returns an empty list if there are no results, never null. Therefore, the problem is certainly not that queryResults.Select() returns null.
What is probably going on is that we're looking at a lazily evaluated linq-to-objects 'query'. ToList() triggers the evaluation of it, and probably the nullreference exception occurs in a lambda expression higher up the chain.
Neither Enumerable.Select, nor Queryable.Select, nor Enumerable.ToList return null.
The query is not realized until ToList enumerates it. During that enumeration, a null reference exception is occuring due to code you have not posted in the question.
Consider this code with and without the commented line:
List<int> source = Enumerable.Range(1, 10).ToList();
IEnumerable<int> query = null;
try
{
query = source.Where(i => 1 / i > 0);
}
catch(Exception ex)
{
Console.WriteLine("Exception was caught {0}", ex.Message);
}
// source.Add(0);
List<int> result = query.ToList();
Consider this code with and without the commented line:
DataContext myDC = new DataContext();
string name = null;
IQueryable<Person> query = myDC.Persons.Where(p => p.Name.StartsWith(name));
// name = "Zz";
List<Person> result = query.ToList();