SQLite Update "Cannot update List1: it has no PK" - c#

I'm having an issue when trying to update a value on my database and can't really find much if any help through Google.
I want to set a column called IsOpen (bool but because of SQLite I'm using integer) to 0 (false) if the EndDate for this entry is today (now). When I run my UPDATE query I get the following exception; "Cannot update List1: it has no PK".
I don't understand this because I've checked my Model class and I clearly have a PK set;
[SQLite.AutoIncrement, SQLite.PrimaryKey]
public int GoalID
{
get { return _goalID; }
set
{
if (_goalID != value)
_goalID = value;
OnPropertyChanged("GoalID");
}
}
I'm attempting to update this way;
string sql = #"UPDATE GoalsTrackerModel
SET IsOpen = '0'
WHERE EndDate = datetime('now')"; // I've also tried date('now')
_dbHelper.Update<GoalsTrackerModel>(sql);
My Update<> looks like;
public void Update<T>(string stmt) where T : new()
{
using (var conn = new SQLiteConnection(App.ConnectionString))
{
var result = conn.Query<T>(stmt);
if (result != null)
{
conn.RunInTransaction(() =>
{
conn.Update(result);
});
}
}
}
But like I said, I keep getting "Cannot update List1: it has no PK". What's throwing me off as well is if I change the WHERE to something like; WHERE IsOpen = '1' then it'll update all the values that have 1 to 0, but it'll still give me the "Cannot update List1: it has no PK" message.
Maybe my WHERE is wrong when checking if the EndDate = now? I'm implementing all this as soon as the page is opened. Any ideas?
Thanks.

"[SQLite.AutoIncrement, SQLite.PrimaryKey]" is C# code, not SQL code. Just because you've defined in C# what your primary key is, doesn't mean the SQLite table is really defined that way. You'll need to look at the table itself as it is defined within SQLite to fix that.

My Update method was causing the problem. Changed it and started using SQLiteCommand and ExecuteNonQuery instead of the SQLiteConnection's Update().
In case it helps anyone in the future, here's my new update method;
public void Update<T>(string stmt, string table) where T : new()
{
using (var conn = new SQLiteConnection(App.ConnectionString))
{
var result = conn.Query<T>("SELECT * FROM " + table);
if (result != null)
{
conn.RunInTransaction(() =>
{
SQLiteCommand cmd = conn.CreateCommand(stmt);
cmd.ExecuteNonQuery();
});
}
}
}

Related

SaveChanges problem in N-Tier Architecture

Normally, with MVC I use db.savechanges() method after I do some processes. But check the below code when I use N-Tier Architecture in everyloop its gonna insert in this way but I dont want it. I have to check all the items first. If there is no problem then I have to insert it all together.
foreach (var item in mOrderList)
{
MOrder mOrder = new MOrder();
mOrder.StatusAdmin = false;
mOrder.Date = DateTime.Now;
mOrder.StatusMVendor = "Sipariş alındı.";
mOrder.HowMany = item.HowMany;
mOrder.MBasketId = item.MBasketId;
mOrder.MProductId = item.MProductId;
mOrder.MVendorId = item.MVendorId;
mOrder.WInvestorId = item.WInvestorId;
MProduct mprostock = _imProductService.GetMProductById(item.MProductId);
if (mprostock.Stock<=0)
{
return ReturnErrorAndSuccess(HttpStatusCode.NotFound, "MProduct", mprostock.Name + " ürününde stok kalmadığı için işlem tamamlanamadı.");
}
_imOrderService.InsertMOrder(mOrder);
}
all you have to do is:
first you should define a method that get list of mProductId and then return list of MProduct.
after that you should check if there is any record with Stock<=0 then return your error.
-also for your insert you should define a method that get list of MOrder and return appropriate datatype for example Boolean.
public List<MProduct> GetMProductByIds(List<MProductId> mProductId)
{
//getting record code
}
public bool AddMOrder(List<MOrder> mOrder)
{
//inserting record code
}

How to properly get index of a custom class list <CustomClass> and check if the list value in the selected index is equal to int [c#]

I have created a custom class list which is filled from the result of a query. Specifically, me query returns (int, timestamp) 2 columns -> 2 values.
public class SucessfulCompletion
{
public int Result;
public string Timestampvalue;
public SucessfulCompletion(int result, string timestamp) => (Result, Timestampvalue) = (result, timestamp);
}
public List<SucessfulCompletion> SuccessfulCalculationsTimestamp(string connectionstring)
{
List<SucessfulCompletion> QueryListResult = new List<SucessfulCompletion>();
using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
{
var query_table_timestamp = (
#"SELECT CASE
WHEN t.STATUS = 'SUCCESS' AND t.STATUS_DESCRIPTION = 'ALL QUERIES COMPLETED SUCCESSFULLY' THEN 1
ELSE 0
END SuccessfulCompletion, t.TIMESTAMP
FROM (SELECT TOP (1) l.TIMESTAMP, l.STATUS, l.STATUS_DESCRIPTION
FROM LOG_DETAILS l
ORDER BY 1 DESC) t");
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(query_table_timestamp, sqlConnection))
{
using (SqlDataReader reader = sqlCommand.ExecuteReader())
{
while (reader.Read())
{
QueryListResult.AddRange(new List<SucessfulCompletion>
{
new SucessfulCompletion(reader.GetInt32(0), reader.GetDateTime(1).ToString())
});
}
reader.Close();
}
sqlCommand.Cancel();
}
}
return QueryListResult;
}
The code to create the custom class list was taken from this SO question
So the QueryListResult would be like [1, "2020-10-04 HH:MM:SS"]
Now I want to make an if statement to check if the first index of the QueryListResult is ether 0 or 1.
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0]=1) //my problem is here
{
//Enable is only if successful
PreviewCalculationsButton.IsEnabled = true;
PreviewReportButton.IsEnabled = true;
//add textbox of success
SQLSuccessfulTextCalculations.Text = String.Format("Completed On: {0}", reportsucessfulcompletion[1]);
}
else
{
//add textbox of fail
SQLFailedTextCalculations.Text = String.Format("Failed On: {0}", reportsucessfulcompletion[1]);
}
In the if statement I get an error
Cannot implicitly convert type 'int' to 'TestEnvironment.MainWindow.SucessfulCompletion'
I know it may be a silly question for someone experienced with C# but I am a newbie, so I would appreciate your help. Please inform me in the comments if the theme of the question is a duplicate one I will close the question.
You're comparing an jnstance of your class to a number.
These are different things.
And one equal sign sets rather than compares
Try
If ( reportsucessfulcompletion[0].Result == 1)
You should make these properties rather than variables.
I also recommend Dapper as a "micro" orm very close to the ado metal, but which saves a fair bit of coding whilst implementing best practice for you.

