How do INSERT INTO Firebird, with autoincrement for the primary key?
For the table fields I have:
fstPriority VARCHAR(30), fstInfo VARCHAR(100), fstDateCreated VARCHAR(30), fstDateModified VARCHAR(30), fiKeyID INTEGER PRIMARY KEY
For the INSERT INTO I have:
FbConnection fbConn = new FbConnection(stOpenConn))
fbConn.Open();
...
FbTransaction fbTransaction = fbConn.BeginTransaction();
FbCommand fbCmd = new FbCommand("INSERT INTO " + stTableName + "(" + stFieldNames + ") VALUES ( #p0, #p1, #p2, #p3, #p4 ) RETURNING fiKeyID ", fbConn, fbTransaction);
but am uncertain what should be used for the
fbCmd.Parameters.AddWithValue
fbCmd.Parameters.AddWithValue("#p0", "1st value");
fbCmd.Parameters.AddWithValue("#p1", "2nd value");
fbCmd.Parameters.AddWithValue("#p2", "3rd value");
fbCmd.Parameters.AddWithValue("#p3", "4th value");
Then what? For fiKeyID, do I add
fbCmd.Parameters.AddWithValue("#p4", "");
Also, I see at http://www.firebirdfaq.org/faq29/ creating an autoincrement column, but am uncertain how to do this in C# ... Firebird ADO.NET ... FirebirdClient.5.8.0 ... Visual Studio 2013.
CREATE GENERATOR ...;
SET GENERATOR ...;
set term !! ;
CREATE TRIGGER ...
are not recognized by the Visual Studio compiler.
An important thing is that SET TERM is not part of the Firebird statement syntax, instead it is a client-side feature to set the statement terminator in query tools like ISQL. This terminator is necessary to know when a statement is complete and can be sent to the server. By default these tools do that on a semi-colon (;), but that doesn't work with PSQL (stored procedures, triggers), because PSQL code uses the semi-colon as well. To address this, these tools have SET TERM to switch this terminator.
Using the Firebird ADO.net provider however, you need to execute statements one at a time, so a statement terminator is irrelevant.
To be able to generate a primary key you can use the following solutions:
Firebird 3 has an identity type column, so you don't need to create a sequence and trigger yourself:
create table withgeneratedid(
id integer generated by default as identity primary key,
column2 varchar(100)
)
For Firebird 2.5 and earlier you will need to create a sequence and trigger:
create table withgeneratedid(
id integer primary key,
column2 varchar(100)
);
create sequence seq_withgeneratedid;
set term #;
create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
if (new.id is null) then new.id = next value for seq_withgeneratedid;
end#
set term ;#
When you insert values into a table and want to have a generated key, you should not include the id column in the column-list. Including the id column allows you to override the key value, but that might lead to future inserts generating a duplicate key!. If you do include the id column, then no key will be generated in the Firebird 3 example, in the Firebird 2.5 example a key will be generated if the value of the column is null, otherwise it will take the provided value.
In ADO.net you'd normally need to execute the statements individually (and not use set term). Alternatively, you could use FbScript to parse a DDL script and execute the parse statements. Note that FbScript does support (and even requires) set term.
To execute this with the Firebird ADO.net provider, you can do something like the example below. I have included three alternatives for creating the table Firebird3, Firebird2_5, and FbScriptFB2_5 (which is the same as Firebird2_5 but uses FbScript). It also show how to retrieve the generated key:
namespace FbGeneratedKeys
{
class Program
{
private static SolutionType solutionType = SolutionType.FbScriptFB2_5;
static void Main(string[] args)
{
var connectionString = new FbConnectionStringBuilder
{
Database = #"D:\temp\generatedkey.fdb",
ServerType = FbServerType.Default,
UserID = "SYSDBA",
Password = "masterkey",
}.ToString();
FbConnection.CreateDatabase(connectionString, pageSize: 8192, overwrite : true);
using (FbConnection connection = new FbConnection(connectionString))
using (FbCommand cmd = new FbCommand())
{
connection.Open();
cmd.Connection = connection;
switch (solutionType) {
case SolutionType.Firebird3:
Firebird3Example(cmd);
break;
case SolutionType.Firebird2_5:
Firebird2_5Example(cmd);
break;
case SolutionType.FbScriptFB2_5:
FbScriptFB2_5Example(cmd);
break;
}
cmd.CommandText = #"insert into withgeneratedid(column2) values (#column2) returning id";
cmd.Parameters.AddWithValue("#column2", "some value");
cmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
cmd.ExecuteNonQuery();
Console.WriteLine("Id:" + cmd.Parameters[1].Value);
Console.ReadLine();
}
}
private static void Firebird3Example(FbCommand cmd)
{
// Firebird 3 identity column
cmd.CommandText = #"create table withgeneratedid(
id integer generated by default as identity primary key,
column2 varchar(100)
)";
cmd.ExecuteNonQuery();
}
private static void Firebird2_5Example(FbCommand cmd)
{
// Firebird 2.5 and earlier normal primary key with trigger to generate key
// Table
cmd.CommandText = #"create table withgeneratedid(
id integer primary key,
column2 varchar(100)
)";
cmd.ExecuteNonQuery();
// Sequence
cmd.CommandText = "create sequence seq_withgeneratedid";
cmd.ExecuteNonQuery();
// Trigger
cmd.CommandText = #"create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
if (new.id is null) then new.id = next value for seq_withgeneratedid;
end";
cmd.ExecuteNonQuery();
}
private static void FbScriptFB2_5Example(FbCommand cmd)
{
string script = #"
create table withgeneratedid(
id integer primary key,
column2 varchar(100)
);
create sequence seq_withgeneratedid;
set term #;
create trigger withgeneratedid_bi before insert on withgeneratedid
as
begin
if (new.id is null) then new.id = next value for seq_withgeneratedid;
end#
set term ;#
";
FbScript fbScript = new FbScript(script);
fbScript.Parse();
FbBatchExecution exec = new FbBatchExecution(cmd.Connection);
exec.AppendSqlStatements(fbScript);
exec.Execute();
}
}
enum SolutionType
{
Firebird3,
Firebird2_5,
FbScriptFB2_5
}
}
Definitions:
public const string stMAIN_TABLE_NAME = " OrgTable ";
public const string stDELETED_TABLE_NAME = " BackupTable ";
public const string stFIELD_DEFINITIONS = " fstPriority VARCHAR(30)" +
", fstInfo VARCHAR(100)" +
", fstDateCreated VARCHAR(30)" +
", fstDateModified VARCHAR(30)" +
", fiKeyID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ";
public const string stFIELD_NAMES = " fstPriority" +
", fstInfo" +
", fstDateCreated" +
", fstDateModified" +
", fiKeyID ";
public const string stFIELD_NAMES_NO_KEY_ID = " fstPriority" +
", fstInfo" +
", fstDateCreated" +
", fstDateModified ";
Code:
//------------------------------
static private bool boCreateDatabaseTables(string stPathFilename,
string stUserID,
string stPassword,
List<string> liststTableNames,
List<string> liststFieldDefinitions)
{
bool boErrorFlag = false;
int iTablesCount = liststTableNames.Count();
string stOpenConn = new FbConnectionStringBuilder {
Database = stPathFilename,
UserID = stUserID,
Password = stPassword,
ServerType = FbServerType.Embedded,
ClientLibrary = stCLIENT_LIBRARY
}.ToString();
using (FbConnection fbConn = new FbConnection(stOpenConn)) {
try {
fbConn.Open();
FbTransaction fbTransaction = fbConn.BeginTransaction();
for (int ii = 0; ii < iTablesCount; ii++) {
string stSql = "CREATE TABLE " + liststTableNames[ii] + "( " + liststFieldDefinitions[ii] + ")";
FbCommand fbCmd = new FbCommand(stSql, fbConn, fbTransaction);
fbCmd.ExecuteNonQuery();
}
fbTransaction.Commit();
}
catch (Exception ex) {
boErrorFlag = true;
MessageBox.Show("catch ... GlobalsFirebird ... boCreateDatabaseTables ... " + ex.Message);
}
}
return boErrorFlag;
}//boCreateDatabaseTables
//------------------------------
//------------------------------
static public bool boAddRow(string stPathFilename,
string stUserID,
string stPassword,
string stTableName,
string stFieldNamesNoKeyId,
List<string> liststFieldValuesNoKeyId)
{
bool boErrorFlag = false;
string stOpenConn = new FbConnectionStringBuilder {
Database = stPathFilename,
UserID = stUserID,
Password = stPassword,
ServerType = FbServerType.Embedded,
ClientLibrary = stCLIENT_LIBRARY
}.ToString();
using(FbConnection fbConn = new FbConnection(stOpenConn)) {
fbConn.Open();
try {
string stValuesPlaceHolder = "#p0";
for (int iii = 1; iii < liststFieldValuesNoKeyId.Count; iii++)
stValuesPlaceHolder += ", #p" + (iii).ToString();
FbTransaction fbTransaction = fbConn.BeginTransaction();
string stCmd = "INSERT INTO " + stTableName + "(" + stFieldNamesNoKeyId + ") VALUES ( " + stValuesPlaceHolder + " ) RETURNING fiKeyID ";
FbCommand fbCmd = new FbCommand(stCmd, fbConn, fbTransaction);
for (int iii = 0; iii < liststFieldValuesNoKeyId.Count; iii++) {
string stPlaceHolder = "#p" + (iii).ToString();
string stValue = liststFieldValuesNoKeyId[iii];
fbCmd.Parameters.AddWithValue(stPlaceHolder, stValue);
}
fbCmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
fbCmd.ExecuteNonQuery();
fbTransaction.Commit();
}
catch (Exception ex) {
boErrorFlag = true;
MessageBox.Show("catch ... GlobalsFirebird ... boAddRow ... " + ex.Message);
}
}
return boErrorFlag;
}//boAddRow
//------------------------------
Related
Regarding a question and its related answer, I'm trying to create a new table type using SQL queries and run a join query on data which are of this table type.
I put both queries in the same script, i.e. create type and select but it is not working. My query is as below (the whole code is brought in the end of question):
IF TYPE_ID(N'BranchMappingType') IS NULL
CREATE TYPE BranchMappingType As Table
(COL_ServerID int,
COL_ServerSchema Common._SMALL_,
COL_TableName Common._SMALL_,
COL_BranchNo Common._SMALL_,
COL_BranchSchema Common._SMALL_ );
SELECT [VMA_BranchID] FROM [VIP].[VipMapping]
JOIN #TVP ON
VMA_ServerID=COL_ServerID AND
VMA_ServerSchema=COL_ServerSchema AND
VMA_TableName=COL_TableName AND
VMA_BranchNo=COL_BranchNo AND
VMA_BranchSchema=COL_BranchSchema;
I will execute my query using SqlDataReader.ExecuteReader but It will throw exception:
Column, parameter, or variable #TVP. : Cannot find data type BranchMappingType.
But when I run 1st part of query, i.e., IF TYPE_ID(...) ... CREATE TYPE it will execute with no errors and then running the second part i.e. Select from ... join #TVP works flawlessly.
It seems newly created table type is not recognized when I run both queries with single execution of SqlDataReader.ExecuteReader.
I want to know what is the reason and If I've made some mistakes during my implementation or not? I prefer to run both queries with one-time calling of SqlDataReader.ExecuteReader method.
Here is full code for better inspection:
List<int> keys = new List<int>();
if (foreignKeyInfoList == null || foreignKeyInfoList.Count == 0)
return new StatusResponseKeysList { ErrorCode = ErrorCodes.ERROR_CODE_MINUS_019_NULL_OR_EMPTY_INPUT_ARGUMENT };
const string ID_FIELD = "VMA_BranchID";
const string STRUCTURED_DATA_TABLE_NAME = "#TVP";
const string COL1_SERVER_ID = "COL_ServerID";
const string COL2_SERVER_SCHEMA = "COL_ServerSchema";
const string COL3_TABLE_NAME = "COL_TableName";
const string COL4_BRANCH_NO = "COL_BranchNo";
const string COL5_BRANCH_SCHEMA = "COL_BranchSchema";
//const string COL6_TABLE_ID = "COL_TableID";
const string MAP1_SERVER_ID = "VMA_ServerID";
const string MAP2_SERVER_SCHEMA = "VMA_ServerSchema";
const string MAP3_TABLE_NAME = "VMA_TableName";
const string MAP4_BRANCH_NO = "VMA_BranchNo";
const string MAP5_BRANCH_SCHEMA = "VMA_BranchSchema";
const string TYPE_NAME = "BranchMappingType";
//const string MAP6_TABLE_ID = "VMA_TableID";
string sqlQuery = $"IF TYPE_ID(N'{TYPE_NAME}') IS NULL" + " " +
$"CREATE TYPE {TYPE_NAME} As Table" + " " +
$"({COL1_SERVER_ID} int," + " " +
$"{COL2_SERVER_SCHEMA} Common._SMALL_," + " " +
$"{COL3_TABLE_NAME} Common._SMALL_," + " " +
$"{COL4_BRANCH_NO} Common._SMALL_," + " " +
$"{COL5_BRANCH_SCHEMA} Common._SMALL_" + " " +
$"); ";
sqlQuery += $"SELECT [{ID_FIELD}] " +
$"FROM {Settings.VIP_MAPPING_TABLE} " +
$"JOIN {STRUCTURED_DATA_TABLE_NAME} ON " +
$"{MAP1_SERVER_ID}={COL1_SERVER_ID} " +
$" AND {MAP2_SERVER_SCHEMA}={COL2_SERVER_SCHEMA} " +
$" AND {MAP3_TABLE_NAME}={COL3_TABLE_NAME} " +
$" AND {MAP4_BRANCH_NO}={COL4_BRANCH_NO} " +
$" AND {MAP5_BRANCH_SCHEMA}={COL5_BRANCH_SCHEMA} ";// +
//$" AND {MAP6_TABLE_ID}={COL6_TABLE_ID}";
DataTable dataTable = new DataTable(TYPE_NAME);
dataTable.Columns.Add(COL1_SERVER_ID, typeof(int));
dataTable.Columns.Add(COL2_SERVER_SCHEMA, typeof(string));
dataTable.Columns.Add(COL3_TABLE_NAME, typeof(string));
dataTable.Columns.Add(COL4_BRANCH_NO, typeof(string));
dataTable.Columns.Add(COL5_BRANCH_SCHEMA, typeof(string));
//dataTable.Columns.Add(COL6_TABLE_ID, typeof(int));
for (int i = 0; i < foreignKeyInfoList.Count; i++)
{
ForeignKeyLookupModelInBranchSide oneKeySet = foreignKeyInfoList[i];
DataRow row = dataTable.NewRow();
row[COL1_SERVER_ID] = Int32.Parse(oneKeySet.ServerID);
row[COL2_SERVER_SCHEMA] = oneKeySet.ServerSchema;
row[COL3_TABLE_NAME] = oneKeySet.TableName;
row[COL4_BRANCH_NO] = oneKeySet.BranchNo;
row[COL5_BRANCH_SCHEMA] = oneKeySet.BranchSchema;
dataTable.Rows.Add(row);
}
try
{
using (SqlConnection sqlConnection = new SqlConnection(Settings.connectionString))
{
SqlCommand command = new SqlCommand(sqlQuery, sqlConnection);
SqlParameter p = command.Parameters.Add(STRUCTURED_DATA_TABLE_NAME, SqlDbType.Structured);
p.Value = dataTable;
p.TypeName = TYPE_NAME;
sqlConnection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
keys.Add(reader.GetInt32(i));
}
}
}
else
{
}
sqlConnection.Close();
}
return new StatusResponseKeysList
{
keysList = keys,
ErrorCode = ErrorCodes.ERROR_CODE_MINUS_019_NULL_OR_EMPTY_INPUT_ARGUMENT
};
}
catch (Exception e)
{
//Exception caught here !!!!!!!:
//"Column, parameter, or variable #TVP. : Cannot find data type BranchMappingType."
if (e.GetType() == typeof(SqlException))
{
//this is a db error
return new StatusResponseKeysList
{
ErrorCode = ErrorCodes.ERROR_CODE_MINUS_014_DATABASE_EXCEPTION
};
}
else
{
//other types of erros (not a DB-error)
return new StatusResponseKeysList
{
ErrorCode = ErrorCodes.ERROR_CODE_MINUS_001_COMMON_ERROR
};
}
}
The table type must exist before the parameterized query that uses the type is executed. The reason is the database engine first creates an internal table in tempdb matching the table type defined on the server before the batch executes. It then bulk inserts the TVP value into tempdb using the structured parameter value and meta-data provided by the API. Only after the table is loaded does the batch execute, where it can use the TVP value.
The implication is that one cannot create the table type and use it in the same batch that uses the type as a parameter.
So, I'm trying to execute a stored procedure from a string to get a wide range of data but I get a
The data reader has more than one field. Multiple fields are not valid for EDM primitive or enumeration types
How do I probably execute my stored procedure
public class CarBLL
{
public static string GetCar(string carnumberxx, string authTicketxx)
{
using (var context = new SindalDBEntities())
{
var doerTicket = "0x0100000067294c938207c9e5e0cb6b98ddc4edd9464bc14e68b59cbc73c9dc559c5d43ad9b3705d1720a52542e0220b7df11b2e4bd913d873b1aa18c";
var stamdata = "G0057";
String sql =
"SET NOCOUNT ON; " +
"EXEC [file].[usp_getCar] " +
"#p_CarNumber= '" + stamdata + "', " +
"#p_DoerTicket= '" + doerTicket + "' ";
List<SqlParameter> sqlParams = new List<SqlParameter>();
sqlParams.Add(new SqlParameter("CarNumber", System.Data.SqlDbType.VarChar, 100, stamdata));
sqlParams.Add(new SqlParameter("DoerTicket", System.Data.SqlDbType.VarChar, 100, doerTicket));
return context.Database.SqlQuery<string>(sql).FirstOrDefault<string>();
}
}
}
}
If I try to exclude one of my parameters from the SP as
public static usp_getCar_Result GetCar(string carnumber, string authTicket)
{
using (var context = new SindalDBEntities())
{
int? id = null;
// ObjectParameter Id = new ObjectParameter("p_ID", typeof(global::System.Int32));
// ObjectResult result = new ObjectResult("usp_getCar_Result", typeof(string));
return context.usp_getCar(id, carnumber, authTicket).FirstOrDefault();
}
}
}
}
I gets a
Additional information: The data reader is incompatible with the specified 'SindalDbModel.usp_getCar_Result'. A member of the type, 'ID1', does not have a corresponding column in the data reader with the same name.
It's better to import your usp_getCar stored procedure to your DbContext. See this tutorial.
In this case you will have a strongly typed method in your SindalDBEntities. So you will be able to just execute it:
return context.GetCar(stamdata, doerTicket).FirstOrDefault();
Why the ExecuteNonQuery catch exception {"validation error for column \"ORGTABLE\".\"FIKEYID\", value \"* null *\""}
string stValuesPlaceHolder = "#p0";
for (int iii = 1; iii < liststFieldValuesNoKeyId.Count; iii++)
stValuesPlaceHolder += ", #p" + (iii).ToString();
FbTransaction fbTransaction = fbConn.BeginTransaction();
FbCommand fbCmd = new FbCommand("INSERT INTO " + stTableName + "(" + stFieldNamesNoKeyId + ") VALUES ( " + stValuesPlaceHolder + " )", fbConn, fbTransaction);
for (int iii = 0; iii < liststFieldValuesNoKeyId.Count; iii++) {
string stPlaceHolder = "#p" + (iii).ToString();
string stValue = liststFieldValuesNoKeyId[iii];
fbCmd.Parameters.AddWithValue(stPlaceHolder, stValue);
}
fbCmd.ExecuteNonQuery();
fbTransaction.Commit();
The stTableName is OrgTable.
The fields names are:
fstPriority, fstInfo, fstDateCreated, fstDateModified, fiKeyID.
The field definitions are:
fstPriority VARCHAR(30), fstInfo VARCHAR(100), fstDateCreated VARCHAR(30), fstDateModified VARCHAR(30), fiKeyID INTEGER PRIMARY KEY
In this section of the code:
stFieldNamesNoKeyId = "fstPriority, fstInfo, fstDateCreated, fstDateModified".
stValuesPlaceHolder = "#p0, #p1, #p2, #p3"
Four
fbCmd.Parameters.AddWithValue:
stPlaceHolder = "#p0" ... stValue = "1st value";
stPlaceHolder = "#p1" ... stValue = "2nd value";
stPlaceHolder = "#p2" ... stValue = "3rd value";
stPlaceHolder = "#p3" ... stValue = "4th value";
I did not add a value for fiKeyID as it as the PRIMARY KEY.
I did not add a value for fiKeyID as it as the PRIMARY KEY.
So you try to insert a NULL primary key. This is not allowed.
http://www.firebirdsql.org/manual/nullguide-keys.html
NULLs are never allowed in primary keys. A column can only be (part of) a PK if it has been defined as NOT NULL, either in the column definition or in a domain definition.
Then, you might want to ask server for auto-generating IDs. There are few ways of doing it.
Firebird 3 comes with auto-inc column type, for example. Which is a syntactic sugar over tools that were explicitly used by database developer before.
Firebird 2 and prior versions used GENERATORS (aka SQL SEQUENCE) to achieve it.
You have to make a BEFORE-INSERT (or BEFORE-INSERT-OR-UPDATE) trigger on the table, that would fill the ID field from the generator, if the ID field was NULL. http://www.firebirdfaq.org/faq29/
CREATE GENERATOR gen_t1_id;
SET GENERATOR gen_t1_id TO 0;
set term !! ;
CREATE TRIGGER T1_BI FOR T1
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
if (NEW.ID is NULL) then NEW.ID = GEN_ID(GEN_T1_ID, 1);
END!!
set term ; !!
There it boils down to your SQL access library.
Because typically after you inserted the row - you have to know its ID.
If you do not care about that ID of newborn row, you may skip the rest.
But if you want to both insert the row and know its ID then it boils down to another choice.
Low-tech SQL-only libraries would force you to take a doubletrip:
SELECT GEN_ID(GEN_T1_ID, 1) FROM RDB$DATABASE or SELECT NEXT VALUE FOR GEN_T1_ID FROM RDB$DATABASE would reserve you a free token, then you would explicitly assign your ID PK-column to that value and insert it together with data columns, bypassing the trigger.
Or with advanced SQL libraries you may ask Firebird to auto-calculate value and report it to you: INSERT INTO tablename(data1,data2,dataq3) VALUES (1,2,3) RETURNING id. See https://en.wikipedia.org/wiki/Insert_(SQL)#Retrieving_the_key
Whether you need to learn the inserted ID or not, and whether your SQL library supports INSERT-RETURNING command or not - it is up to you to decide.
However when I do Google search ( it is www.google.com ) it comes with many links about C# Firebird Insert Returniung for many different C# SQL libraries, and again only you can tell which one you use. For few examples from different libs:
http://www.ibprovider.com/eng/documentation/firebird_21_adonet.html
http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=720869&msg=8075816
Retrieve last id from Firebird db table
Firebird insert...returning asp.net
https://social.msdn.microsoft.com/Forums/en-US/e7099cfb-7809-460a-aae9-79a2bd703454/how-i-can-return-the-id-after-insert-a-record-into-a-database-firebird-25-with-linq?forum=adodotnetentityframework
et cetera
Definitions:
public const string stMAIN_TABLE_NAME = " OrgTable ";
public const string stDELETED_TABLE_NAME = " BackupTable ";
public const string stFIELD_DEFINITIONS = " fstPriority VARCHAR(30)" +
", fstInfo VARCHAR(100)" +
", fstDateCreated VARCHAR(30)" +
", fstDateModified VARCHAR(30)" +
", fiKeyID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY ";
public const string stFIELD_NAMES_NO_KEY_ID = " fstPriority" +
", fstInfo" +
", fstDateCreated" +
", fstDateModified ";
public const string stFIELD_NAMES_KEY_ID = " fiKeyID ";
public const string stFIELD_NAMES = stFIELD_NAMES_NO_KEY_ID + ", " + stFIELD_NAMES_KEY_ID;
Code:
//------------------------------
static private bool boCreateDatabaseTables(string stPathFilename,
string stUserID,
string stPassword,
List<string> liststTableNames,
List<string> liststFieldDefinitions)
{
bool boErrorFlag = false;
int iTablesCount = liststTableNames.Count();
string stOpenConn = new FbConnectionStringBuilder {
Database = stPathFilename,
UserID = stUserID,
Password = stPassword,
ServerType = FbServerType.Embedded,
ClientLibrary = stCLIENT_LIBRARY
}.ToString();
using (FbConnection fbConn = new FbConnection(stOpenConn)) {
try {
fbConn.Open();
FbTransaction fbTransaction = fbConn.BeginTransaction();
for (int ii = 0; ii < iTablesCount; ii++) {
string stSql = "CREATE TABLE " + liststTableNames[ii] + "( " + liststFieldDefinitions[ii] + ")";
FbCommand fbCmd = new FbCommand(stSql, fbConn, fbTransaction);
fbCmd.ExecuteNonQuery();
}
fbTransaction.Commit();
}
catch (Exception ex) {
boErrorFlag = true;
MessageBox.Show("catch ... GlobalsFirebird ... boCreateDatabaseTables ... " + ex.Message);
}
}
return boErrorFlag;
}//boCreateDatabaseTables
//------------------------------
//------------------------------
static public bool boAddRow(string stPathFilename,
string stUserID,
string stPassword,
string stTableName,
string stFieldNamesNoKeyId,
string stFieldNamesKeyId,
List<string> liststFieldValuesNoKeyId)
{
bool boErrorFlag = false;
string stOpenConn = new FbConnectionStringBuilder {
Database = stPathFilename,
UserID = stUserID,
Password = stPassword,
ServerType = FbServerType.Embedded,
ClientLibrary = stCLIENT_LIBRARY
}.ToString();
using(FbConnection fbConn = new FbConnection(stOpenConn)) {
fbConn.Open();
try {
string stValuesPlaceHolder = "#p0";
for (int iii = 1; iii < liststFieldValuesNoKeyId.Count; iii++)
stValuesPlaceHolder += ", #p" + (iii).ToString();
FbTransaction fbTransaction = fbConn.BeginTransaction();
string stCmd = "INSERT INTO " + stTableName + "(" + stFieldNamesNoKeyId + ") VALUES ( " + stValuesPlaceHolder + " ) RETURNING " + stFieldNamesKeyId;
FbCommand fbCmd = new FbCommand(stCmd, fbConn, fbTransaction);
for (int iii = 0; iii < liststFieldValuesNoKeyId.Count; iii++) {
string stPlaceHolder = "#p" + (iii).ToString();
string stValue = liststFieldValuesNoKeyId[iii];
fbCmd.Parameters.AddWithValue(stPlaceHolder, stValue);
}
fbCmd.Parameters.Add(new FbParameter() { Direction = System.Data.ParameterDirection.Output });
fbCmd.ExecuteNonQuery();
fbTransaction.Commit();
}
catch (Exception ex) {
boErrorFlag = true;
MessageBox.Show("catch ... GlobalsFirebird ... boAddRow ... " + ex.Message);
}
}
return boErrorFlag;
}//boAddRow
//------------------------------
I am using the following code for performing a delete operation via EF code first using a inline query internally
void IRepository<T>.Delete(params Guid[] ids)
{
var sql = string.Format("UPDATE {0} SET [IsDeleted] = 1 WHERE [Id] IN (#ids) ", GetTableName());
string sep = String.Join(", ", ids.Select(x => "'" + x + "'"));
var sqlParams = new Object[]
{
new SqlParameter("ids", string.Join(",",sep)),
};
DataContext.Database.ExecuteSqlCommand(sql, sqlParams);
}
Now when I execute the command it gives me
conversion failed when converting from a character string to uniqueidentifier
error.
Hiwever when I run the query in sql say.
UPDATE [dbo].[Table] SET [IsDeleted] = 1 WHERE [Id] IN ('20Cr0BCA-6EBB-E411-A04B-BC305BA8C713','506c79c1-6ebb-e411-a04b-bc305ba8c733')
it works fine.
Is this possible to do this way ?Or what am I doing wrong ?
I would do something like this
void IRepository<T>.Delete(params Guid[] ids)
{
if (ids == null || !ids.Any())
return;
var idParams = ids.Select((x, cnt)=> new { ParamName ="#ids"+ cnt, Param = x});
var sql = string.Format("UPDATE {0} SET [IsDeleted] = 1 WHERE [Id] IN ("+ String.Join(", ",idParams.Select(x => x.ParamName)) + ") ", "Table");
var sqlParams = idParams.Select(x=> new SqlParameter(x.ParamName, x.Param)).ToArray();
DataContext.Database.ExecuteSqlCommand(sql, sqlParams);
}
At program startup, I'm creating a SQL Server CE table if it doesn't exist, and adding a record to it:
if (dbconn.isValidTable("hhs_settings") == -1) // <-- (IOW, it's *not* a valid table)
{
DBUtils.CreateSettingsTable();
AppSettings.WriteSettingsVal("beltprinter", "None");
}
public static void CreateSettingsTable()
{
try
{
string sqlddl =
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)";
dbconn.DBCommand(sqlddl, false);
}
catch (SqlCeException sqlcex)
{
MessageBox.Show("sqlcex - CreateSettingsTable " + sqlcex.Message);
}
catch (Exception ex)
{
MessageBox.Show("CreateSettingsTable " + ex.Message);
}
}
...but then when I open a form that reads from the table:
lblSelectedPrinter.Text = AppSettings.ReadSettingsVal("beltprinter");
...it fails with "cannot find table 0"
public static string ReadSettingsVal(string settingName)
{
string retVal = string.Empty;
string sqldml = string.Format("select setting_value from hhs_settings where setting_name = {0}", " + settingName + ");
// There should only be one value returned, but using the existing getDataSet() method for now...
DataSet dsSettingVal = frmCentral.dbconn.getDataSet(sqldml);
foreach (DataRow row in dsSettingVal.Tables[0].Rows)
{
if (retVal == string.Empty)
{
retVal = row["setting_value"].ToString();
}
}
return retVal;
}
Am I missing a step here, so that the table (hhs_settings) is not being created? Or...???
UPDATE
I'm still getting the same err msg even after changing all of the goofily formatted strings so that they are now:
string sqldml = string.Format("insert into hhs_settings (setting_name, setting_value) values('{0}', '{1}')", settingName, settingVal);
string sqlqry = string.Format("select setting_value from hhs_settings where setting_name = '{0}'", settingName);
And, I'm still seeing the msg "about to create hhs_settings" even though, presumably, the createtable sql was already good:
string sqlddl =
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)";
...and so, the table should have been created (which should have made the isValidTable() method return something other than -1 (the value it returns when the table is not found).
UPDATE 2
It helped (it works now) when I changed my ddl to:
string sqlddl =
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name nvarchar(40) not null, setting_value nvarchar(63) not null)";
string.Format("select setting_value from hhs_settings where setting_name = {0}", " + settingName + ");
The result of it is
select setting_value from hhs_settings where setting_name = + settingName +
what is obviously not a correct SQL. You need to use something like this
string.Format("select setting_value from hhs_settings where setting_name = '{0}'", settingName);
(also note quotation marks around the parameter placeholder)
but it much more better if you use parameters instead of generating SQL with all identifiers embedded
If you are using stored procedures, check out the name of the passing queries in it. It should exactly match with the front end and backend.