SQLite DataAdapter no update or insert - sqlite

I use Sqlite v1.0.79 and vs2010 to create a simple winform application.
I have a customer table, and want to use the SQLiteDataAdapter to easily insert, update and delete records. So i do not need to type the whole insert, update and delete statements.
So i have a Customer class with a static load function that returns a dataset.
private static SQLiteDataAdapter _Adapter;
internal static DataSet Load(long id)
{
var q = "SELECT * FROM Customer WHERE id = {0}".FormatInvariant(id);
var cmd = new SQLiteCommand();
cmd.Connection = [_Connection];
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 10;
cmd.CommandText = commandText;
return cmd; _Adapter = new SQLiteDataAdapter();
_Adapter.SelectCommand = cmd;
var ds = new DataSet();
_Adapter.Fill(ds, "Customer");
if (id == 0)
{
ds.AddRow(ds.NewRow());
}
var b = new SQLiteCommandBuilder(_Adapter);
_Adapter.AcceptChangesDuringUpdate = true;
_Adapter.InsertCommand = b.GetInsertCommand();
_Adapter.UpdateCommand = b.GetUpdateCommand();
_Adapter.DeleteCommand = b.GetDeleteCommand();
// Commented out code for note A:
////ds.SetRowValue("lastname", "blaat44");
////_Adapter.Update(ds, "Customer");
return ds;
}
After calling the Load method, the DataSet is used in bindings on a windows form. And after some changes, the Save method is called, where the changes supposed to be saved.
internal static void Save(DataSet data)
{
//// data.AcceptChanges();
_Adapter.Update(data, "Customer");
}
But after the update, the database is not updating anything. What am i missing? I already tried the data.AcceptChanges before the update, but nothing works.
btw. the dataset in the save methods does have the 'right' values, but the update or insert is not working....
The strange thing is if i change a field in the dataset in the Load method (the commented out code at Note A in the example above), the data is saved correctly.

Im not an expert and have a basic understanding of sqlite etc but could the problem be that you are passing the dataset to the save function so the adapter is using a copy maybe of the original dataset. Which is why it works in the load method as the adapter is acessing tje original dataset?
Again this maybe complete babble and i may not understand but ive found sometimes the uneducated answer is a very good push in the right direction

Related

Why doesn't TransactionScope start a transaction for my following SQL commands

