Dapper fails when converting int to bool (Sqlite) - dapper

I have a table with IsValid column, with int datatype (Sqlite).
When selecting from it Dapper fails:
{"Error parsing column 0 (IsValid=1 - Int32)"}
{"Specified cast is not valid."}
Which makes sense, but it has to be a pretty common case - shouldn't it be accounted for ?
This is the code:
public void Test()
{
string conns = #"Data Source=mydbfile.db3;Version=3;";
string sql = null;
using (SQLiteConnection connection = new SQLiteConnection(conns))
{
connection.Open();
sql = "INSERT INTO Test (IsValid) VALUES (1)";
connection.Execute(sql);
sql = "SELECT * FROM Test";
var x = connection.Query<Valid>(sql);
}
}
private class Valid
{
public bool IsValid { get; set; }
}
Table has a single 'IsValid' column of type int.

Related

Multiple database db2 and sql server with single query

The db2 database is called panth01
the sqlserver database is called proof
As you can see in the code, I made a single query, to extract language in db2 and language in sqlserver.
The problem that in Db2 the column is called LANGUAGE
In sqlserver the column is called LINGUA
In db2 the LANGUAGE column has two words "en"
in SqlServer the LINGUA column has three corners "eng"
I made the query:
//DB2
private static final String DB_DRIVER_DB2 = "com.ibm.db2.jcc.DB2Driver";
private static final String DB_CONNECTION_DB2 = "jdbc:db2://10.12.230.83:50000/PANTH01";
private static final String DB_USER_DB2 = "finance";
private static final String DB_PASSWORD_DB2 = "finance";
//SQLSERVER
private static final String DB_DRIVER_SQLSERVER = "net.sourceforge.jtds.jdbc.Driver";
private static final String DB_CONNECTION_SQLSERVER = "jdbc:jtds:sqlserver://10.65.21.15:1433;DatabaseName=PROVA";
private static final String DB_USER_SQLSERVER = "sa";
private static final String DB_PASSWORD_SQLSERVER = "sa";
public void selectTHERACLASS_HDR_THERACLASS_HDR_NLS() throws Exception {
//DB2_inizio
Connection dbConnection = null;
Statement statement = null;
//DB2_fine
//SqlServer_inzio
Connection dbConnectionSqlServer = null;
Statement statementSqlServer = null;
//SqlServer_fine
//Query SqlServer and db2
String query = "select PANTH01.THERA.CLASS_HDR.LANGUAGE, PROVA.DIZIOPT.LINGUA from PANTH01.THERA.CLASS_HDR JOIN PROVA.DIZIOPT.LINGUA ON PANTH01.THERA.CLASS_HDR.LANGUAGE = PROVA.DIZIOPT.LINGUA";
try {
//DB2_inizio
dbConnection = getConnectionDb2();
statement = dbConnection.createStatement();
//DB2_fine
//sqlserver_inizio
dbConnectionSqlServer = getConnectionSqlServer();
statementSqlServer = dbConnectionSqlServer.createStatement();
ResultSet rSqlServer = statementSqlServer.executeQuery(query);
//sqlserver_inizio_fine
while (rSqlServer.next()) {
String language = rSqlServer.getString("LANGUAGE");
String lingua = rSqlServer.getString("LINGUA");
System.out.println("LANGUAGE: " + language);
System.out.println("LINGUA: " + lingua);
}
} catch (SQLException e) {
System.out.println(e.getMessage());
} finally {
if (statement != null && statementSqlServer != null) {
statement.close();
statementSqlServer.close();
}
if (dbConnection != null && dbConnectionSqlServer != null) {
dbConnection.close();
dbConnectionSqlServer.close();
}
}
}
Output
Invalid object name 'PANTH01.THERA.CLASS_HDR'

Dapper return specfic field from query

