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.
Related
I have a model that has some columns defined with default values like
table.Column<bool>(nullable: false, defaultValueSql: "1")
When I save a new entity in the database using context.SaveChanges(), I noticed that the columns with default values are not included in the insert into query that Entity Framework generates, so the values generated in the database are the default ones instead of the ones I'm passing in the model.
Do I have to set up some property in the context to be able to set these properties through code? I'm using EF Core, but I don't know if this is a general behavior of all EF versions.
UPDATE: the code is pretty simple. This is pseudo code of what I have.
The Model has some properties defined with constraints such as the one I describe above
table.Column<bool>(nullable: false, defaultValueSql: "1")
I'll use column MyBooleanProperty as an example. I have in a service:
var newModel = new GEAddress();
newModel = someEntity.MyBooleanProperty; //it is false,but ends up as 1 in the database
I'm using Unit Of Work and Repositories so I have
_unitOfWork.MyModelRepository.Add(newModel);
_unitOfWork.SaveChanges();
In the output window of VS, I see how it send all properties in an INSERT INTO query and then it does a SELECT on the properties with default values. The result is the newModel in the database with all the values I sent, except the columns with default values.
I cannot change the configuration for those tables since it's being used by another system that needs those rules.
I would like to know an explanation on why this is happening more than a solution. I can work around this, but I'd like to know why this behavior is happening
I would call this a bug.
I snapped together a simple test, just a class with some defaults:
public class Test
{
public int ID { get; set; }
public int IntDef { get; set; }
public bool BoolDef { get; set; }
public DateTime? DateDef { get; set; }
}
(Note that the DateTime is nullable)
The mapping:
modelBuilder.Entity<Test>().HasKey(a => a.ID);
modelBuilder.Entity<Test>().Property(s => s.DateDef).HasDefaultValueSql("GETDATE()");
modelBuilder.Entity<Test>().Property(s => s.IntDef).HasDefaultValueSql("1");
modelBuilder.Entity<Test>().Property(s => s.BoolDef).HasDefaultValue(true);
// Equivalent:
// modelBuilder.Entity<Test>().Property(s => s.BoolDef).HasDefaultValueSql("1");
SQL statement that creates the table:
CREATE TABLE [Tests] (
[ID] int NOT NULL IDENTITY,
[BoolDef] bit NOT NULL DEFAULT 1,
[DateDef] datetime2 DEFAULT (GETDATE()),
[IntDef] int NOT NULL DEFAULT (1),
CONSTRAINT [PK_Tests] PRIMARY KEY ([ID])
);
When I insert a new Test without setting any value, the insert statement is:
INSERT INTO [Tests]
DEFAULT VALUES;
SELECT [ID], [BoolDef], [DateDef], [IntDef]
FROM [Tests]
WHERE ##ROWCOUNT = 1 AND [ID] = scope_identity();
You see that the three default values (and the generated identity value) are read from the database after the insert. [By the way, this is new in EF-Core. In EF6, only identity values and column values that were marked as DatabaseGeneratedOption.Computed were read from the database after insert (and update)].
This is the created Test object:
ID IntDef BoolDef DateDef
1 1 True 21-11-16 19:52:56
Now I insert a new Test and assign all values, but, just for fun, I use the default values for the non-nullable types:
var item = new Test
{
IntDef = default(int), // 0
BoolDef = default(bool), // false
DateDef = default(DateTime), // 01-01-01 0:00:00
};
Here's the SQL statement:
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Tests] ([DateDef])
VALUES (#p0);
SELECT [ID], [BoolDef], [IntDef]
FROM [Tests]
WHERE ##ROWCOUNT = 1 AND [ID] = scope_identity();
',N'#p0 datetime2(7)',#p0='0001-01-01 00:00:00'
Of course, EF has no way to infer that the default values were assigned deliberately. So as you see, only for the nullable DateTime column the assigned value is inserted, not for the non-nullable columns. Now the value for DateDef isn't read from the database after the insert.
The entity values are:
ID IntDef BoolDef DateDef
1 1 True 01-01-01 0:00:00
Not what one would expect after saving the entity --not at all!
Which means:
When you configure a property with a default value in EF-Core, and this default is different than the .Net default value, you can't insert an entity with default values for the .Net type (like false for a boolean).
I think this is a serious bug, maybe it even disqualifies the new EF-Core behaviour concerning defaults.
Addition
As said in Ivan's comment, you can stop EF from setting default values for you by adding ValueGeneratedNever(), for example:
modelBuilder.Entity<Test>().Property(s => s.IntDef)
.HasDefaultValueSql("1").ValueGeneratedNever();
Now the value will be saved as it is and EF won't read it back after inserts and updates. All in all, I think defining defaults for non-nullable properties isn't useful.
Tested with EF Core 1.1.0 preview.
If you don't mark the property as computed with the proper attribute
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
ef will generate the insert statement with the property value.
During update EF generates the UPDATE statement SET clause inserting only changed values.
You have already a workaround anyway, if the property is only generated by the DBMS you can use the attribute above otherwise you have to insert the default value in the constructor of the class that rapresent the entity.
ValueGeneratedNever didn't do the job for me (Entity Framework Core 3.1.21). I simply deleted .HasDefaultValueSql from DBContext and leaved default value in database schema. Works fine, maybe this note can be useful for somebody
Here is my code:
UVCUpdate update = new UVCUpdate();
update.CurrentDate = DateTime.Now;
_context.UVCUpdates.Add(update);
_context.SaveChanges();
Now I am getting an inner exception though saying this:
Cannot insert the value NULL into column 'CurrentDate', table 'bLinked.dbo.BlackbookUpdateUVC'; column does not allow nulls. INSERT fails.
If I output the DateTime.Now just before this code it outputs:
9/15/2016 7:26:35 PM
My data type for CurrentDate in the db is set to datetime and in the class it is set to DateTime. Neither allow for nulls, but DateTime.Now should not be null right?
It almost always happens when there is mismatch between so called "store generated pattern" between EF model and database. If model column has store generated pattern of Identity or Computed - that means EF will be sure those values will be automatically provided by database on insert or update, and there is no need to include them in INSERT or UPDATE statements. Missing values will have default NULL value, and if this column is non-nullable in database at the same time, and is not really computed or identity - you have the error in question.
I am sorry guys, I feel like an idiot. Thank you to Leopard.
I went to get the UVCUpdate class to show him and realized that when I copied that class from another class I accidentally left [DatabaseGenerated(DatabaseGeneratedOption.Identity)] in the class for the CurrentDate. So it was likely attempting to send a null value to the SQL so the SQL would create an ID, but the field was set to not allow nulls. CurrentDate was not supposed to be an ID so I removed that and now it works...
I am working on code which I cannot run until it's finished, as it is a long and tedious process to go through. I have been tasked to add a new table to the existing database, update the .edmx of the model and write a method to add new rows to the table through c# backend code.
In my situation, I have 2 default constraints on my table
SomeTable
------------
ID INT IDENTITY (1,1) PRIMARY KEY CLUSTERED,
SomeDate DATETIME2 NOT NULL DEFAULT GETDATE(),
SomeOtherField VARCHAR(1024) NOT NULL DEFAULT ''
Using the .edmx model of this table, I set SomeDate's StoreGeneratedPattern to Computed. I also manually double-checked it in the SSDL to ensure the computed StoreGeneratedPattern attribute was on the SomeDate Field in the SomeTable entity.
As far as I know, and are unsure of, when I have a the following code
public void AddSomeRow(...)
{
SomeDbContext context = new SomeDbContext;
var table = new SomeTable { SomeOtherField = "Value" };
context.SomeTables.Add(table);
context.SaveChanges();
}
I believe the SomeDate Field will be set to it's default constraint (because it hasn't been populated in the entity). Is this true?
Doing the same steps listed above for SomeOtherField, can I still manually give it a value (the value appearing into that field instead of the Default Constraint) or omit it (The default constraint will be set into that field)?
This question was hard to explain, I apologize if it doesn't make sense
The StoreGeneratedPattern implies that the value is always generated by the DB, so that you're not allowed to modify it. I.e it only makes sense for DB computed columns.
At least until EF 6.1 there is no direct way to support DB defaults. The only thing that you can do is move the default values out of the DB and generate them in the model (or code first) side. You'll find some work-aounds but they're not safe for some cases (specially for N-tier apps).
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.