I set up a using block for a TransactionScope at the beginning of an ASP.NET action. Somewhere w/i the block I execute a function that both creates a using block for a SqlCommand and w/i that a using block for my SqlConnection.
The blocks for the command and connection open and close as the function is re-used but all w/i the TransactionScope using block. Eventually I call scope.Complete() and when leaving the TransactionScope using block I get an exception saying that no transaction was started that can be committed. In debugging I find that in fact all database calls are just happening w/o a transaction.
Based off the documentation its seeming like the generation of the TransactionScope should be the generation of the transaction, OR, at least that the first time I open any database connection that a transaction should begin because it's w/i the transaction scope block. However this is not the case and I'm unsure why this is.
I actually had this working fine at one point and then all of a sudden, it was not working. So there's something that I did that caused it but I have no idea what it was as the section this was implemented in was completed long ago.
Here's some code:
Starting the transactionScope
using (TransactionScope transactionScope = new TransactionScope())
{
try
{
LegacyDataManager.Start();
//*******************Replace W/ Controller Logic*********************
ViewBag.Message = "Finish";
...
...
...
LegacyDataManager.Commit();
transactionScope.Complete();
}
finally
{
LegacyDataManager.Stop();
}
}
Call to code that executes database update
var theApproval = LegacyDataManager.PrepareForUpdate(Constants.ObjectApproval, row["objectInstance"].ToString(), row["sourceServer"].ToString());
theApproval.write("approvalaction", Request.Form[val].ToString().Substring(0, 1));
theApproval.write("approvaldate", Data_Legacy.getAodDateTimeNow());
theApproval.saveObject();
The database query execution
Data.executeSP("sp_Object_InsertData", SearchNew.generateSQLParameterString("ObjectType", "ObjectInstance", "Parameter", "ParameterValue", "SourceServer"),
SearchNew.generateSQLParameterString(
ObjectType,
ObjectInstance.ToString(),
theNametext,
Regex.Replace(thetheVal, #"\'", "\'\'").Trim(),
SourceServer));
public static DataTable executeSP(string storedProc, SqlParameter[] parms = null, bool gatherParams = true, int commandTimeout = 0)
{
using (SqlCommand cmd = new SqlCommand())
{
if (commandTimeout != 0)
{
cmd.CommandTimeout = commandTimeout;
}
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedProc;
handleParameters(cmd.Parameters, storedProc, parms, gatherParams);
using (SqlConnection conn = Data.getConnection("AOD"))
{
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Connection = conn;
var da = new SqlDataAdapter();
var dt = new DataTable();
da.SelectCommand = cmd;
da.Fill(dt);
return dt;
}
}
}
Thanks for any suggestions in advance.
The good news is that this can just be removed but the bad news is that it creates an annoying programming environment where the XML data files this system is based don't get updated during an error but the updated SQL data that is mirrored out to is retained... Don't ask why that's how it is... it just is.
Apparently I had added a try/catch block around a function that ran a Stored procedure because the data being passed in would sometimes need the function and sometimes not (and thus fail, hence the try catch). Unfortunately when the db call failed the transaction associated w/ the TransactionScope was dropped and the try catch kept the program going.
I removed the the try catch block and replaced it w/ a check on the data passed into the function before determining whether or not to execute the Stored Procedure and now it is once again operating properly.
TL;DR - Check to see if any database calls are failing w/i the transactionScope as it might lead to dropping of the transaction.

Convert date in SQLite?

I created an scheduler application with SQL server and now i want to make another one using SQLite. I have a convert query in SQL and it does not work in SQLite. Can anyone help?
try
{
ObservableCollection<Classes.EventClass> listEvents = new ObservableCollection<EventClass>();
SQLiteConnection conn = new SQLiteConnection(#"Data Source=Scheduler.db;Version=3;");
string query= "Select * from Sche_Event where CONVERT(DATE,Event_TimeFrom) = CONVERT(DATE,'" +d.ToString("yyyy-MM-dd HH:mm:ss") + "') ORDER BY Event_TimeFrom ASC";
SQLiteCommand command= new SQLiteCommand(query, conn);
conn.Open();
SQLiteDataReader dr = command.ExecuteReader();
while (dr.Read())
{
EventClass dog = new EventClass();
dog.DogID = dr.GetInt32(0);
dog.DogName = dr.GetString(1);
dog.DogText = dr.GetString(2);
dog.DogPriority = dr.GetInt32(3);
dog.DogTimeFrom = dr.GetDateTime(4);
dog.DogTimeTo = dr.GetDateTime(5);
dog.KliID = dr.GetInt32(6);
listEvents .Add(dog);
}
return listEvents ;
}
catch (Exception)
{
return null;
}
I expect that my code goes to While() and read the information about the Event but all it does it goes to Catch() and returns nothing.
The query in SQL works just fine but i dont not work with SQLite :(
Of course the statement doesn't work in SQLite, because convert() is not a known function there. But if you're lucky you don't even need it, depending on the format in which the timestamp is stored in your SQLite table. As you didn't provide any sample data nor described what you actually want to do, you could either read the SQLite doc about date and time functions or rephrase your question to "How do I do X in SQLite?".

Passing a user defined table type to SQL function in Ormlite

I've to pass a table to a SQL function (till now I've passed to stored procedures and everything was fine)
Consider the following snippet
var dataTable = new DataTable();
dataTable.Columns.Add(new DataColumn("ID", typeof(Guid)));
foreach (var o in orders)
{
var r = dataTable.NewRow();
r["ID"] = o;
dataTable.Rows.Add(r);
}
var res = db.Exec(cmd =>
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("INPUT", dataTable));
cmd.CommandText = "SELECT * FROM FUNCTION";
return cmd.ConvertToList<MyObj>();
});
I'm not aware if parameters are considered when specifing CommandType as Text, I've tried on SQLServer and it works...
What am I doing wrong? is this a limitation of ServiceStack's OrmLite?
Thanks
When manually populating cmd as in your example you're using ADO.NET (i.e. not OrmLite), so you need to find out the correct way to call a SQL Server function from ADO.NET.
It seems you can only pass a datatable to a UDF from SQL Server 2008+ and requires that your TableType parameter is READONLY.

