Database changes do not persist after ObjectContext.SaveChanges() is called - c#

I am trying to insert a record into a table using an Entity Framework. I made my database (mdf file), made a model from the database (edmx), and now I am trying to insert a record.
Users Brad = new Users
{
UserInitials="BCH",
UserName="Brad"
};
using (WeighProgramDatabaseEntities wDB = new WeighProgramDatabaseEntities())
{
wDB.Users.AddObject(Brad);
int res = wDB.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
List<Users> lwt = wDB.Users.ToList(); //call to look at the inserted data
}
When I follow this procedure it appears everything works fine. My wDB.Users.ToList(); returns one record which is the one I just inserted. However, if I run my code again, removing the insert stuff and just call the query (leaving just) List<Users> lwt = wDB.Users.ToList(); my query returns no records. What should I be doing differently to get my changes to persist? Why did my records "go away"?
WeighProgramDatabaseEntities inherits from ObjectContext.
This is my first attempt at using EF stuff so some of my verbiage might not be correct and I might not have included something important - let me know.
Edit:
I think my problem is that my Insert/Update/Delete functions are not mapped. But I'm not sure how to create a stored procedure for them to map to...
Edit2:
Ok, phew, I figured out the stored procedure mapping. and now that I have an INSERT mapped, and I call AddObject it will actually be inserted.
A little more complicated than I would have expected. For example if I right click on a table in my Database Explorer and select New Query it brings me to a great query designer window which let's me create Inserts, Selects, Updates... but there is no Save button! It's grey out. I need to write them from another route with does not let me generate Inserts/Deletes/Updates via a GUI. Which is ok I guess because they are not much text, but still a bit of a hassle.

I think that your ConnectionString points to the mdf in the Debug/Release folder of your solution, and that file is probably overwritten each time you build your application that's why you think your changes are not persisted. Try setting the ConnectionString to point to the mdf in the project folder (the one that is actually reference by the project).

You can also set the "Copy to output directory" file property to "Copy if newer" rather than "Copy always". If you update the schema of the database in your project then it becomes newer than the database file in the debug directory and it will be overwritten on compile.

Related

What's causing a Concurrency Violation when using a MySQL linked Dataset via TableAdapters?