I am working on connecting to SQL database and setting variables in my C# application to match returned values from a Dapper Query.
I am able to return the correct row information (I used a datagridview to show that i get the correct row, and when i debug i see the right data) but how do I set a program variable to just one of the columns? here is some code showing my process
Connect & run stored procedure:
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MfgDataCollector"].ToString()))
{
DynamicParameters param = new DynamicParameters();
param.Add("#UserID", txt_userid.Text.Trim());
List<User> userinfo = conn.Query<User>("GetUserInfo", param, commandType: CommandType.StoredProcedure).ToList<User>();
Variables.UserID = txt_userid.Text.Trim();
datagridview1.DataSource = userinfo; //this displays the right information, however I want to store the information as a public variable(like above)
Variables.Userfull = userinfo; //when debugging this shows I have the right information but its all columns of user
User Class:
class User
{
public string UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int AccessLevel { get; set; }
public int FirstUse { get; set; }
public string Password { get; set; }
public int LoginCounter { get; set; }
public string LastLogin { get; set; }
public string FirstLogin { get; set; }
public string UserFullName { get; set; }
}
EDIT
per answer, I have tried to switch the command to executescalar with this code:
var userinfo = conn.ExecuteScalar<User>("GetUserInfo", param, commandType: CommandType.StoredProcedure);
This allows me to set
Variables.UserID = userinfo.UserID;
without red-squiggle lines however during run-time i get an error on the execute scalar of "System.InvalidCastException: 'Invalid cast from 'System.String' to 'Name_Space.User'.'
I have checked the User Class to ensure the data type matches with the Database and i see no problems there, I'm not sure what I'm doing wrong?
If you want value from only one column of only one row, use ExecuteScalar which is similar to ADO.NET ExecuteScalar. It will return object with which you have to deal further.
string sql = "SELECT COL1 FROM Table1 WHERE ID = 1";
//OR
//string sql = "SELECT TOP 1 COL1 FROM Table1";
//OR similar
object colValue = conn.ExecuteScalar(sql, ....);
If matching record not found, return value will be null.
Check Dapper documentation for other generic variants of method:
public static T ExecuteScalar<T>(this IDbConnection cnn, CommandDefinition command);
If you want single column from multiple rows, the way you are doing this now is correct. Just modify your SQL query/Stored Procedure to return the same. Dapper will only map returned column. All other properties in your User will remain unassigned.
If you simply want to assign values to Variables.UserID instead of binding those using DataSource, then just do that like:
Variables.UserID = userinfo.UserID;
Variables.Userfull = userinfo.UserFullName;
//and so on....
So, complete code will be something like below:
using(SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MfgDataCollector"].ToString()))
{
DynamicParameters param = new DynamicParameters();
param.Add("#UserID", txt_userid.Text.Trim());
User userinfo = conn.Query<User>("GetUserInfo", param, commandType: CommandType.StoredProcedure).ToList<User>().First();
Variables.UserID = userinfo.UserID;
Variables.Userfull = userinfo.UserFullName;
}

How can I implement this T-SQL in my stored procedure?

I'm new to SQL Server and want to implement this scenario. My stored procedure gets 8 input parameters from a C# web application, and checks all input has into the table. For that purpose I wrote this simple stored procedure:
CREATE PROCEDURE CheckValid
#p_bank varchar,
#p_pay_date varchar,
#p_bill_id varchar,
#p_payment_id varchar,
#p_ref_code varchar,
#p_branch varchar,
#p_channel_type varchar,
#p_send_date varchar
AS
BEGIN
SELECT
[p_bank], [p_pay_date], [p_bill_id], [p_payment_id],
[p_ref_code], [p_branch], [p_channel_type], [p_send_date]
FROM
[SAMPLE].[dbo].[MixedTable]
WHERE
[p_bank] = #p_bank
AND [p_pay_date] = #p_pay_date
AND [p_bill_id] = #p_bill_id
AND [p_payment_id] = #p_payment_id
AND [p_ref_code] = #p_ref_code
AND [p_branch] = #p_branch
AND [p_channel_type] = #p_channel_type
AND [p_send_date] = #p_send_date
END
But want to return to c# application this scenario, for example c# sends all field but when stored procedure select run for this purpose can not find data, for example p_bill_id not correct into the table for more explain in select query into where clause in the [p_bill_id]=#p_bill_id not trust and now want to return sp this :
p_bill_id,not found
and other example c# all variable correct but two field [p_channel_type] and [p_payment_id] not correct into where clause but other 6 field correct now SP return this:
[p_channel_type],not found
[p_payment_id],not found
Summary of question:
When data for passed parameter value is not found, I want it to return that corresponding column.
For example:
[p_channel_type],not found
[p_payment_id],not found
Note, varchar means varchar(1) so you should specify length for each argument explicitly like varchar(100)
CREATE PROCEDURE CheckValid
#p_bank varchar(<length>),
#p_pay_date varchar(<length>),
#p_bill_id varchar(<length>),
#p_payment_id varchar(<length>),
#p_ref_code varchar(<length>),
#p_branch varchar(<length>),
#p_channel_type varchar(<length>),
#p_send_date varchar(<length>)
AS
BEGIN
if not exists(select 1 from dbo.BankTable where p_bank = #p_bank)
begin
raiserror('Bank %s not found', 16, 1, #p_bank)
return
end
if not exists(select 1 from dbo.BillTable where p_bill_id = #p_bill_id)
begin
raiserror('Bill %s not found', 16, 1, #p_bill_id)
return
end
...
SELECT [p_bank],[p_pay_date],[p_bill_id],[p_payment_id],[p_ref_code],[p_branch],[p_channel_type],[p_send_date]
FROM [SAMPLE].[dbo].[MixedTable]
where [p_bank]=#p_bank and [p_pay_date]=#p_pay_date
and [p_bill_id]=#p_bill_id and [p_payment_id]=#p_payment_id
and [p_ref_code]=#p_ref_code and [p_branch]=#p_branch
and [p_channel_type]=#p_channel_type and [p_send_date]=#p_send_date
END
GO
Instead of creating stored procedure for this move "validation" logic to your c# application.
Database is just IO device and I think keeping "business logic" in IO device not a good approach.
// Class which represent your eight parameters
public class Data
{
public string Bank { get; set; }
public string PayDate { get; set; }
public string BillId { get; set; }
public string PaymentId { get; set; }
public string RefCode { get; set; }
public string Branch { get; set; }
public string ChannelType { get; set; }
public string SendDate { get; set; }
}
public class Validation
{
private Data _data;
public Validation(Data data)
{
_data = data;
}
public IEnumerable<string> Validate()
{
var columns = new KeyValuePair<string, string>[]
{
new KeyValuePair("p_bank", _data.Bank),
new KeyValuePair("p_pay_date", _data.PayDate),
new KeyValuePair("p_bill_id", _data.BillId),
new KeyValuePair("p_payment_id", _data.PaymentId),
new KeyValuePair("p_ref_code], _data.RefCode),
new KeyValuePair("p_branch", _data.Branch),
new KeyValuePair("p_channel_type", _data.ChannelType),
new KeyValuePair("p_send_date", _data.SendDate)
};
return columns.Where(pair => IsValueExists(pair.Key, pair.Value) == false);
}
private bool IsValueExists(string columnName, string value)
{
var query =
$"SELECT [{columnName}]
FROM [SAMPLE].[dbo].[MixedTable]
WHERE [{columnName}] = #value";
var parameter = new SqlParameter
{
ParameterName = "#value",
SqlDbType = SqlDbType.VarChar,
Value = _data.Bank
};
using (var connection = new SqlConnection(yourConnectionString))
using (var command = new SqlCommand(query, connection))
{
command.Parameters.Add(parameter);
connection.Open();
var value = command.ExecuteScalar();
return value != null; // null returned if no rows exists
}
}
}
Then you can use this method somewhere
var data = new Data { Bank = "BankName", RefCode = "SomeRefcode" } // put all values
var validation = new Validation(data);
var invalidValues = validation.Validate();
foreach(var invalidValue in invalidValues)
{
// Print or save column names where value is invalid
}

Is it possible to use `SqlDbType.Structured` to pass Table-Valued Parameters in NHibernate?

I want to pass a collection of ids to a stored procedure that will be mapped using NHibernate. This technique was introduced in Sql Server 2008 ( more info here => Table-Valued Parameters ). I just don't want to pass multiple ids within an nvarchar parameter and then chop its value on the SQL Server side.
My first, ad hoc, idea was to implement my own IType.
public class Sql2008Structured : IType {
private static readonly SqlType[] x = new[] { new SqlType(DbType.Object) };
public SqlType[] SqlTypes(NHibernate.Engine.IMapping mapping) {
return x;
}
public bool IsCollectionType {
get { return true; }
}
public int GetColumnSpan(NHibernate.Engine.IMapping mapping) {
return 1;
}
public void NullSafeSet(DbCommand st, object value, int index, NHibernate.Engine.ISessionImplementor session) {
var s = st as SqlCommand;
if (s != null) {
s.Parameters[index].SqlDbType = SqlDbType.Structured;
s.Parameters[index].TypeName = "IntTable";
s.Parameters[index].Value = value;
}
else {
throw new NotImplementedException();
}
}
#region IType Members...
#region ICacheAssembler Members...
}
No more methods are implemented; a throw new NotImplementedException(); is in all the rest. Next, I created a simple extension for IQuery.
public static class StructuredExtensions {
private static readonly Sql2008Structured structured = new Sql2008Structured();
public static IQuery SetStructured(this IQuery query, string name, DataTable dt) {
return query.SetParameter(name, dt, structured);
}
}
Typical usage for me is
DataTable dt = ...;
ISession s = ...;
var l = s.CreateSQLQuery("EXEC some_sp #id = :id, #par1 = :par1")
.SetStructured("id", dt)
.SetParameter("par1", ...)
.SetResultTransformer(Transformers.AliasToBean<SomeEntity>())
.List<SomeEntity>();
Ok, but what is an "IntTable"? It's the name of SQL type created to pass table value arguments.
CREATE TYPE IntTable AS TABLE
(
ID INT
);
And some_sp could be like
CREATE PROCEDURE some_sp
#id IntTable READONLY,
#par1 ...
AS
BEGIN
...
END
It only works with Sql Server 2008 of course and in this particular implementation with a single-column DataTable.
var dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
It's POC only, not a complete solution, but it works and might be useful when customized. If someone knows a better/shorter solution let us know.
A simpler solution than the accepted answer would be to use ADO.NET. NHibernate allows users to enlist IDbCommands into NHibernate transactions.
DataTable myIntsDataTable = new DataTable();
myIntsDataTable.Columns.Add("ID", typeof(int));
// ... Add rows to DataTable
ISession session = sessionFactory.GetSession();
using(ITransaction transaction = session.BeginTransaction())
{
IDbCommand command = new SqlCommand("StoredProcedureName");
command.Connection = session.Connection;
command.CommandType = CommandType.StoredProcedure;
var parameter = new SqlParameter();
parameter.ParameterName = "IntTable";
parameter.SqlDbType = SqlDbType.Structured;
parameter.Value = myIntsDataTable;
command.Parameters.Add(parameter);
session.Transaction.Enlist(command);
command.ExecuteNonQuery();
}
For my case, my stored procedure needs to be called in the middle of an open transaction.
If there is an open transaction, this code works because it is automatically reusing the existing transaction of the NHibernate session:
NHibernateSession.GetNamedQuery("SaveStoredProc")
.SetInt64("spData", 500)
.ExecuteUpdate();
However, for my new Stored Procedure, the parameter is not as simple as an Int64. It's a table-valued-parameter (User Defined Table Type)
My problem is that I cannot find the proper Set function.
I tried SetParameter("spData", tvpObj), but it's returning this error:
Could not determine a type for class: …
Anyways, after some trial and error, this approach below seems to work.
The Enlist() function is the key in this approach. It basically tells the SQLCommand to use the existing transaction. Without it, there will be an error saying
ExecuteNonQuery requires the command to have a transaction when the
connection assigned to the command is in a pending local transaction…
using (SqlCommand cmd = NHibernateSession.Connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = "MyStoredProc";
NHibernateSession.Transaction.Enlist(cmd); // Because there is a pending transaction
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#wiData", SqlDbType.Structured) { Value = wiSnSqlList });
int affected = cmd.ExecuteNonQuery();
}
Since I am using the SqlParameter class with this approach, SqlDbType.Structured is available.
This is the function where wiSnList gets assigned:
private IEnumerable<SqlDataRecord> TransformWiSnListToSql(IList<SHWorkInstructionSnapshot> wiSnList)
{
if (wiSnList == null)
{
yield break;
}
var schema = new[]
{
new SqlMetaData("OriginalId", SqlDbType.BigInt), //0
new SqlMetaData("ReportId", SqlDbType.BigInt), //1
new SqlMetaData("Description", SqlDbType.DateTime), //2
};
SqlDataRecord row = new SqlDataRecord(schema);
foreach (var wi in wiSnList)
{
row.SetSqlInt64(0, wi.OriginalId);
row.SetSqlInt64(1, wi.ShiftHandoverReportId);
if (wi.Description == null)
{
row.SetDBNull(2);
}
else
{
row.SetSqlString(2, wi.Description);
}
yield return row;
}
}
You can pass collections of values without the hassle.
Example:
var ids = new[] {1, 2, 3};
var query = session.CreateQuery("from Foo where id in (:ids)");
query.SetParameterList("ids", ids);
NHibernate will create a parameter for each element.

How to update a postgresql array column with spring JdbcTemplate?

I'm using Spring JdbcTemplate, and I'm stuck at the point where I have a query that updates a column that is actually an array of int. The database is postgres 8.3.7.
This is the code I'm using :
public int setUsersArray(int idUser, int idDevice, Collection<Integer> ids) {
int update = -666;
int[] tipi = new int[3];
tipi[0] = java.sql.Types.INTEGER;
tipi[1] = java.sql.Types.INTEGER;
tipi[2] = java.sql.Types.ARRAY;
try {
update = this.jdbcTemplate.update(setUsersArrayQuery, new Object[] {
ids, idUser, idDevice }, tipi);
} catch (Exception e) {
e.printStackTrace();
}
return update;
}
The query is "update table_name set array_column = ? where id_user = ? and id_device = ?".
I get this exception :
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [update acotel_msp.users_mau set denied_sub_client = ? where id_users = ? and id_mau = ?]; The column index is out of range: 4, number of columns: 3.; nested exception is org.postgresql.util.PSQLException: The column index is out of range: 4, number of columns: 3.
Caused by: org.postgresql.util.PSQLException: The column index is out of range: 4, number of columns: 3.
I've looked into spring jdbc template docs but I can't find any help, I'll keep looking, anyway could someone point me to the right direction? Thanks!
EDIT :
Obviously the order was wrong, my fault...
I tried both your solutions, in the first case I had this :
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [update users set denied_sub_client = ? where id_users = ? and id_device = ?]; nested exception is org.postgresql.util.PSQLException: Cannot cast an instance of java.util.ArrayList to type Types.ARRAY
Trying the second solution I had this :
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [update users set denied_sub_client = ? where id_users = ? and id_device = ?]; nested exception is org.postgresql.util.PSQLException: Cannot cast an instance of [Ljava.lang.Object; to type Types.ARRAY
I suppose i need an instance of java.sql.Array, but how can I create it using JdbcTemplate?
After struggling with many attempts, we settled to use a little helper ArraySqlValue to create Spring SqlValue objects for Java Array Types.
usage is like this
jdbcTemplate.update(
"UPDATE sometable SET arraycolumn = ?",
ArraySqlValue.create(arrayValue))
The ArraySqlValue can also be used in MapSqlParameterSource for use with NamedParameterJdbcTemplate.
import static com.google.common.base.Preconditions.checkNotNull;
import java.sql.Array;
import java.sql.JDBCType;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Locale;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.support.SqlValue;
public class ArraySqlValue implements SqlValue {
private final Object[] arr;
private final String dbTypeName;
public static ArraySqlValue create(final Object[] arr) {
return new ArraySqlValue(arr, determineDbTypeName(arr));
}
public static ArraySqlValue create(final Object[] arr, final String dbTypeName) {
return new ArraySqlValue(arr, dbTypeName);
}
private ArraySqlValue(final Object[] arr, final String dbTypeName) {
this.arr = checkNotNull(arr);
this.dbTypeName = checkNotNull(dbTypeName);
}
#Override
public void setValue(final PreparedStatement ps, final int paramIndex) throws SQLException {
final Array arrayValue = ps.getConnection().createArrayOf(dbTypeName, arr);
ps.setArray(paramIndex, arrayValue);
}
#Override
public void cleanup() {}
private static String determineDbTypeName(final Object[] arr) {
// use Spring Utils similar to normal JdbcTemplate inner workings
final int sqlParameterType =
StatementCreatorUtils.javaTypeToSqlParameterType(arr.getClass().getComponentType());
final JDBCType jdbcTypeToUse = JDBCType.valueOf(sqlParameterType);
// lowercasing typename for Postgres
final String typeNameToUse = jdbcTypeToUse.getName().toLowerCase(Locale.US);
return typeNameToUse;
}
}
this code is provided in the Public Domain
private static final String ARRAY_DATATYPE = "int4";
private static final String SQL_UPDATE = "UPDATE foo SET arr = ? WHERE d = ?";
final Integer[] existing = ...;
final DateTime dt = ...;
getJdbcTemplate().update(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(final Connection con) throws SQLException {
final PreparedStatement ret = con.prepareStatement(SQL_UPDATE);
ret.setArray(1, con.createArrayOf(ARRAY_DATATYPE, existing));
ret.setDate(2, new java.sql.Date(dt.getMillis()));
return ret;
}
});
This solution is kind of workaround using postgreSQL built-in function, which definitely worked for me.
reference blog
1) Convert String Array to Comma Separated String
If you are using Java8, it's pretty easy. other options are here
String commaSeparatedString = String.join(",",stringArray); // Java8 feature
2) PostgreSQL built-in function string_to_array()
you can find other postgreSQL array functions here
// tableName ( name text, string_array_column_name text[] )
String query = "insert into tableName(name,string_array_column_name ) values(?, string_to_array(?,',') )";
int[] types = new int[] { Types.VARCHAR, Types.VARCHAR};
Object[] psParams = new Object[] {"Dhruvil Thaker",commaSeparatedString };
jdbcTemplate.batchUpdate(query, psParams ,types); // assuming you have jdbctemplate instance
The cleanest way I found so far is to first convert the Collection into an Integer[] and then use the Connection to convert that into an Array.
Integer[] idArray = ids.toArray(new Integer[0]);
Array idSqlArray = jdbcTemplate.execute(
(Connection c) -> c.createArrayOf(JDBCType.INTEGER.getName(), idArray)
);
update = this.jdbcTemplate.update(setUsersArrayQuery, new Object[] {
idSqlArray, idUser, idDevice })
This is based on information in the documentation: https://jdbc.postgresql.org/documentation/head/arrays.html
The argument type and argument is not matching.
Try changing the argument type order
int[] tipi = new int[3];
tipi[0] = java.sql.Types.ARRAY;
tipi[1] = java.sql.Types.INTEGER;
tipi[2] = java.sql.Types.INTEGER;
or use
update = this.jdbcTemplate.update(setUsersArrayQuery, new Object[] {
ids.toArray(), idUser, idDevice })
and see if it works
http://valgogtech.blogspot.com/2009/02/passing-arrays-to-postgresql-database.html explains how to create java.sql.Array postgresql
basically Array.getBaseTypeName should return int and Array.toString should return the array content in "{1,2,3}" format
after you create the array you can set it using preparedstatement.setArray(...)
from PreparedStatementCreator e.g.
jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
Good Luck ..
java.sql.Array intArray = connection.createArrayOf("int", existing);
List<Object> values= new ArrayList<Object>();
values.add(intArray);
values.add(dt);
getJdbcTemplate().update(SQL_UPDATE,values);

Resources