I have a WPF DataGrid bound to some entities (Entity Framework 4+).
User then edits the DataGrid and presses SAVE. Data is then saved back to MS SQL Server 2008 using SaveChanges(). Well... now, I would like to have AUTOMATIC timestamp which stores the time of the last change and updates itself automatically.
Guys, is it possible? How?
Thank you, James
The easiest solution to achieve this is on the database level:
Create a new column in the table for storing your DateTime value.
Create database trigger to set the column for each insert or update to the table.
Map the new column as property in your entity
Set StoreGeneratedPattern for the new property to Computed so the value generated in the database is correctly updated to your attached entity after each insert or update.
If you don't like the trigger you must do it manually in overriden SaveChanges where you find all entities which will be inserted or updated and set the column:
public override int SaveChanges(SaveOptions options)
{
var entities = ObjectStateManger.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Select(e => e.Entity)
.OfType<YourEntityType>();
DateTime now = DateTime.Now;
foreach(var entity in entities)
{
entity.Updated = now;
}
return base.SaveChanges(options);
}
Related
I need some more understanding of how Entity Framework works. I have implemented code which is running ok and doing the job.
But I need to know that it is the good way or not.
I have a table with 8 columns, say
Table1
Column1 (pk)
Column2, Column3, Column4, Column5, Column6, Column7, Column8
Now when I click a button, I need to insert (if new) or update (for existing record) first 6 columns.
And on the same button click event, as part of the same process, I will call a stored procedure (with a parameter of primary key id, Column1) that will fetch these 6 columns values in the stored procedure itself and will do some calculations based on six column values and return two new values which I need to update in Column7, Column8.
So, process will be:
New record: insert (six columns data), calculations (call stored procedure), update (last 2 column)
Existing record: update (six columns data), calculations (call stored procedure), update(last 2 column)
Now, for insert, I use
_dbContext.Table1.Add(entity);
_dbContext.SaveChanges();
For existing record update (of first 6 columns), I use
//code - Entity property values are updated with new ones
_dbContext.Table1.Attach(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
For last update, Column7, Column8, I use
var entity = GetById(Id);
if (entity != null)
{
entity.Column7 = value1;
_dbContext.Entry(entity).Property(t => t.Column7).IsModified = true;
entity.Column8 = value2;
_dbContext.Entry(entity).Property(t => t.Column8).IsModified = true;
_dbContext.SaveChanges();
}
I am not sure why I need not to attach entity for last update. Is it due to I have called GetById method for same table just above that?
Without attaching entity how it updates columns? (if I attach entity it gives error saying already being tracked)
Also, I have to call GetById multiple times to get record for both updates (in existing record scenario). Any other solution for that?
for that you can use MapToModel method
here is example
//code - Entity property values are updated with new ones
var newEntity= new Entity
{
Id = p.Id, // the Id you want to update
Column1= "" // put value for column/s that you need to update
};
newEntity.MapToModel(oldEntity);
_dbContext.SaveChanges();
IMHO I don't think you need to specify IsModified for each property if you fetch through the Context.
EF Context will automatically track Entities unless you change the default behavior
EF knows which entities are changed after loading from the database,hence it will be only updated.
Assuming GetById is fetching the data from data-store.
var entity = GetById(Id);
if (entity != null)
{
entity.Column7 = value1;
entity.Column8 = value2;
_dbContext.SaveChanges();
}
Detect Changes
Similar Question
Forgive me for not knowing how to technically express the situation...
Main table, drop down table. Saving an Updated entity for main table (which holds FK for drop down table, deletes value for that record in the drop down table.
Why would changing a value in the main table, delete the value for the corresponding record in the drop down table?
save function
public void UpdateApplicationDetails(Application UpdatedApp)
{
AtlasContext updatecontext = new AtlasContext();
Application currAppRecord = _context.Apps.Where(a => a.Id == UpdatedApp.Id).Single();
currAppRecord = UpdatedApp;
updatecontext.Apps.Update(UpdatedApp);
updatecontext.SaveChanges();
}
Will update with necessary info if requested. Thanks for taking the time
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.
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.
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.