The more I read on this, the more confused I get, so hope someone can help. I have a complex database setup, which sometimes produces the error on update:
"Concurrency violation: the UpdateCommand affected 0 of the expected 1 records"
I say sometimes, because I cannot recreate conditions to trigger it consistently. I have a remote mySQL database connected to my app through the DataSource Wizard, which produces the dataset, tables and linked DataTableAdapters.
My reading suggests that this error is meant to occur when there is more than one open connection to the database trying to update the same record? This shouldn't happen in my instance, as the only updates are sequential from my app.
I am wondering whether it has something to do with running the update from a background worker? I have my table updates in one, for example, thusly:
Gi_gamethemeTableAdapter.Update(dbDS.gi_gametheme)
Gi_gameplaystyleTableAdapter.Update(dbDS.gi_gameplaystyle)
Gi_gameTableAdapter.Update(dbDS.gi_game)
These run serially in the backgroundworker, however, so unsure about this. The main thread also waits for it to finish, and there are no other db operations going on before or after this is started.
I did read about going into the dataset designer view, choosing "configure" in the datatableadapter > advanced options and setting "Use optimistic concurrency" to false. This might have worked (hard to say because of the seemingly random nature of the error), however, there are drawbacks to this that I want to avoid:
I have around 60 tables. I don't want to do this for each one.
I sometimes have to re-import the mysql schema into the dataset designer, or delete a table and re-add it. This would obviously lose this setting and I would have to remember to do it on all of them again, potentially. I also can't find a way to do this automatically in code.
I'm afraid I'm not at code level in terms of the database updates etc, relying on the Visual Studio wizards. It's a bit late to change the stack as well (e.g. can't change to Entity Framework etc).
SO my question is:
what is/how can I find what's causing the error?
What can I do about it?
thanks
When you have tableadapters that download data into datatables, they can be configured for optimistic concurrency
This means that for a table like:
Person
ID Name
1 John
They might generate an UPDATE query like:
UPDATE Person SET Name = #newName WHERE ID = #oldID AND Name = #oldName
(In reality they are more complex than this but this will suffice)
Datatables track original values and current values; you download 1/"John", and then change the name to "Jane", you(or the tableadapter) can ask the DT what the original value was and it will say "John"
The datatable can also feed this value into the UPDATE query and that's how we detect "if something else changed the row in the time we had it" i.e. a concurrency violation
Row was "John" when we downloaded it, we edited to "Jane", and went to save.. But someone else had been in and changed it to "Joe". Our update will fail because Name is no longer "John" that it was (and we still think it is) when we downloaded it. By dint of the tableadapter having an update query that said AND Name = #oldName, and setting #oldName parameter to the original value somedatarow["Name", DataRowVersion.Original].Value (i.e. "John") we cause the update to fail. This is a useful thing; mostly they will succeed so we can opportunistically hope our users can update our db without needing to get into locking rows while they have them open in some UI
Resolving the cases where it doesn't work is usually a case of coding up some strategy:
My changes win - don't use an optimistic query that features old values, just UPDATE and erase their changes
Their changes win - cancel your attempts
Re-download the latest DB state and choose what to do - auto merge it somehow (maybe the other person changed fields you didn't), or show the user so they can pick and choose what to keep etc (if both people edited the same fields)
Now you're probably sat there saying "but noone else changes my DB" - we can still get this though, if the database has changed some values upon one save and you don't have the latest ones in your dataset..
There's another option in the tableadapter wizardd - "refresh the dataset" - it's supposed to run a select after a modification to import any latest database calculated values (like auto inc primary keys or triggers/defaults/etc). Some query like INSERT INTO Person(Name) VALUES(#name) is supposed to silently have a SELECT * FROM PERSON WHERE ID = last_inserted_id() tagged on the end of it to retrieve the latest values
Except "refresh the dataset" doesn't work :/
So, while I can't tell you exactly why youre getting your CV exception, I hope that explaining why they occur and pointing out that there are sometimes bugs that cause them (insert new record, calculated ID is not retreieved, edit this recent record, update fails because data wasn't fresh) will hopefully arm you with what you need to find the problem: when you get one, keep the app stopped on the breakpoint and inspect the datarow: take a look at the query being run and what original/current values are being put as parameters - inspect the original and current values held by the row using the overload of the Item indexer that allows you to state the version you want and look in the DB
Somewhere in all of that there will be the mismatch that explains why 0 records were updated - the db has "Joe" as the name or 174354325 as the ID, your datarow has "John" as the original name or -1 as the ID (it never refreshed), and the WHERE clause is finding 0 records as a result
Some of your tables will contain a field that is marked as [ConcurrencyCheck] or [TimeStamp] concurrency token.
When you update a record, the SQL generated will include a WHERE [ConcurrencyField]='Whatever the value was when the record was retrieved'.
If that record was updated by another thread or process or something other than the current thread, then your UPDATE will return 0 records updated, rather than the 1 (or more) that was expected.
What can you do about it? Firstly, put a try/catch(DbConcurrencyException) around your code. Then you can re-read the offending record and try and update it again.

Set a permanent value of a variable without using a database

I don't know how to describe it thoroughly in the title, but I need to set a permanent value of a variable/flag once a process has return true and maybe set some flag in the program itself the value rather than saving it to database. And once that variable/flag has already have that value then the program won't run the process again and just use the value. Is it possible? I'm using VB.Net. I can't use the database because database can be overridden and change values by using query. Thanks in advance!
You can simply use binary/XML serialization in a file to save the state of that variable through your program. Every time you restart your app you can access the value from that file to get its current state.
You can look at this example - http://www.centerspace.net/examples/nmath/csharp/core/binary-serialization-example.php
Basically, you will not save the value in the database but in a file. Anyways you need to persist the value somewhere.
Some ways below
You did not specify if you are afraid that your application or another one could change the value
How I would do it
My ideas below
1)You could use an xml file for example and zip a copy of it with a strong password. Every time you update the first xml you will update also the encrypted zipped xml.You can use a FileSystemWatcher and capture any file change, so if something/someone has changed the file you just get a new copy from the zip
2)You can store the value in the DB and add a trigger to prevent delete/update
for example
-- delete trigger
CREATE TRIGGER Function_Value_Deleted
ON [dbo].[FunctionsValueTb]
AFTER DELETE
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (
SELECT [Flag] FROM deleted
)
BEGIN
ROLLBACK;
RAISERROR ('Record deletion is not allowed...', 16, 1);
END
END
*You can use also use THROW rather than RAISERROR
**Do the same for the insert and update actions
***You can also store the value into a log table or send an email
I found myself in a situation quite similar to yours a couple of days ago.
In the end, I decided to use the settings functionaly provided by .NET: it is easy to use and maintain, and so far it has given me good results.
Yo can see here what I am talking about:
Best practice to save application settings in a Windows Forms Application
That thread refers to C# but is easily applicable for VB.NET: I just had to follow the same steps in order to add the Settings file:
Right click on the project in Solution Explorer, choose Properties.
Select the Settings tab, click on the hyperlink if settings doesn't
exist. Use the Settings tab to create application settings. Visual
Studio creates the files Settings.settings and
Settings.Designer.settings that contain the singleton class Settings
inherited from ApplicationSettingsBase
And then, from my code, I use the settings like this:
Dim lastExecDate As Date = My.Settings.LastSuccessfulExecution
lastExecDate = lastExecDate.AddDays(1)
// Perform my next execution and do other stuff
My.Settings.LastSuccessfulExecution = lastExecDate
My.Settings.Save()
Next time you retrieve the parameter LastSuccessfulExecution, it will have the updated value.
One more remark, as stated in the post that I linked above:
Note that you need to set the scope property of your settings. If you
select Application scope then Settings.Default.< your property > will
be read-only
Finally, I see that you are using this to store the expiration date of a product, so you don't want the user messing around with it. According to this post, the actual values of the parameters are stored in an Application Data user folder. It is somehow obfuscated since it is not that easy to find and besides it contains a hash on its name... I don't know if that is well hidden enough for you.
If you want the value only to exist in memory when the application is running then you can use the main thread of the application and use:
int slotData = randomGenerator.Next(1, 200);
//to set the data
Thread.SetData(Thread.GetNamedDataSlot("SomeDataKey"), slotData);
//to get the data
int newSlotData = (int)Thread.GetData(Thread.GetNamedDataSlot("SomeDataKey"));
Or you can use the Windows Registry if your app only runs on Windows, if not then you would have to write the value/object to a file and read it from there.