Return Two Data Sets

So I'm passing a ClientID to my DB and using that to look up all their details, then I want to use those details to also get all other users closely matching the details. I have all this written but my problem is I want to return the initial user's details also. For example;
Select Details = #UserDetails
from UnregisteredUserTable
where UserId = #UserID
Select BunchOfUsersWithMatchingData
from RegisteredUserTable
where UserDetails like #UserDetails
Obviously I've removed unnecessary info. But as you can see this returns all the data of the matching users but not the initial user's details. Could I use a CTE somehow?
UPDATE
Apologies, no idea my data access mattered. I'm doing pretty much the following atm but can change it no problem.
Dim results = thisObjectContext.MatcherSP(UserID)
For Each obj In results
TableData.Rows.Add(obj.IdNumber, obj.name,
obj.emailaddress1, obj.telephone1, obj.telephone2, obj.address1_line1,
obj.address1_line2)
Next
UPDATE 2
ok so I'm just using the two selects in my SP and it runs fine in SQL Server. But when I try to add it to my dbml in Visual Studio I get a strange error:
Unable to extract stored procedure 'dbo.MySP' because its result set contains muultiple anonymous columns.
Any ideas about that?
Well, this isn't VB code, but I will keep it as simple as possible.
Use a SqlDataAdapter to fill a data set. Results from both your select statements will populate different tables in the the DataSet.
cmd.CommandText = "MatcherSP";
cmd.CommandType = CommandType.StoredProcedure;
adapter = new SqlDataAdapter(cmd);
ds = new DataSet();
adapter.Fill(ds);
You can then access the data as follows:
tableA = ds.Tables[0];
tableB = ds.Tables[1];
You can use the SqlDataReader's nextresult() method.
using(SqlCommand cmd = new SqlCommand("NameOfSP",c))
{
cmd.CommandType = CommandType.StoredProcedure;
using(SqlDataReader d = cmd.ExecuteReader())
{
while(d.Read()){
//Result data from the first select
}
d.NextResult();
while(d.Read()){
//Result data from the second select
}
}
}
http://twogeeks.mindchronicles.com.dnpserver.com/?p=28&cpage=1#comment-37818
Brilliant article, outlined very clearly exactly what I wanted to do.

linq with Msaccess [duplicate]