C# Entity Framework and stored procedures

I made my database with its stored procedures then attached it with my project Entity Framework database-first.
This function to insert a company info and return its id back and insert it to another table in relation with company table
public string InsertCompany(company company, out int index)
{
try
{
using (vendors_managerEntities db = new vendors_managerEntities())
{
db.companies.Add(company);
db.SaveChanges();
index = company.id_company;
return $"{company.name_company} Is Saved";
}
}
catch (Exception ex)
{
index = 0;
return ex.Message;
}
}
But when I tried to my stored procedure which has been created in database, I couldn't return any value the id always be 0
public string InsertCompany(company company, out int index)
{
try
{
using (vendors_managerEntities db = new vendors_managerEntities())
{
db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
index = company.id_company;
return $"{company.name_company} Is Saved";
}
}
catch (Exception ex)
{
index = 0;
return ex.Message;
}
}
I read that I can make it in SQL but I'm looking for a solution in C#, so I opened the stored procedure definition in C# and found the following code and was thinking if can I change its return value because it's not return the id value
public virtual int SP_insert_companies(string name, string website, string address, string country, string description)
{
var nameParameter = name != null ?
new ObjectParameter("name", name) :
new ObjectParameter("name", typeof(string));
var websiteParameter = website != null ?
new ObjectParameter("website", website) :
new ObjectParameter("website", typeof(string));
var addressParameter = address != null ?
new ObjectParameter("address", address) :
new ObjectParameter("address", typeof(string));
var countryParameter = country != null ?
new ObjectParameter("country", country) :
new ObjectParameter("country", typeof(string));
var descriptionParameter = description != null ?
new ObjectParameter("description", description) :
new ObjectParameter("description", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("SP_insert_companies", nameParameter, websiteParameter, addressParameter, countryParameter, descriptionParameter);
}
Please tell me if there's a solution in C# or should I go back to old code without stored procedure in that case?
The issue is that you are using a stored procedure to insert the company entity which I suspect does not cause the object to be refreshed by the context:
db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
You then try to get the id from the object which is 0 because it hasn't been refreshed:
index = company.id_company;
If you insist on using a stored procedure, what I would suggest is that you have the SP return the id of the company, then grab it from the call and use that as the value of index:
index = db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
Once you modify the SP, make sure to update the definition in your code so it knows to make a function that returns a value.
If you prefer to have it in the object itself, then make sure to update it manually, although I don't recommend this as the object is not in true sync with the database:
index = db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
company.id_company = index;
Based on what you're saying it's automatically going to the "Catch" and then the index is already set to 0. So chances are your code elsewhere, not listed is messing up. I suspect wherever your code for saving the company information isn't saving properly. Try manually inputting a company into the DB and then check it with your program. If it returns that it's "Saved" then you know your problem isn't within this method and is a result of your saving method.

Xamarin.Android SQLite SQLiteConnection.Insert does not return inserted object's ID

I'm very new to Xamarin and C# and trying to insert some sample data to my simple recipe database for testing purpourses.
However, when inserting a new recipe, the SQLiteConnection.Insert(data) does not return the ID of the inserted record as it should (see here), but instead returns 1 every time.
My insert data method:
public static int insertUpdate(Object data) {
string path = DB.pathToDatabase ();
DB.createDatabase ();
try {
var db = new SQLiteConnection(path);
int inserted = db.Insert(data);
if (inserted != 0)
inserted = db.Update(data);
return inserted;
}
catch (SQLiteException ex) {
return -1;
}
}
Inserting the sample data:
int stand = insertUpdate (new Recipe {
Name = "Standard",
Author = "Aeropress Nerd",
Description = "The perfect recipe for your first cup",
Type = "Default"
});
insertUpdate (new Step { Action = "Pour", Duration = 10, Recipe = stand });
insertUpdate (new Step { Action = "Stir", Duration = 20, Recipe = stand });
insertUpdate (new Step { Action = "Steep", Duration = 15, Recipe = stand });
int inv = insertUpdate (new Recipe {
Name = "Inverted",
Author = "Aeropress Nerd",
Description = "A more advanced brew using the inverted method.",
Type = "Default"
});
I cannot figure out what I am doing wrong. Thanks in advance for any help and sorry for the (probably) stupid question.
However, when inserting a new recipe, the SQLiteConnection.Insert(data) does not return the ID of the inserted record as it should (see here), but instead returns 1 every time.
I am pretty sure you downloaded some nuget package or component to include SQLite functionality to your Xamarin.Android App and it may be slightly different from the native implementation. Then, you should refer to the specific documentation for whatever it is that you're using on Xamarin.
My wild guess is that you are using this component. If I'm wrong, please comment to correct my answer. If I'm right, you should try this:
The object you want to insert
class Row
{
public int Id { get; set; }
}
class Recipe : Row
{
//Recipe's properties
}
class Step : Row
{
//Step's properties
}
The insertUpdate method definition
public static int insertUpdate(Row data) {
string path = DB.pathToDatabase ();
DB.createDatabase ();
try {
var db = new SQLiteConnection(path);
int inserted = db.Insert(data); //will be 1 if successful
if (inserted > 0)
return data.Id; //Acording to the documentation for the SQLIte component, the Insert method updates the id by reference
return inserted;
}
catch (SQLiteException ex) {
return -1;
}
}
The insertUpdate method usage
//As Step and Recipe both are derived classed from Row, you should be able to use insertUpdate indistinctively and without casting
insertUpdate (new Step { Action = "Steep", Duration = 15, Recipe = stand });
int inv = insertUpdate (new Recipe {
Name = "Inverted",
Author = "Aeropress Nerd",
Description = "A more advanced brew using the inverted method.",
Type = "Default"
});
why after inserting data to your db, you starting to update it by the same object right away. This code most likely redundant.
if (inserted != 0)
inserted = db.Update(data);
return inserted;

SQLite exception deleting row using sqlite-net

I got tired to search so here it goes my first SO question hoping someone had the same problem and can help me
Goal
I am trying to store my application data with a SQLite database
Application description
Windows 8 app C# XAML with local SQLite database using SQLite for Windows Runtime Extension and sqlite-net library
Table definition
public class Product {
private int _id;
[SQLite.PrimaryKey, SQLite.AutoIncrement]
public int ID
{
get { return _id; }
set { _id = value; }
}
private string _date;
public string DATE
{
get { return _date; }
set { _date = value; }
}
private string _desc;
public string DESC
{
get { return _desc; }
set { _desc = value; }
}
}
Problem1
public int Insert (object obj) description says the following:
Inserts the given object and retrieves its auto incremented primary key if it has one.
However everytime I insert a row it return 1. I can sucessfully insert with a auto-incremet ID but somehow it does not return me its ID. Why?
Problem 2
I can insert new rows but not delete them
Working around problem 1 to get last row generated ID, I try to delete rows but with no success.
See this example test that always fails:
using (var db = new SQLiteConnection(Path.Combine(_path, _dbname)))
{
var p1 = new Product() { DESC = "insert1", DATE = DateTime.Now.ToString() };
db.Insert(p1);
p1.ID = 1;
var p2 = new Product() { DESC = "insert2", DATE = DateTime.Now.ToString() };
// I am sure that this row is getting ID 2, so it will not have a wrong ID
p2.ID = 2;
db.Insert(p2);
var p3 = new Product() { DESC = "insert3", DATE = DateTime.Now.ToString() };
db.Insert(p3);
p3.ID = 3;
db.Delete<Product>(p2);
}
As you can see I try to insert 3 rows and delete the second one. The rows are inserted but I get the following SQLite.SQLiteException exception:
unable to close due to unfinalized statements or unfinished backups
Why? I don't open other connections before and after that.
Thanks in advance
Solved
Problem 1
+1 and thanks for #Bridgey for pointing out that function does not match it description and for the relevant search
The function does not return ID as it says but it defines the object ID. So when I insert a new Product, Product.ID will have last inserted ID.
Problem 2
I changed db.Delete<Product>(p2); to db.Delete(p2); and now it works. SQLite-net correctly identify the row as Product. I still don't know why the unable to close due to unfinalized statements or unfinished backups exception was happening. If someone knows why tell me please.
I think for problem 2, the issue is that you are passing the Product object as the parameter for the Delete method. The documentation says: Deletes the object with the specified primary key. I think the following should work:
db.Delete<Product>(p1.ID);
Regarding problem 1, the code of the Insert method of the sqlite-net package ends:
var count = insertCmd.ExecuteNonQuery (vals);
if (map.HasAutoIncPK) {
var id = SQLite3.LastInsertRowid (Handle);
map.SetAutoIncPK (obj, id);
}
return count;
As you can see, count is returned, even if id is set.
EDIT: Actually, according to the author this is deliberate.
"Insert returns the number of rows modified. The auto incremented columns are stored in the object. Please see the doc comments."
https://github.com/praeclarum/sqlite-net/issues/37

Categories

Resources