Getting inserted record from DataSet TableAdapter.Insert

I'm using a DataSet with the DataSet designer on xsd file (System.Data v 4 in VS 2015). I've been searching for how to get the ID for a newly created record. I see on the designer advanced options there is an option for reading back the record for insert and updates. I also see the SELECT included in the INSERT INTO statement. However the designer generated code runs ExecuteNonQuery and returns the number of records affected. How do we get access to that record that was read back in?
Most solutions I've read involve creating your own query or sp that is set to ExecuteScalar and selects the ##ROWIDENTITY. Either that or timestamp the new record and get it back that way. But if the read-back is already being done then I should be able to get to it, right? What' the trick? What happens to that data that's been read back in?
TIA,
Mike

TableAdapter Insert not persisting data

I created a service-based local db via Visual Studio 2013 Express edition.. The connection string, dataset and TableAdapter were added automatically.
On click of a button, I am trying to insert some data by calling TableAdapter.Insert
As it is I already have a dataGridView's datasource bound to the dataset, So I immediately see that the data was inserted in table properly at run time, But when I close the application, The actual DB dosn't contain the data. Therefore, The data isn't actually inserted in DB.
According to http://msdn.microsoft.com/en-us/library/ms233812%28v=vs.110%29.aspx
With insert, you have to only call insert, yet I am calling Update and AcceptChanges on table, for safety, well I tried the first way shown in link (i.e. creating a row and adding it to dataset then calling update) as well, but it seems the data isn't being inserted in DB at all.
Finally, the insert code, rds is DataSet and ta is TableAdapter
private void AddBtn_Click(object sender, EventArgs e)
{
ta.Insert("foo", "bar", 2, "zing", "tada");
ta.Fill(rds.reminders);
rds.reminders.AcceptChanges();
ta.Update(rds.reminders);
}
It turned out that, as I was using the VS compiled application each time the mdf database was being overwritten, hence the changes I made were completely erased,
The possible solution could be one of following
1)Change the connectionstring to point to database that is in Debug folder, which wont be overwritten each time you compile and run the application
2)You could simply let the connectionstring be as it is and just test it through detached compiler mode.
I was able to figure this out due to the following stackoverflow link I suddenly sumbled upon after 2 days.
Database changes do not persist after ObjectContext.SaveChanges() is called
ClearBeforeFill Property
http://msdn.microsoft.com/en-us/library/bz9tthwx.aspx
"By default, every time you execute a query to fill a TableAdapter's
data table, the data is cleared and only the results of the query are
loaded into the table."