I have a *.MDB database file, and I am wondering if it is possible or recommended to work against it using LINQ in C#. I am also wondering what some simple examples would look like.
I don't know a lot about LINQ, but my requirements for this task are pretty simple (I believe). The user will be passing me a file path to Microsoft Access MDB database and I would like to use LINQ to add rows to one of the tables within the database.
What you want is a LINQ to ODBC provider, or a LINQ to JET/OLEDB provider.
Out of the box, MS doesn't make one. There may be a 3rd party who does.
Actually I recently (today) discovered that you can access an Access database with LinqToSql. It must be in the 2002 or newer format, you will not be able to drag and drop the tables to your datacontext so either manually create the objects in your dbml or you can use SQL Server Migration for Access to move it to a sql server and then drag and drop all you want. When you want to actually create the context pass it an OleDbConnection. Use your standard Jet.OLEDB.4.0 connection string on the OleDbConnection and you are good to go. Not sure of the limitation this may incurr though. I just did a quick sample and did an OrderBy without issue.
I wrote a small sample program to test this out with David's answer. You'll need to make an access database and manually create the DBML for Linq-to-SQL, as you cannot drag 'n drop them.
Inserts fail, citing Missing semicolon (;) at end of SQL statement. but queries seem to work alright.
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using Linq2Access.Data;
namespace Linq2Access
{
class Program
{
static readonly string AppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
static readonly string DbPath = Path.Combine(AppPath, "Data", "database.accdb");
static readonly string DbConnString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + DbPath + "';Persist Security Info=False;";
static void Main(string[] args)
{
if (!File.Exists(DbPath))
throw new Exception("Database file does not exist!");
using (OleDbConnection connection = new OleDbConnection(DbConnString))
using (DataRepositoryDataContext db = new DataRepositoryDataContext(connection))
{
List<dbProject> projects = new List<dbProject>();
for (int i = 1; i <= 10; i++)
{
dbProject p = new dbProject() { Title = "Project #" + i };
for (int j = 1; j <= 10; j++)
{
dbTask t = new dbTask() { Title = "Task #" + (i * j) };
p.dbTasks.Add(t);
}
projects.Add(p);
}
try
{
//This will fail to submit
db.dbProjects.InsertAllOnSubmit(projects);
db.SubmitChanges();
Console.WriteLine("Write succeeded! {0} projects, {1} tasks inserted",
projects.Count,
projects.Sum(x => x.dbTasks.Count));
}
catch(Exception ex)
{
Console.WriteLine("Write FAILED. Details:");
Console.WriteLine(ex);
Console.WriteLine();
}
try
{
//However, if you create the items manually in Access they seem to query fine
var projectsFromDb = db.dbProjects.Where(x => x.Title.Contains("#1"))
.OrderBy(x => x.ProjectID)
.ToList();
Console.WriteLine("Query succeeded! {0} Projects, {1} Tasks",
projectsFromDb.Count,
projectsFromDb.Sum(x => x.dbTasks.Count));
}
catch (Exception ex)
{
Console.WriteLine("Query FAILED. Details:");
Console.WriteLine(ex);
Console.WriteLine();
}
Console.WriteLine();
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
}
You can use a DataSet. There are linq extensions that will allow you to query the data with all that LINQ goodness we have become use to :)
eICATDataSet.ICSWSbuDataTable tbl = new eICATDataSet.ICSWSbuDataTable();
ICSWSbuTableAdapter ta = new ICSWSbuTableAdapter();
ta.Fill(tbl);
var res = tbl.Select(x => x.ProcedureDate.Year == 2010);
I have seen this question a lot and in several fora. I made a go at it and here is a complete answer for those who have been looking at it.
LinQ was not made for Access. However, many of the queries will work with Access, including delete procedure. So, according to me, there are only 2 crucial deficiencies when working with Access, which are:
not being able to save data.
not being able to drag and drop objects onto the dbml
Insert will fail with the error "missing semicolon (;)". This is because LinQ save procedure was made to save data and retrieve the primary key ID of the record saved in one go. We know that you cannot execute multiple SQL statements in Access, so that is the reason for that failure.
Update will fail with the error "record not found". An update procedure will of cause look for the record to be updated then update it. I cannot tell why it wouldn't find it, when normal LinQ query to find a record works fine.
Because there is so much benefit to use LinQ, I figured out how to work around the deficiency, while enjoy the other benefits throughout my application. This is how (NB: My codes are in VB.net, but you can convert if required):
Create the LinQ to SQL (.dbml) class to manage your LinQ against the access database, and a way to manager your save procedure. Below is the full procedures of what I created and I now work with LinQ to Access without any problems:
Add a DataGridView on a form. Add buttons for Add, Edit & Delete
Code to fill the grid:
Private Sub ResetForm()
Try
Using db As New AccessDataClassesDataContext(ACCCon)
Dim rows = (From row In db.AccountTypes
Where row.AccountTypeID > 1
Order By row.AccountTypeID Ascending
Select row).ToList()
Me.DataGridView1.DataSource = rows
End Using
Catch ex As Exception
MessageBox.Show("Error: " & vbCr & ex.ToString, "Data Error", MessageBoxButtons.OK)
End Try
End Sub
DetailForm
Code to set control values
Private Sub ResetForm()
Try
If _accountTypeID = 0 Then
Exit Sub
End If
Using db As New AccessDataClassesDataContext(ACCCon)
'Dim rows = (From row In db.AccountTypes
' Where row.AccountTypeID = _accountTypeID
' Order By row.AccountTypeID Ascending
' Select row.AccountTypeID, row.AccountType, row.LastUpdated).ToList()
Dim rows = (From row In db.AccountTypes
Where row.AccountTypeID = _accountTypeID
Select row).ToList()
For Each s In rows
Me.AccountTypeIDTextBox.Text = s.AccountTypeID
Me.myGuidTextBox.Text = s.myGuid
Me.AccountTypeTextBox.Text = s.AccountType
Me.AcHeadIDTextBox.Text = s.AcHeadID
Me.DescriptionTextBox.Text = s.Description
Me.LastUpdatedDateTimePicker.Value = s.LastUpdated
Next
End Using
Catch ex As Exception
End Try
End Sub
LinQToSQLClass
You will have to add the data objects to the dbml manually since you cannot drag and drop when using Access. Also note that you will have to set all the properties of the fields correctly in the properties windows. Several properties are not set when you add the fields.
Code to Save
Public Function SaveAccountType(Optional ByVal type As String =
"Close") As Boolean
Dim success As Boolean = False
Dim row As New AccountType
Using db As New AccessDataClassesDataContext(ACCCon)
If _accountTypeID > 0 Then
row = (From r In db.AccountTypes
Where r.AccountTypeID = _accountTypeID).ToList()(0)
If String.IsNullOrEmpty(row.AccountTypeID) Then
MessageBox.Show("Requested record not found", "Update Customer Error")
Return success
End If
End If
Try
With row
.myGuid = Me.myGuidTextBox.Text
.AccountType = Me.AccountTypeTextBox.Text
.Description = Me.DescriptionTextBox.Text
.AcHeadID = Me.AcHeadIDTextBox.Text
.LastUpdated = Date.Parse(Date.Now())
End With
If _accountTypeID = 0 Then db.AccountTypes.InsertOnSubmit(row)
db.SubmitChanges()
success = True
Catch ex As Exception
MessageBox.Show("Error saving to Customer: " & vbCr & ex.ToString, "Save Data Error")
End Try
End Using
Return success
End Function
Now replace these two lines:
If _accountTypeID = 0 Then db.AccountTypes.InsertOnSubmit(row)
db.SubmitChanges()
with something like this:
Dim cmd As IDbCommand
cmd = Me.Connection.CreateCommand()
cmd.Transaction = Me.Transaction
cmd.CommandText = query
If myGuid.Trim.Length < 36 Then myGuid = UCase(System.Guid.NewGuid.ToString())
cmd.Parameters.Add(New OleDbParameter("myGuid", row.myGuid))
cmd.Parameters.Add(New OleDbParameter("AccountType", row.AccountType))
cmd.Parameters.Add(New OleDbParameter("Description", row.Description))
cmd.Parameters.Add(New OleDbParameter("AcHeadID", row.AcHeadID))
cmd.Parameters.Add(New OleDbParameter("LastUpdated", Date.Now))
If AccountTypeID > 0 Then cmd.Parameters.Add(New OleDbParameter("AccountTypeID", row.AccountTypeID))
If Connection.State = ConnectionState.Closed Then Connection.Open()
result = cmd.ExecuteNonQuery()
cmd = Me.Connection.CreateCommand()
cmd.Transaction = Me.Transaction
cmd.CommandText = "SELECT ##IDENTITY"
result = Convert.ToInt32(cmd.ExecuteScalar())
The last part of the code above is what gets you the ID of the record saved. Personally, I usually make that an option, because I don't need it in most of the cases, so I don't need to add that overhead of fetching back data every time a record is saved, I am happy just to know a record was saved.
That is the overhead added to LinQ, which causes Insert to fail with Access. Is it really necessary to have it? I don't think so.
You may have noted that I normally put my Update and Insert procedures together, so that saves me time and has address both the Insert & Update procedures in one go.
Code for Delete:
Private Sub DelButton_Click(sender As Object, e As EventArgs) Handles DelButton.Click
Using db As New AccessDataClassesDataContext(ACCCon)
Dim AccountTypeID As Integer = Me.DataGridView1.CurrentRow.Cells(0).Value
Dim row = From r In db.AccountTypes Where r.AccountTypeID = AccountTypeID
For Each detail In row
db.AccountTypes.DeleteOnSubmit(detail)
Next
Try
db.SubmitChanges()
Catch ex As Exception
' Provide for exceptions.
MsgBox(ex)
End Try
End Using
End Sub
Now you can enjoy LinQ to Access! Happy coding :)
LINQ to SQL only works for SQL Server databases. What you need is the Microsoft Entity Framework. This makes object oriented access to your mdb. From this you can run LINQ queries.
http://msdn.microsoft.com/en-us/library/aa697427(vs.80).aspx

Resources