How to detect which column's value has updated in a Table - c#

Suppose I have a student registration form with 85 fields in my c# project and there are three buttons Save, Update, Delete.
The person who update the field knows very well that which field has updated but how other person will know that which field has updated ? Because...
If I click update button without changing any value, the Update query execute and the same update query execute too when I click update button with some changes.
So I want that database should detect which field has updated in update query.
The traditional way says that store the previous values and then compare them with new values one by one field. But this slow down the performance.
Any smart way ?

You can implement optimistic concurrent using a rowversion column. This avoids the need to check the old/new values of each column individually. Specify the original rowversion in the WHERE clause of the UPDATE statement and verify a row was updated. Proc example:
CREATE TABLE dbo.OptimisticConcurrencyExample(
OptimisticConcurrencyExampleID int NOT NULL
CONSTRAINT PK_OptimisticConcurrencyExample PRIMARY KEY
, Column1 int NOT NULL
, Column2 int NOT NULL
, Column3 int NOT NULL
, RowVersion rowversion
);
GO
CREATE PROC dbo.UpdateOptimisticConcurrencyExample
#OptimisticConcurrencyExampleID int
, #Column1 int
, #Column2 int
, #Column3 int
, #OriginalRowVersion rowversion
AS
UPDATE dbo.OptimisticConcurrencyExample
SET
Column1 = #Column1
, Column2 = #Column2
, Column3 = #Column3
WHERE
OptimisticConcurrencyExampleID = #OptimisticConcurrencyExampleID
AND RowVersion = #OriginalRowVersion;
IF ##ROWCOUNT = 0
BEGIN
RAISERROR('data updated or deleted by another user', 16, 1);
END
GO
When an optimistic concurrency error occurs, the app code can refresh the data with the current values, let the user know someone else updated the row, and allow the user to re-enter changes. You could get fancy and transparently merge pending changes in code (checking old/new values for each column) and notifying the user only if a conflict occurs. Similarly, you could present the user with a merge form after a conflict with the entered and current values side-by-side and the different values marked, etc.
But given conflicts are typically rare (unlikely that different users will update the same student record at the same time), the additional development effort may not be worth the trouble.

I have one way to do that thing using table versioning using that you can maintain every update, create, delete the record.
Tutorial for table versioning
Another Tutorial
The second way is you have to add audit table or field that store SQL query and also store which user change the data and also store last modified date like that.

Why do you need a save button and an update button? Same thing, right?
Modify the logic in your program to allow an update only if the user
changes the data on the student registration form relative to the
data in the database.
Read record from database
Store record data in a local DataRow or List
Load data onto the student registration form
update_button.enable = false
delete_button.enable = true
Create TextChange event methods for each TextBox on the student registration form
When a TextChange event method fires, compare the TextBox text to the corresponding DataRow field or List item (case
insensitive).
If they don't match, enable the update button
If the user clicks the update button, update the record and disable the update button

Related

Adding data to SQL tables in C#

This may or may not be a noob question, so I'm sure it should be solved easily enough.
I'm trying to use a table in a .mdf database for a contacts list programme I'm writing. For the ID for each of the entries I put, I've got an Identity seed of 1000, with in increment of 1. This works fine when I input things directly into the table. The first entry is 1000, the second is 1001 and so on. I put some text boxes onto the form, and dragged each data source from the table onto their respective text box, so that new entries can be added from the form. The first two entries, which I added directly into the table, show up fine on the form, with their ID as I've previously stated. It's when I try to add a new entry using the form and the controls that I've created that problems arise.
When I add a new entry, the Contact ID for the new entry is set to -1, then -2 and so on. Why is this happening, and how can I fix it?
Relevant snips:
First entry is in there and works fine
New entry has an ID of -1 for some reason
It is default value for IDENTITY fields.
As far as you cannot know which will be the value of an IDENTITY fields unless you insert the record, by default it show -1
The best way to avoid it, it now showing this field when you dateset is on INSERT mode.
In fact, if you try to set it value manually, you will get an error.
Make ID field read-only or don't show it altogether. The INSERT sql for your form should not include identity, i.e.
Insert into contacts (name, company, telephone, mobile, email)
Output INSERTED.*
Values (. . . . .)
If you in c# do command.ExecuteReader using above sql, you will get back all values including ID