C# LINQ-to-SQL - not updating table with stored procedure

SOLVED
I have problems with Linq to SQL inserting/updating/deleting dataset from table with stored procedures.
I have checked data connections, points on the same table that has been created when adding connection
I'm using Linq-to-Sql classes designer for managing behavior of sprocs for my tables (insert, update, delete)
I have same app created with different code style and everything works just fine but i can't include location change (when someone else installs app) in my .cs files, so I want to do it on "right way" and make it work for everyone.
using sql server 2012 and vs2010
Here is my problem.
this is included in form as reference for DataContext
private ZavrsniradDataContext poziv = new ZavrsniradDataContext();
//I've tried without private, and including this into methods where I'm using it and nothing.
after I declare some variables for sending it to sproc I call sproc for altering the table..
poziv.p_IzmjenaMjesta(#ID, #Naziv, #PP, #IDZupanije);
poziv.SubmitChanges();
note: I'm sending ID number of the dataset I want to change, for comparison..
Alter proc [dbo].[p_IzmjenaMjesta]
#ID int,
#Naziv varchar(30),
#PP int,
#IDZupanije int
.
.
.
Update Mjesto
SET Naziv= #Naziv, PostanskiPretinac = #PP, IDZupanije = #IDZupanije
WHERE ID = #ID;
After I call the sproc, my table is being refreshed, and all changes are visible. But after a while, all changes dissapear. I believe all my sprocs and functions are working properly, with my table, but somehow I'm doing something wrong. everything works perfecrly when I run sprocs in Management studio.
My other app contains this definition in beggining of the form, and everything works perfectly. updateing, inserting, deleting! But i don't want that solution because then I have to create fixed location for my database on every PC. I just want to install it and run it :)
public partial class Mjesta : Form
{
ZavrsniRad poziv;
public Mjesta()
{
InitializeComponent();
poziv = new ZavrsniRad(#"c:\zavrsni_rad\Zavrsni rad.mdf");
}
NOTE:
I'm using exact copy of both databases on different locations with same sprocs/functions/dataset for different apps, and one is working, the other one is not working.
and that grinds my gears :(
I have solved it.
app is running and working properly...
I forgot on that little thing :(
Problem solver, add all rights on modifying your database to every account you have.
That also includes rights to log file.
(i dont have rep points for posting pics)
http://www.pohrani.com/f/v/AQ/2bPLiVcP/slika.jpg

Categories

Resources