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.
Related
I have a GridView that pulls data using Entity Framework. The RowUpdating event is giving me a weird problem. Pulling desired data, Inserting, and Deleting works just fine, it's just Updating.
In the method I get the item given the datakey from the GridView:
int dataKey = Convert.ToInt32(gridView.DataKeys[e.RowIndex].Value);
MyEntity ent = context.MyEntities.First(x => x.Key == dataKey;
Then I update the necessary fields and save:
ent.Field1 = "some data";
ent.Field2 = "other data";
ent.ModifiedDate = DateTime.Now;
context.SaveChanges();
While debugging, after context.SaveChanges() I see Field1 and Field2 saved their new values whereas ModifiedDate changed back to what it had before.
The only thing I could think that would cause this is when I look at the table in the .edmx file the ModifiedDate field has "Computed" in StoreGeneratedPattern. However, this is supposed to be just computed initially. For example, a user inserts an item without a ModifiedDate and SQL Server puts getdate() in the field on creation.
USE [MyDB]
GO
ALTER TABLE [dbo].[MyEntities] ADD CONSTRAINT
DF_MyEntities_ModifiedDate] DEFAULT (getdate()) FOR [ModifiedDate]
GO
the problem was indeed in the StoreGeneratedPattern being computed. If it is set to Computed it overrides whatever the user sets the value as and tries to find the constraint in the DB that will give it it's computed value. The problem lies with a default constraint only fires if nothing is supplied, but the query EF generates for the UPDATE command sends something like ModifiedDate = NULL. So EF says i'll send ModifiedDate = NULL, but it will be overridden once the DB runs this update query. Then the DB says ok I will supply a calculated value for ModifiedDate if the user did not supply one. Oh wait, I see they supplied a ModifiedDate I don't need to run my constraint.
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 am trying to write some content to my database via LINQ. Currently, I'm getting the following error:
Cannot insert explicit value for identity column in table 'MyTable' when IDENTITY_INSERT is set to OFF.
I understand this is a primary key related issue. My table has the identity specification set to "Yes" with Identity Increment set to "1". But I have successfully updated other tables using code that looks like the following:
using (DataModelDataContext context = new DataModelDataContext())
{
List<MyTable> newRecords = new List<MyTable>();
MyTable record1 = new MyTable();
record1.CreatedBy = GetUserName();
record1.Information = GetInformation();
newRecords.Add(record1);
MyTable record2 = new MyTable();
record2.CreatedBy = GetUserName();
record2.Information = GetInformation();
newRecords.Add(record2);
context.MyTables.InsertAllOnSubmit(newRecords);
context.SubmitChanges();
}
What would cause this error? If I want to write new records, is there a way to set the primary key before it gets sent to the db? Something like "GetNextKey()" or something? Basically, I just need to insert these records. What am I doing wrong?
Thanks!
The code you have posted would work, assuming neither of those fields have the identity value. You basically need to assure you do not try to set the identity value before you add the item to the Table collection. (actually, you should never set the identity value from your code in most circumstances when it is to be generated in the DB).
So somewhere, the code that is having an error is trying to set the identity field's value.
If the ID is important (say you're importing data, and records you'll be inserting later will reference these records), then turn off the identity temporarily while you perform the insert. If having a particular ID on each record doesn't matter, and you can let the identity column pick one (which is 99.9 percent of the time), make sure you're not specifying a value for the ID before trying to save the record. Also make sure that your mapping between the object and the DB specifies that the ID is an identity, so EF knows not to try to insert the ID column.
Check to make sure your primary key/identity column has the following set within the DBML:
Auto Generated Value: True
Auto-Sync: OnInsert
This will make sure that LINQ-To-SQL will not try and insert the key value into that column and that it will update the value after an insert takes place. These should be on when you added the table to the DBML.
I have a Table in my DataBase (MS SQL 2008) with a default value for a column SYSUTCDATETIME ().
The Idea is to automatically add Date and Time as soon as a new record is Created.
I create my Conceptual Model using EF4, and I have created an ASP.PAGE with a DetailsView Control in INSERT MODE.
My problems:
When I create a new Record. EF is not able to insert the actual Date and Time value but it inserts instead this value 0001-01-01 00:00:00.00.
I suppose the EF is not able to use SYSUTCDATETIME () defined in my DataBase
Any idea how to solve it? Thanks
Here my SQL script
CREATE TABLE dbo.CmsAdvertisers
(
AdvertiserId int NOT NULL IDENTITY
CONSTRAINT PK_CmsAdvertisers_AdvertiserId PRIMARY KEY,
DateCreated dateTime2(2) NOT NULL
CONSTRAINT DF_CmsAdvertisers_DateCreated DEFAULT sysutcdatetime (),
ReferenceAdvertiser varchar(64) NOT NULL,
NoteInternal nvarchar(256) NOT NULL
CONSTRAINT DF_CmsAdvertisers_NoteInternal DEFAULT ''
);
My Temporary solution:
Please guys help me on this
e.Values["DateCreated"] = DateTime.UtcNow;
More info here:
http://msdn.microsoft.com/en-us/library/bb387157.aspx
How to use the default Entity Framework and default date values
http://msdn.microsoft.com/en-us/library/dd296755.aspx
The problem is that EF doesn't know about that default value so it sends .NET default value to database. It is possible to force EF to use default value from DB but you must manually modify EDMX file (XML not designer). You must find column definition in SSDL part of EDMX and add StoreGeneratedPattern="Computed" attribute. You also must the same in CSDL part (this can be done in designer).
This setting will have several problems. First of all each update from database will delete your changes in SSDL. Also each insert and update will trigger select which will requery value from this column (because EF thinks that it is computed every time).
In your ASP .NET page when you create the record are you actually adding a value for the DateCreated value. I see that you are doing that now, which is what you needed to be doing in the first place.
So I would guess what was happening earlier is that you were creating your new record but not setting a value for the DateCreated Field. But since a DateTime has a default value of 0001-01-01 00:00:00.00 is why you were getting that behavior.
The default value would only work for the case where you insert a row but provide no indication of what to do with the DateCreated field. But the way that EF works it is always going to provide a value for this (unless you write your own insert query to override the generated ones).
You will also be potentially changing the value any time that you update the record as EF won't know what to do with it.