Change Tracking Entity framework - c#

i make table for property name that changed and value before and value after
How i can use Change Tracking to store changed in this table?

You can track the operation, the changed columns and the new values by using Change Tracking. However getting the old Value out of Change Tracking is not possible. SQL Server 2016 offers the new feature "Change data capture", which gives you the needed Information about the old value before the update/delete happened ( see https://msdn.microsoft.com/en-us/library/bb933994.aspx ).
If you don't have access to a SQL Server 2016, here is how you can configure Change Tracking:
Activate at Database
ALTER DATABASE <YourDatabase> f.e. DeviceDatabase
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)
Activate Change Tracking for your needed tables
ALTER TABLE <YourTable> f.e. Devices
ENABLE CHANGE_TRACKING
WITH (TRACK_COLUMNS_UPDATED = ON)
Setup a DB Job, which will copy change-information into your custom table every minute,hour,day (what you need)
DECLARE #minversion bigint;
SET #minversion = (SELECT MinVersion = CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID('Devices')) )
SELECT SYS_CHANGE_COLUMNS, e.Id FROM
CHANGETABLE(CHANGES Devices, #minversion) AS C
LEFT OUTER JOIN Devices AS e
ON e.Id = c.Id;
To Get the latest Value of the Changed Column you can try this (but beware of multiple updates of the same row. you only get the latest value).
CHANGE_TRACKING_IS_COLUMN_IN_MASK
(COLUMNPROPERTY(OBJECT_ID('Devices'), 'Id', 'ColumnId')
,c.sys_change_columns)
This will return 1 if Column changed, 0 if not. You can add this for every column of your table and join on value = 1 and then add the value to your query.
Finally, I would just recommend to use Stored Procedures to Update/Insert/Delete on your Tables. In those you can easily insert all information you want to store about the change in your custom table.
If you have SQL Server 2016 tho, try what I mentioned above, eventually.

Actually if you override the SaveChanges() method in your data context class you can access ChangeTracker. This gives you all the entities currently tracked by the context and their EntityState (if they are added/modified/deleted/unchanged etc).
Here you can get the DbEntityEntry class and from that get the entitys current values and/or its previous values if the entity is in the modified state.
public override int SaveChanges()
{
var allTrackedEntities = this.ChangeTracker.Entries().ToList();
return base.SaveChanges();
}
I currently use this method to do some basic auditing of who is doing that to what entity.

Related

Primary Key value in datagrid with ObservableCollection (Entity Framework)

Don't throw stones at me - I am quite new in programming, especially in Entity Framework.
I created a small program, that records some variables to SQL table. For representation of table with record variables I use datagrid, that is bound with ObservableCollection. The Datagrid looks like this:
recorded variable
Id is the primary key with seed 1. The problem is how to show actual Id number without querying a SQL table?
I tried this:
VariableRecordOCtoShow.Add(new VariableRecord() //VariableRecord is class from entity model
{
Value = newVariableRecord.Value,
Time = newVariableRecord.Time,
IdVariableAssignment = newVariableRecord.IdVariableAssignment,
IdUser = newVariableRecord.IdUser
});
But in Id column in datagrid were only zeros.
I tried this: firstly create a database, what is las Id number
public int getIdValue()
{
var query = (from p in context.VariableRecords
orderby p.Id descending
select p).Take(1).Single();
return query.Id;
}
Secondly write it do variable:
idVariable = getIdValue();
Then write it to Id of ObservableCollection in (not class of entity model) and increase by one this variable after each record (below is part of writing method):
VariableRecordOCtoShow.Add(new VariableRecord() // v tomto velkem radku jenom zapisuju do Observable Collection hodnoty ze vstupu + Id
{
Id = idVariable, //fake Id (calculated from query)
Value = newVariableRecord.Value,
Time = newVariableRecord.Time,
IdVariableAssignment = newVariableRecord.IdVariableAssignment,
IdUser = newVariableRecord.IdUser
});
idVariable++;
It works, but only in the beginning - when I tested my program overnight, there was some error with performance (some variables didn't write in time) and at final Id from OC and Id from SQL doesn't match.
Can you help me? How can I display real primary key value in datagrid without querying while record is running?
Thank you in advance.
The problem is how to show actual Id number without querying a SQL table?
I am not sure about your question
Firstly, why don't you want to query the SQL table?
Secondly, we generally don't show ID field (Identity field) in the view. It's not a good practice.
And still you want to show then you can always query in EF :
ObservableCollection<VariableRecords> v =
new ObservableCollection<VariableRecords>(from p in context.VariableRecords
select c)
Also your second approach might create problems in future as identity field ID is not always Max(ID) +1. See this
Reuse of values – For a given identity property with specific seed/increment, the identity values are not reused by the engine. If a particular insert statement fails or if the insert statement is rolled back then the consumed identity values are lost and will not be generated again. This can result in gaps when the subsequent identity values are generated.

EF5 always sets id to 0

I'm adding objects to a database where the id isn't auto-autogenerated in the database due to me wanting to specify the id myself (It's stupid I know, just play along ^^)
So I'm using Entity Framework 5 to insert the data into the database, however, eventhou I set the id before saving it, when I look in the database it's always zero. Why is this and how do I fix it?
The code is like this:
public Profile Add()
{
Profile item = new Profile()
{
id = 1,
name = "Bob"
};
db.Entry(item).State = EntityState.Added;
db.SaveChanges();
return item;
}
EDIT
I tried with db.Profiles.Add(item). Same problem
1.- In your edmx designer, right click id column, select properties
2.- In StoreGeneratedPattern select None
As PhilipStuyck said, your model was out of sync with your database, whenever you change the database you must update the model (Right click empty space in edmx designer select Update model from database).
Check that your database and your model are actually the same.
If you created your model from an existing database and then changed the database then your model is out of sync. This can also happen with code first of course.
Bottom line is that your sql will do an insert without a value for id, because EF thinks your id is going to come from the database. If you inspect the sql you will see an insert without id being provided followed with a select to get the value for id.
Conclusion is that your model and db are not the same.
Right click the designer and do update model from database.
You might have to remove your id column, or the table to begin with, EF will correct everything
You need to specify the table that you are adding it to.
so for you that would be db.NAMEOFTABLE.add(item)
normally you don't have to change the entity state to added.
Atleast I didn't have to do that in my solution.
You need to add the entity to the DbSet that represents your profiles table... something like this...
public Profile Add()
{
Profile item = db.Profiles.Create();
item.Name = "Bob";
db.Profiles.Add(item);
db.SaveChanges();
return item;
}
I prefer using the Create method however it makes no difference.
By default Id property is treated as Primary Key and as Identity by Entity Framework. So it just ignores property value, while generating sql for insert command. You should specify metadata explicitly: add [DatabaseGenerated(DatabaseGeneratedOption.None)] attribute or use method HasDatabaseGeneratedOption from FluentApi.

Why is NPoco ignoring column and property names and mapping almost randomly?

I'm using the NPoco ORM (a branch of PetaPoco) but I've just noticed it's mapping the columns incorrectly in some cases.
I'm using a stored procedure and my POCO property names are identical to the column names produced by the stored procedure:
string sql = "EXEC API_GetVenueSummaryByID ##venueID = #venueID";
var venue = db.FirstOrDefault<VenueSummary>(sql, new { venueID = venueID });
The stored procedure is a simple SELECT statement with a couple of variables included (removing them doesn't help):
DECLARE #hasOffers bit
IF EXISTS(SELECT * FROM Offers WHERE dbo.Offers.EntryType='V' AND Offers.EntryID = #VenueID AND GETDATE() <= validToDate) SET #hasOffers = 1
SELECT
Venue.VenueID, VenueName, Town, County, Country, PackageCode,
MeetingRoomsNo, MaxMeetingCapacity, BedroomsNo,
MetaDescription AS ShortDescription,
'dummyresult.jpg' AS PrimaryImageFilename,
#hasOffers AS HasSpecialOffers,
CAST(TimeStamp AS BIGINT) AS RecordVersion
FROM dbo.Venue
WHERE Venue.VenueID = #VenueID
Is there a function in NPoco which causes it to guess the mappings (ignoring their names)? If so, how I can I disable this and force it to only match based on the column and property names?
Currently the only work around seems to be to use the column attribute <-- doesn't work either
At the moment, even someone auto-formatting stored procedure (or any change which results in a change of column order) is breaking the application.
Edit 2
I've noticed that if I restart the website application (eg by editing web.config or updating application code) then the column order fixes itself. So I can only assume the problem is related to NPoco internally caching the column indexes - and if the indexes change, the mappings will then be incorrect. I'm not sure if there's a mechanism to clear the cache that's perhaps not working?
This is a problem with how NPoco (and PetaPoco) caches the codegen that is used to map from a SQL statement to a POCO.
Usually this isn't a problem if you are changing the code when you are changing the SQL as the cache will be rebuild, however if you create your POCO first then start to change the SP after the first initial run the mappings will be incorrect.
This issues has now been fixed in 2.5.83-beta, and it will now look at the column names and their positions to determine the cache key.
Thanks for the help #NickG

Updating SQL bit column from Entity Framework

I'm having an issue updating a 'bit' column in a SQL database, using C# and Entity Framework.
I have a 'Settings' table with a NOT NULL bit column named 'Active'. When I create a record in the table and specify 'Active' in code as 'true' or 'false', the record in the database that's created is accurate and the 'Active' column contains the correct value that was specified in code. When I update the record and change 'Active' from 'false' to 'true' in code, that works as well. However, when I update the record going from 'true' to 'false', the 'Active' column in the database still contains '1' (true).
Is this a known issue? If so, is there a workaround? I've done a fair amount of research and was unable to find anything.
Here's my update code:
public int UpdateSetting(SettingModel settingModel)
{
using (var db = new EfMyDB())
{
// Create a new setting record with the ID of the record to update.
Setting updatedSetting = new Setting { Id = settingModel.Id };
// Attach the record.
db.Settings.Attach(updatedSetting);
// Update the attached record.
updatedSetting.Name = settingModel.Name;
updatedSetting.Value = settingModel.Value;
updatedSetting.Active= settingModel.Active;
// Save the database changes.
return db.SaveChanges();
}
}
EF 'Active' column properties:
Versions: .NET 4.0, SQL Server 2008, Entity Framework 4.1
I think this is because when you create a Setting object .Active field takes is default value: that is false. So when you set Active to false there is no change. You have to load the entity from the Db before the update.

TSQL: UPDATE with INSERT INTO SELECT FROM

so I have an old database that I'm migrating to a new one. The new one has a slightly different but mostly-compatible schema. Additionally, I want to renumber all tables from zero.
Currently I have been using a tool I wrote that manually retrieves the old record, inserts it into the new database, and updates a v2 ID field in the old database to show its corresponding ID location in the new database.
for example, I'm selecting from MV5.Posts and inserting into MV6.Posts. Upon the insert, I retrieve the ID of the new row in MV6.Posts and update it in the old MV5.Posts.MV6ID field.
Is there a way to do this UPDATE via INSERT INTO SELECT FROM so I don't have to process every record manually? I'm using SQL Server 2005, dev edition.
The key with migration is to do several things:
First, do not do anything without a current backup.
Second, if the keys will be changing, you need to store both the old and new in the new structure at least temporarily (Permanently if the key field is exposed to the users because they may be searching by it to get old records).
Next you need to have a thorough understanding of the relationships to child tables. If you change the key field all related tables must change as well. This is where having both old and new key stored comes in handy. If you forget to change any of them, the data will no longer be correct and will be useless. So this is a critical step.
Pick out some test cases of particularly complex data making sure to include one or more test cases for each related table. Store the existing values in work tables.
To start the migration you insert into the new table using a select from the old table. Depending on the amount of records, you may want to loop through batches (not one record at a time) to improve performance. If the new key is an identity, you simply put the value of the old key in its field and let the database create the new keys.
Then do the same with the related tables. Then use the old key value in the table to update the foreign key fields with something like:
Update t2
set fkfield = newkey
from table2 t2
join table1 t1 on t1.oldkey = t2.fkfield
Test your migration by running the test cases and comparing the data with what you stored from before the migration. It is utterly critical to thoroughly test migration data or you can't be sure the data is consistent with the old structure. Migration is a very complex action; it pays to take your time and do it very methodically and thoroughly.
Probably the simplest way would be to add a column on MV6.Posts for oldId, then insert all the records from the old table into the new table. Last, update the old table matching on oldId in the new table with something like:
UPDATE mv5.posts
SET newid = n.id
FROM mv5.posts o, mv6.posts n
WHERE o.id = n.oldid
You could clean up and drop the oldId column afterwards if you wanted to.
The best you can do that I know is with the output clause. Assuming you have SQL 2005 or 2008.
USE AdventureWorks;
GO
DECLARE #MyTableVar table( ScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO #MyTableVar
VALUES (N'Operator error', GETDATE());
It still would require a second pass to update the original table; however, it might help make your logic simpler. Do you need to update the source table? You could just store the new id's in a third cross reference table.
Heh. I remember doing this in a migration.
Putting the old_id in the new table makes both the update easier -- you can just do an insert into newtable select ... from oldtable, -- and the subsequent "stitching" of records easier. In the "stitch" you'll either update child tables' foreign keys in the insert, by doing a subselect on the new parent (insert into newchild select ... (select id from new_parent where old_id = oldchild.fk) as fk, ... from oldchild) or you'll insert children and do a separate update to fix the foreign keys.
Doing it in one insert is faster; doing it in a separate step meas that your inserts aren't order dependent, and can be re-done if necessary.
After the migration, you can either drop the old_id columns, or, if you have a case where the legacy system exposed the ids and so users used the keys as data, you can keep them to allow use lookup based on the old_id.
Indeed, if you have the foreign keys correctly defined, you can use systables/information-schema to generate your insert statements.
Is there a way to do this UPDATE via INSERT INTO SELECT FROM so I don't have to process every record manually?
Since you wouldn't want to do it manually, but automatically, create a trigger on MV6.Posts so that UPDATE occurs on MV5.Posts automatically when you insert into MV6.Posts.
And your trigger might look something like,
create trigger trg_MV6Posts
on MV6.Posts
after insert
as
begin
set identity_insert MV5.Posts on
update MV5.Posts
set ID = I.ID
from inserted I
set identity_insert MV5.Posts off
end
AFAIK, you cannot update two different tables with a single sql statement
You can however use triggers to achieve what you want to do.
Make a column in MV6.Post.OldMV5Id
make a
insert into MV6.Post
select .. from MV5.Post
then make an update of MV5.Post.MV6ID

Categories