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).
Related
I am trying to understand the (functional) difference between the two DatabaseGeneratedOption's in Entity Framework (code first):
DatabaseGeneratedOption.Identity
DatebaseGeneratedOption.Computed
I have read the documentation, but I do not understand the functional difference between the two.
The option Identity is described as The database generates a value when a row is inserted. But I cannot update the value later if I try I get an exception saying that I cannot modify a column with Identity pattern.
The option Computed is described as The database generates a value when a row is inserted or updated. However, this is just what you tell Entity Framework, so far I have not been able to achieve this, without SQL triggers. If I try to update the value, nothing happens (Entity Framework refuses to overwrite the existing value).
So what I am left with, is two functionally identical options. I can have a default value in my SQL table, which will be applied on the insert. And I cannot update this value afterward (using Entity Framework). So where in lies the difference in how they should be used?
A computed column contains a value that is computed (hence the name) when the record is requested.
A computed column can be composed of other column values, constants and function return values.
You could for example create a computed column for an invoice expiry date:
CREATE TABLE Invoice
(
InvoiceDate DATETIME NOT NULL,
ExpiryDate AS DATEADD(DAY, 30, InvoiceDate)
)
Now if you mark this column as computed in Entity Framework, it will refuse any updates to that column, because it'll know the database won't support that.
So the documentation for that attribute is incorrect or incomplete.
A column marked as Identity will use the database-specific syntax to generate a primary key for the given table when inserting a record, being IDENTITY() in MSSQL and AUTO_INCREMENT for MySQL. A computed column will simply be marked as read-only, and you'll have to provide your own implementation (either in the database or in your migration file) to specify the computation (see How to add computed column using migrations in code first?).
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
I have this simple code : (update value)
I'm trying to update column "c"
using (MaxEntities ctx = new MaxEntities())
{
aa orders = (from order in ctx.aa
select order).First();
orders.c = 22;
ctx.SaveChanges();
}
this is the table :
CREATE TABLE [dbo].[aa](
[a] [int] NULL,
[b] [int] NOT NULL,
[c] [int] NOT NULL
) ON [PRIMARY]
and values inside :
but i get an exception :
The property 'c' is part of the object's key information and cannot be modified.
I'm new to EF.
any help will be much appreciated.
The property 'c' is part of the object's key information and cannot be modified.
That's why you can't edit it. Maybe you need to add id column as a key with identity specified
As explained in another answer EF must uniquely identify every entity. If you don't have PK in the database, EF will infer some key. Key is considered as fixed so if EF inferred c as part of the key (and it did it because it uses all non-nullable non-binary columns) you cannot change its value. Moreover EF takes all tables without primary key as readonly so even if you remove c from the key in the designer and modify c value you will get another exception when you execute SaveChanges.
The reason for the second exception is in the way how EF describes model and the database. When EF inferred key, it did it only for description of your entities and for context's internal needs but not for description of the database. When EF tries to save changes it builds UPDATE statement from database description and without information about real database PK columns it will not be able to identify correct record for update (every update in EF can affect only single record - EF checks ROWCOUNT). This can be solved by cheating EF and updating its database description = by describing some column in the table description as primary key. This leads to multiple problems:
You must have some unique column in the database otherwise this method will not work.
You must edit EDMX manually (as XML) to add this change
You must not use default MS EDMX designer for updating your model from database because it will delete your change
Simple advice: Either use database tables with primary keys or don't use Entity framework.
Primary key missing here. Add primary key in table and it work.
I believe if there's no PK at all, EF uses all of the fields/columns as part of the key info.Here's a nice explanation: by #SteveWilkes of why. But what do your entities look like? The other possibility is that it doesn't have a property because the association is inside a different entity, if this is a foreign key.
EDIT
This got me thinking. There are just going to be situations where you have to work with legacy tables having no PK, even if you would never create such a thing. What about views? EF is a mapper - it has to uniquely identify that record so it infers and defines this key. Yes, you could use stored procedures, but could you also hack the XML and remove the keys from the table definition?
AND EDIT AGAIN
After posting this, I see #Ladislav Mrnka already said a similar idea (cheating EF and updating its database description), so it has been done (WARNING: Consume at your own risk - never tried). Quick google got me this blog with clear instructions:
Close the model designer in Visual Studio if it is still open and re-open the .edmx file in an XML editor
Find the edmx:StorageModels -> Schema -> Entity Container -> EntitySet element that refers to the table in question
On the EntitySet element, rename the store:Schema attribute to Schema
Remove the store:Name attribute altogether
Remove the opening and closing DefiningQuery tags and everything in between them
Save and close the .edmx file
But really, who doesn't like a PK? Can you not add an id?
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.
Rightly or wrongly, I am using unique identifier as a Primary Key for tables in my sqlserver database. I have generated a model using linq to sql (c#), however where in the case of an identity column linq to sql generates a unique key on inserting a new record for guid /uniqueidentifier the default value of 00000000-0000-0000-0000-000000000000.
I know that I can set the guid in my code: in the linq to sql model or elsewhere, or there is the default value in creating the sql server table (though this is overridden by the value generated in the code). But where is best to put generate this key, noting that my tables are always going to change as my solution develops and therefore I shall regenerate my Linq to Sql model when it does.
Does the same solution apply for a column to hold current datetime (of the insert), which would be updated with each update?
As you noted in you own post you can use the extensibility methods. Adding to your post you can look at the partial methods created in the datacontext for inserting and updating of each table. Example with a table called "test" and a "changeDate"-column:
partial void InsertTest(Test instance)
{
instance.idCol = System.Guid.NewGuid();
this.ExecuteDynamicInsert(instance);
}
partial void UpdateTest(Test instance)
{
instance.changeDate = DateTime.Now;
this.ExecuteDynamicUpdate(instance);
}
Thanks, I've tried this out and it seems to work OK.
I have another approach, which I think I shall use for guids: sqlserver default value to newid(), then in linqtosql set auto generated value property to true. This has to be done on each generation of the model, but this is fairly simple.
There's two things you can do:
either just generate the GUID in your C# client side code and use that value
create a DEFAULT constraint on the GUID column in SQL Server that defaults to newid() for the column - the SQL Server will make SURE to always add a default - unless you specify a value yourself
As for the self-updating date/time columns - here you probably have to use either client-side logic to do that, or if you want to do it on SQL Server, you'll have to write a trigger. That's really the only way to update a specific column everytime the row gets updated - there's no "autoupdate" constraint or anything like this (and the default constraint only work on INSERTs, not updates).
Something like this might work:
CREATE TRIGGER TRG_UpdateDateTimestamp
ON (your table name)
AFTER UPDATE
AS
IF NOT UPDATE(DateTimeStamp)
BEGIN
UPDATE (yourtablename)
SET DateTimeStamp = GETDATE()
WHERE EXISTS (SELECT * FROM inserted AS i
WHERE i.OID = (yourtable).OID)
END
Marc