Update older records when a new column is added

In my system, the client will fill in their information to register an account. For new registrations, the record will contain a new check-box option.
My Question is, how do I update the old records so that they contain a checked value for the check-box option that has been added?
My supervisor suggest I patch the data but I'm not sure where to start with this.
I think I got your question. You need to add few Boolean fields to your existing table with DEFAULT value TRUE.
ALTER TABLE TABLE1 add checkField bit default 1
You need to map the corresponding field to a check box in your UI. For existing client it will checked as the field is set true in database.
I assume you want to add a new bit column to an existing table and want all existing records have the value 1 in it.
To do this you first create the new column and than right after set all existing records to 1
alter table MyTable
add CheckedField bit
update MyTable set CheckedField = 1
If the new column is mandatory than you can do it like this :
alter table MyTable
add CheckedField bit not null default (1)

Update master/child rows

I have tables Cases and Degrees with relation Cases.CID - CDegrees.CID. CID is Primary Key of Cases with AutoIncrement.
Both tables are used in one form and it's assumed that user can add new records to both tables same time and than save whole master/child data in one GUI action.
So in Dataset where I created FK I set "Both Relation and Foreign Key Constraint" to ensure during update of new record in Cases table, retrieved IDENTITY value will cause child records to update from -1 to retrieved CID value.
When I update Cases adapter, it causes to retrieve an new IDENTITY value and cascade update in CDegrees child record works ok too. But CDegrees's update causes Insert script with [CID]=-1 (original value). I changed Insert parameter #CID of CDegrees to "Proposed" version but same happens (seen in SQL Profiler).
Actually my task is mach more complicated, I just simplified task to localize my problem.
To describe more clearly.
before update
Both [Cases] and [CDegrees] have one new records with [CID]=-1
after [Cases] update both [Cases] and [CDegrees] have new CID identity value, just [CDegrees].[CID] "Current" value is -1 and "Proposed" is retrieved identity.
But when I call [CDegrees]'s dataset update, it sends Insert Command to SQL with [CID]=-1 regardless that I specified #CID parameter source as proposed value of [CID].
That is strange and funny. It appeared that calendar control on detailsform somehow causes but preventing details data cascade update according master identity value of CID field.
I just changed date field bound control from MonthCalendar to DateTimePicker and it worked.

how to use SCOPE_IDENTITY() not in insert

