3

I have the following classes in an EF6 application.

public class Extension
{
    [Key]
    [Column(Order = 1)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    [ForeignKey("Record")]
    public string RecordId { get; set; }
    public Record Record { get; set; }

    [Key]
    [Column(Order = 2)]
    [DataType(DataType.Date)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    [Display(Name = "Extension Date")]
    public DateTime ExtensionDate { get; set; }

    [Required]
    [Range(1, 100)]
    [Display(Name = "Days Extended")]
    public int DaysExtended { get; set; }

    [Required]
    [Display(Name = "Details")]
    public string Details { get; set; }

    [Required]
    [ForeignKey("ExtendedByUser")]
    public string ExtendedByUserId { get; set; }
    public ApplicationUser ExtendedByUser { get; set; }
}

public class Record
{
    // Id
    [Key]
    public string Id { get; set; }

    // Status
    [Required]
    public RecordStatus Status { get; set; }

    // Date Created
    [Required]
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    [Display(Name = "Date Created")]
    public DateTime DateCreated { get; set; }

    // Created By
    [ForeignKey("CreatedByUser")]
    public string CreatedByUserId { get; set; }
    public ApplicationUser CreatedByUser { get; set; }

    // Date of last edit
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    public DateTime? LastEditedDate { get; set; }

    // User who performed last edit
    [ForeignKey("LastEditedByUser")]
    public string LastEditedByUserId { get; set; }
    public ApplicationUser LastEditedByUser { get; set; }

    // Submission date
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
    [Display(Name = "Date Submitted")]
    public DateTime? SubmissionDate { get; set; }

    // User who submitted the record
    [ForeignKey("SubmittedByUser")]
    public string SubmittedByUserId { get; set; }
    public ApplicationUser SubmittedByUser { get; set; }
}

The ApplicationUser class is just the default class for users created by the Visual Studio ASP.NET MVC template.

I'm able to create Extensions and save them to the database just fine, but when I try to edit them I get a DbUpdateConcurrencyException. For example, the following code throws an exception without fail.

class Program
{
    static void Main(string[] args)
    {
        using (ApplicationDbContext db = new ApplicationDbContext())
        {
            Extension extension = new Extension
            {
                Details = "Details for a new extension.",
                DaysExtended = 25,
                ExtendedByUserId = db.Users.First().Id,
                RecordId = db.Records.First().Id,
                ExtensionDate = DateTime.Now
            };

            // create
            db.Extensions.Add(extension);
            db.SaveChanges(); // <-- Works

            // now edit
            extension.Details = "some new details";
            extension.DaysExtended = 40;

            db.SaveChanges(); // <-- Fails every time
        }
    }
}

Could someone please shed some light on why this may be happening? I know that the exception means that the entry has been modified since it was loaded into the context, but there is no reason that the record should have changed between the creation of the entry and the modification of the entry.

Please let me know if more information is required.

Update

Well, I was able to fix it...

The C# DateTime object and the "datetime" data type in SQL Server have compatibility issues. Somehow the DateTime stored in my Extension object is considered unequal to the "datetime" value stored in the database. This causes Entity Framework to throw the concurrency exception whenever I try to update an Extension record because it believes that the values have been changed since the entry was loaded.

In my case, there were two ways to fix this problem (I went with the latter, because it was more aligned with my original intentions).

  1. Set the data type of the column used to represent the ExtensionDate property to "datetime2".
  2. Set the data type of the column used to represent the ExtensionDate property to "date" and use only the Date when working with DateTime.

I'll post this as an answer, but I'd like to know the exact cause of the incompatibility before posting. Does anyone know why these two types are incompatible?

Community
  • 1
  • 1
Tyler K
  • 110
  • 1
  • 11
  • Are you trying to update an existing entry? – Ram's stack Aug 18 '17 at 18:41
  • Have you verified that the initial `SaveChanges()` call does indeed save the entity to the db? If not, then that would cause the second call to `SaveChanges` to return 0, and then throw the exception. – bcwiniger Aug 18 '17 at 18:43
  • Doesn't seem anything wrong by glancing. This may be helpful : https://stackoverflow.com/questions/22490842/finding-the-reason-for-dbupdateexception – Ram's stack Aug 18 '17 at 19:08
  • I'm betting it has something to do with your ExtendedByUser child. You may need to tell EF in between steps `extension.ExtendedByUser.State = EntityState.Unchanged;` or similar. – Steve Greene Aug 18 '17 at 19:15
  • @bcwiniger Yes. I'm able to verify using Visual Studio's SQL Server Explorer that the entry is inserted into the database. – Tyler K Aug 19 '17 at 16:10
  • Is there a way to determine which properties on the entity caused the exception? – Tyler K Aug 21 '17 at 17:00

1 Answers1

1

This is due to the value of DateTime.Now that is being stored in memory compared to the value that is being stored in SQL Server. datetime has a time range that can store between 00:00:00 through 23:59:59.997. datetime2 has a larger storage range between 00:00:00 through 23:59:59.9999999.

joncloud
  • 757
  • 5
  • 14