11

SQL server 2005 database table has a column 'createdon' for which default value set to getdate(). I am trying to add a record using entity framework. 'createdon' column is not getting updated.

Did I miss any property in Entity framework, please suggest.

Chris_web
  • 743
  • 3
  • 10
  • 19
  • **There are two solutions at the answer : https://stackoverflow.com/a/57577707/7731479** – Leo Aug 20 '19 at 16:24

4 Answers4

15

This is one of the few issues that are problematic with Entity Framework. Say you have a class that looks like this:

public class MyEntity
{
    // Id is a PK on the table with Auto-Increment
    public int Id { get; set; }

    // CreatedOn is a datetime, with a default value
    public DateTime CreatedOn { get; set; }
}

Now, you want to insert a new element:

using(var context = new YourContext()) 
{
    context.MyEntities.Add(new MyEntity())
}

Entity Framework knows how to handle an auto-increment primary key because of the definition in the EDMX. It will not try to insert a value for the Id property. However, as far as Entity Framework is concerned, CreatedOn has a value: the default DateTime. Because Entity Framework cannot say "well, it has a value but I should ignore it", it will actively insert the record with the CreatedOn property value, bypassing the default value on your column definition on your table.

There is no easy way to do this. You can either actively set the CreatedOn property to DateTime.Now when you insert that item. Or you can create an interface and an extension method pair:

public interface ICreatedOn
{
    public DateTime CreatedOn { get; set; }
}

public partial class MyEntity : ICreatedOn
{

}

public static TEntity AsNew<TEntity>(this TEntity entity) where TEntity : ICreatedOn
{
    if(entity != null)
        entity.CreatedOn = DateTime.Now;

    return entity;
}

using(var context = new YourContext()) 
{
    context.MyEntities.Add(new MyEntity().AsNew())
}   

Edit: To expand on this point, the reason why this is an unresolvable issue is because of the meaning behind an autoincrement field and a field with a default value constraint. An auto-increment field should, by definition, always be handle by the server, using a seed and all that jazz. You cannot specify a value for an auto-increment field on an insert unless you have used SET IDENTITY INSERT ON. A default value, however, is just a hint that say "if I don't specify any value, use this". Because value types in .NET cannot be null, there will always be a value and Entity Framework cannot infer that the default value for that field, at that time, means that you want it to be defaulted on the SQL server.

Simon Belanger
  • 14,752
  • 3
  • 41
  • 35
  • Thanks Simon, so only option is to set 'CreatedOn' explicitly in C# code and then add entity? – Chris_web Jul 15 '13 at 11:12
  • 1
    @Chris_web As far as I know, yes. This is also an issue with `Guid` primary keys in Entity Framework. Or bypass Entity Framework completely and insert the value with your own SQL. See my edit for more information as to why this is happening. – Simon Belanger Jul 15 '13 at 11:13
  • @Chris_web The "problem" is not EF, but .Net. A struct like Datetime, int, Guid etc. always has a default value (in case of int, it is 0, even when you do not assign it, and in case of Datetime it is default(DateTime) which happens to be DateTime.Min). You could try to add some logic to the getter of the Property, and check if it is the default value of Datetime and return another one, but it might not work if it does not trigger EF Changetracking (since EF only updates values that changed since loading). – Bernhard Kircher Jul 19 '13 at 09:21
  • @BernhardKircher: [Relevant suggestion](https://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/2929682-support-database-default-values-in-code-first), FWIW. – tne Nov 05 '13 at 17:36
  • @SimonBelanger Just to add to your answer, MS SQL only employs the default value for `not null` table fields, i.e., the MS SQL field must be not null and accept a null value to use the default at all. To get MS SQL to be the source for the default value, EF would need to send a null value. For C# to use any MS SQL generated default value, C# could not generate this value itself because it would not be a SQL generated default value. This is important for example if you have a `modified` datetime field generated by the database used to validate update operations across different time zones. – Zachary Scott Apr 27 '20 at 01:12
15

Next to using the designer and some more nifty stuff shown already, you can also mark the columns as being calculated by simply setting the DatabaseGenerated attribute on the field:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedOn { get; set; }
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • 2
    This is the best answer as you can easily put this in a partial class. It will keep working even if you regenerate your model. – Florian Lavorel Aug 29 '18 at 07:32
11

You can set StoreGeneratedPattern to Computed (as Malcolm suggested) in the GUI of the entity data model as well.

  1. Open your .edmx file in Visual Studio
  2. Open the properties of the field (click on the field -> hit F4 or right click->properties)
  3. Set StoreGeneratedPattern to Computed in the properties window as shown below:

enter image description here

Tony L.
  • 17,638
  • 8
  • 69
  • 66
7

I've got around this issue by telling EF that the column is 'computed', and should therefore be left alone for inserts.

If you look in the configuration for the generated entity

       namespace Data.Context
        {
            // Table
            internal partial class MyTableConfiguration : EntityTypeConfiguration<MyTable>
            {
                public MyTableConfiguration(string schema = "dbo")
                {    
                    ToTable(schema + ".MyTable");
                    HasKey(x => x.Id);

                    Property(x => x.ColumnName).HasColumnName("ColumnName").IsOptional().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
                    ....
Malcolm Swaine
  • 1,929
  • 24
  • 14
  • The problem with a pseudo or real computed column specification on a `modified` datetime field is that EF could not send a null value to get a new default datetime. In other words, you could not set the `modified` field to now. The problem with a real MS SQL computed column is that non-deterministic functions like getdate() (returns a different value each time) cannot be persisted. – Zachary Scott Apr 27 '20 at 01:23