I want to get new id(Identity) before insert it. so, use this code:
select SCOPE_IDENTITY() AS NewId from tblName
but is get this:
1- Null
2- Null
COMPUTED COLUMN VERSION
You'll have to do this on the sql server to add the column.
alter table TableName add Code as (name + cast(id as varchar(200)))
Now your result set will always have Code as the name + id value, nice because this column will remain updated with that expression even if the field are changed (such as name).
Entity Framework Option (Less ideal)
You mentioned you are using Entity Framework. You need to concatenate the ID on a field within the same record during insert. There is no capacity in SQL (outside of Triggers) or Entity Framework to do what you are wanting in one step.
You need to do something like this:
var obj = new Thing{ field1= "some value", field2 = ""};
context.ThingTable.Add(obj);
context.SaveChanges();
obj.field2 = "bb" + obj.id; //after the first SaveChanges is when your id field would be populated
context.SaveChanges();
ORIGINAL Answer:
If you really must show this value to the user then the safe way to do it would be something like this:
begin tran
insert into test(test) values('this is something')
declare #pk int = scope_identity()
print #pk
You can now return the value in #pk and let the user determine if its acceptable. If it is then issue a COMMIT else issue the ROLLBACK command.
This however is not a very good design and I would think a misuse of the how identity values are generated. Also you should know if you perform a rollback, the ID that would of been used is lost and wont' be used again.
This is too verbose for a comment.
Consider how flawed this concept really is. The identity property is a running tally of the number of attempted inserts. You are wanting to return to the user the identity of a row that does not yet exist. Consider what would happen if you have values in the insert that cause it too fail. You already told the user what the identity would be but the insert failed so that identity has already been consumed. You should report to the user the value when the row actually exists, which is after the insert.
I can't understand why you want to show that identity to user before insert, I believe (as #SeanLange said) that is not custom and not useful, but if you insist I think you can do some infirm ways. One of them is
1) Insert new row then get ID with SCOPE_IDENTITY() and show to user
2) Then if you want to cancel operation delete the row and reset
identity (if necessary) with DBCC CHECKIDENT('[Table Name]', RESEED,
[Identity Seed]) method
Other way is not using the Identity column and manage id column by yourself and it must be clear this approach can't be work in concurrency scenarios.
I think perhaps you're confusing the SQL identity with a ORACLE sequence.
They work completely different.
With the ORACLE sequence you'll get the sequence before you insert the record.
With a SQL Identity, the last identity generated AFTER the insert in available via the SCOPE_IDENTITY() function.
If you really need to show the ID to the user before the insert, your best bet is to keep a counter in a separate table, and read the current value, and increment that by one. As long as "gaps" in the numbers aren't a problem.

How to handle the Optimistic concurrency violation in the SQL DB?

I'm working on a real estate windows forms application on C# and SQL Server, which is also a multi-user app. I'm not using Entity Framework or LINQ in it!
I'm struggling on how to properly handle the optmistic concurrency violation. Assume there are two tables in DB, Properties and Addresses.
Properties contain general property details such as type of
property, no of rooms, price etc.
Addresses has the property addresses.
When my form loads, it loads all the information of properties to a datagridview. In this case it load all information in Properties and Addresses table to a datagridview.
When the user clicks a row on datagridview it loads the data on that row to an editing form. Then user updates and updates the db. In this situation an optimistic concurrency violation (OCV) can occur.
But How do I store the rowversion value of data that was fetched from Properties and Addresses in such a way that will help to handle this OCV violation?
Initially I was thinking about when the user double clicks on the row, the rowversions for that record in the Properties and Addresses tables can be stored in a transaction table. But after the records been loaded their rowversion values can be changed by another user thus whats displayed on the datagridview might be out dated. So when the user clicks a row, i need to send the exact data on the "grid" (not in DB) to the editing form along with their original rowversion values. Whats the proper way to do this?
P.S. I was told the proper way to handle OCV is to handle it in the SQL server and not in the ADO .NET since OCV is a database operation. So i would prefer guidance to handle it in the database!
thanks
Optimistic concurrency should be handle by database. For instance
CREATE TABLE Customer (
Id int IDENTITY (1, 1) NOT NULL,
FirstName nvarchar (256) NULL,
LastName nvarchar (256) NULL,
_rowVersion rowversion NOT NULL -- Value generated by SQL Server each time the row is updated
)
When loading rows you need to get the row version generated by SQL Server
SELECT Id, FirstName, LastName, _rowVersion
FROM Customer
When saving the row, you have to add a test to ensure the RowVersion has not changed between the time you load the row and now
UPDATE Customer SET
FirstName = #FirstName,
LastName] = #LastName,
WHERE Id = #Id AND _rowVersion] = #_rowVersion -- Update using the row version
-- This part can be done in the .NET application if you prefer
SELECT #rowcount = ##ROWCOUNT
IF(#rowcount = 0) -- No row updated => Concurrency Error
BEGIN
RAISERROR ('Concurrency error in procedure %s', 16, 1, 'Customer_Save')
RETURN
END

Categories

Resources