4

If I have the following entity:

public class PocoWithDates {
    public string PocoName { get; set; }
    public DateTime DateCreated { get; set; }
   public DateTime DateUpdated { get; set; }
}

corresponding to an SQL Server 2008 (R2) table with the same name/attributes...

How can I automatically: - Set the DateCreated and DateUpdated field to now when doing an INSERT - Set the DateUpdated field to now when doing an UPDATE

When I say automatically, I mean I want to be able to do this:

poco.Name = "Changing the name";
repository.Save(); 

Not this:

poco.Name = "Changing the name";
poco.LastModified = DateTime.Now;
repository.Save();

Behind the scenes, "something" should automatically update the DateTime fields. What is that "something"? I'm using Entity Framework 6.0. Is there a way that EF can do that automatically? I know MySQL can do something like this. Or using triggers/stored procedures in SQL Server. But I do NOT want that.

This question is an alternative and altered version of this question: Entity Framework/SQL2008 - How to Automatically Update LastModified fields for Entities?

This question got some great solutions/answers for EF 4.0. But I want it for EF6.0 with a DBContext. If I had more time I would try and adapt those solutions and get them working in EF6.0. But for now I'm wondering if anybody solved this already, or knows how to transcribe the custom SaveChanges() or Add() overrides for EF4.0 to EF6.0.

Community
  • 1
  • 1
Bart
  • 5,065
  • 1
  • 35
  • 43

3 Answers3

5

You can override the SaveChanges() method on your DbContext to update the DateUpdated value on Save(). I would implement an interface to allow for some items to be updated and others not.

public interface IEntityAutoDateFields
{
    DateTime DateCreated { get; set; }
    DateTime DateUpdated { get; set; }
}

And then:

public class PocoWithDates : IEntityAutoDateFields
{
    public PocoWithDates()
    {

    }

    public DateTime DateCreated { get; set; }
    public DateTime DateUpdated { get; set; }
}

And in your context:

public PocoWithDatesContext : DbContext
{
     public DbSet<PocoWithDates> PocoWithDatesSet { get; set;}

     public override int SaveChanges()
     {
         var now = DateTime.Now;             

         this.ChangeTracker.DetectChanges();
         foreach (var item in this.ChangeTracker.Entries()
                               .Where(i => i.State == EntityState.Added || i.State == EntityState.Modified)
                               .Where(i => i as IEntityAutoDateFields != null))
         {
             if (item.State == EntityState.Added)
             {
                  (item as IEntityAutoDateFields).DateCreated = now;
             }
             (item as IEntityAutoDateFields).DateUpdated = now;
         }
         // Call the SaveChanges method on the context;
         return base.SaveChanges();
     }
}
bebleo
  • 326
  • 4
  • 12
  • 1
    Great! I am going with this answer. Right now I'm resolving some other issues, before I can fully test. I did rename the interface to `IEntityAutoDateFields` and added a test/list in the `SaveChanges` override for `EntityState.Added` for which both the DateCreated and DateUpdated, are set. I want to do both in one place instead of needing to implement a constructor for each new class implementing `IEntityAutoDateFields`. @James-warne, can you change those two things in your answer as well, then I'll mark your answer as thé answer. Otherwise I'll add my own answer based on yours after I'm done. – Bart Feb 25 '14 at 09:59
1

This is based on Ben Cull solution. I just added handling the async method SaveChangesAsync().

1 Create Base Class

public class BaseEntity
{
    public DateTime? DateCreated { get; set; }
    public string UserCreated { get; set; }
    public DateTime? DateModified { get; set; }
    public string UserModified { get; set; }
}

2 Apply Base to Existing class

public class Student : BaseEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

3 Override SaveOn() and SaveOnSync() within DBContext class

public class SchoolContext : DbContext
{

public override int SaveChanges()
{
    var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));

    var currentUsername = HttpContext.Current != null && HttpContext.Current.User != null
        ? HttpContext.Current.User.Identity.Name
        : "Anonymous";

    foreach (var entity in entities)
    {
        if (entity.State == EntityState.Added)
        {
            ((BaseEntity)entity.Entity).DateCreated = DateTime.Now;
            ((BaseEntity)entity.Entity).UserCreated = currentUsername;
        }

        ((BaseEntity)entity.Entity).DateModified = DateTime.Now;
        ((BaseEntity)entity.Entity).UserModified = currentUsername;
    }

    return base.SaveChanges();
}
}

public override Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken)

    {
        var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));

        var currentUsername = HttpContext.Current != null && HttpContext.Current.User != null
            ? HttpContext.Current.User.Identity.Name : "Anonymous";


        foreach (var entity in entities)
        {
            if (entity.State == EntityState.Added)
            {
                ((BaseEntity)entity.Entity).DateCreated = DateTime.Now;
                ((BaseEntity)entity.Entity).UserCreated = currentUsername;
            }

            ((BaseEntity)entity.Entity).DateModified = DateTime.Now;
            ((BaseEntity)entity.Entity).UserModified = currentUsername;
        }

        return base.SaveChangesAsync(cancellationToken);
    }
    }
Barry MSIH
  • 3,525
  • 5
  • 32
  • 53
0

Add a contructor to set the DateCreated/DateUpdated on a new instance of your entity.

public class PocoWithDates {
    public PocoWithDates()
    {
        this.DateCreated = DateTime.Now;
        this.DateUpdated = this.DateCreated;
    }

    public string PocoName { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateUpdated { get; set; }
}

As far as automatically modifying the DateUpdated on a Save() you may want to look here.

CanMan
  • 26
  • 7