Adding CreatedDate to an entity using Entity Framework 5 Code First - entity-framework

I am trying to add a CreatedDate property to entities in my Model and am using EF5 Code First. I want this date to not be changed once set, I want it to be a UTC date. I do NOT want to use a constructor, as I have many entities in my model that I want to inherit from an abstract class containing the CreatedDate property, and I can't enforce a constructor with an interface.
I have tried different data annotations and I have attempted to write a database initializer that would pick up a specific entity type and write an alter constraint with a getdate() default value for the correct table_name and column_name, but I have not been able to write that code correctly.
Please do not refer me to the AuditDbContext - Entity Framework Auditing Context or the EntityFramework.Extended tools, as they do not do what I need here.
UPDATE
My CreatedDate is null on SaveChanges() because I am passing a ViewModel to my view, which correctly has no audit property called CreatedDate in it. And even if I passed the model to my view, I am not editing or storing the CreatedDate in the view.
I read here that I could add the [DatabaseGenerated(DatabaseGeneratedOption.Identity)] and this would tell EF to store the CreatedDate correctly after Insert and Update, but not allow it to be changed by my application: but I just get a Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF error by adding this attribute.
I am about to switch to EF Model First because this simple database requirement is ridiculous to implement in Code First.

Here is how I did it:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate{ get; set; }
in my migration's Up() method:
AddColumn("Agents", "CreatedDate", n => n.DateTime(nullable: false, defaultValueSql: "GETUTCDATE()"));

Override the SaveChanges-Method in your context:
public override int SaveChanges()
{
DateTime saveTime = DateTime.UtcNow;
foreach (var entry in this.ChangeTracker.Entries().Where(e => e.State == (EntityState) System.Data.EntityState.Added))
{
if (entry.Property("CreatedDate").CurrentValue == null)
entry.Property("CreatedDate").CurrentValue = saveTime;
}
return base.SaveChanges();
}
Updated because of comments: only freshly added Entities will have their Date set.

Similar to Stephans's Answer but with Reflection and also ignores all user (external) updates Created/Updated times. Show Gist
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(x => x.Entity.GetType().GetProperty("CreatedTime") != null))
{
if (entry.State == EntityState.Added)
{
entry.Property("CreatedTime").CurrentValue = DateTime.Now;
}
else if (entry.State == EntityState.Modified)
{
// Ignore the CreatedTime updates on Modified entities.
entry.Property("CreatedTime").IsModified = false;
}
// Always set UpdatedTime. Assuming all entities having CreatedTime property
// Also have UpdatedTime
// entry.Property("UpdatedTime").CurrentValue = DateTime.Now;
// I moved this part to another foreach loop
}
foreach (var entry in ChangeTracker.Entries().Where(
e =>
e.Entity.GetType().GetProperty("UpdatedTime") != null &&
e.State == EntityState.Modified ||
e.State == EntityState.Added))
{
entry.Property("UpdatedTime").CurrentValue = DateTime.Now;
}
return base.SaveChanges();
}

Ok so the primary issue here was that CreatedDate was being Updated every time I called SaveChanges and since I wasn't passing CreatedDate to my views it was being updated to NULL or MinDate by Entity Framework.
The solution was simple, knowing that I only need to set the CreatedDate when EntityState.Added, I just set my entity.CreatedDate.IsModified = false before doing any work in my SaveChanges override, that way I ignored changes from Updates and if it was an Add the CreatedDate would be set a few lines later.

Code First doesn't currently provide a mechanism for providing column default values.
You will need to manually modify or create base class to automatic update CreatedDate
public abstract class MyBaseClass
{
public MyBaseClass()
{
CreatedDate = DateTime.Now;
}
public Datetime CreatedDate { get; set; }
}

For EF Core you can find the MS recommended solution here:
Default Values.
Use Fluent API in your DBContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}

Accounts account;
account.Acct_JoinDate = DateTime.Now.ToUniversalTime();
data.Accounts.Add(account);
data.SaveChanges();
Why not give the timestamp upon model creation? Similar to these accounts here.

Related

Perform action when an entity is updated in Entity Framework

Is there a way to run code when an entity is updated? For example I have an entity that is updated many places in my code. I want to be able to update a DateTimeUpdated field any time that entity is updated without changing every function that updates that entity.
This is typically done by overriding the SaveChanges method in the DbContext.
Start by introducing a base class or a common interface for your editable entities that you want to track the DateTimeUpdated for:
public abstract class EditableEntityBase
{
public DateTime DateTimeUpdated { get; internal set; }
}
Your entities that you want to track this for should extend this class or implement a contract interface that will expose the property.
Then in your DbContext, override the SaveChanges method and insert:
var updatedEntities = ChangeTracker.Entries()
.Where(x => x.State == EntityState.Modified)
.Select(x => x.Entity)
.OfType<EditableEntityBase>();
foreach (var entity in updatedEntities)
{
entity.DateTimeUpdated = DateTime.Now; // or DateTime.UtcNow
}
return base.SaveChanges();
You can also include x.State == EntityState.Added for new records, though generally I'd rely on a Default at the DB to capture it on insert.

How to update the "LastModifiedDate" timestamp automatically on parent entity when adding/removing child entities

Is there a way to automatically enforce parent entity to be timestamped as having been modified, if any of its dependent child items are added/deleted/modified? The key word is automatically. I know this can be done by manipulating the DbEntry's EntityState or by manually setting the timestamp field in the parent, but I need this done on a number of parent-child entities in a system, so the desire is to have EF (or a related component) automatically do this somehow.
More Background and Examples
Let's say we have an Order and Order Items (1-many). When order items are added/removed from an order, the parent order itself needs to be updated to store the last modified timestamp.
public interface IModifiableEntity
{
DateTime LastModifiedOn { get; set; }
}
public class Order : IModifiableEntity
{
// some Order fields here...
// timestamp for tracking when the order was changed
public DateTime LastModifiedOn { get; set; }
// list of order items in a child collection
public ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderId { get; set; }
// other order item fields...
}
Somewhere in application logic:
public void AddOrderItem(OrderItem orderItem)
{
var order = _myDb.Orders.Single(o => o.Id == orderItem.OrderId);
order.OrderItems.Add(orderItem);
_myDb.SaveChanges();
}
I already have a pattern in place to detect modified entities and set timestamps automatically via EF's SaveChanges, like this:
public override int SaveChanges()
{
var timestamp = DateTime.Now;
foreach (var modifiableEntity in ChangeTracker.Entries<IModifiableEntity>())
{
if (modifiableEntity.State == EntityState.Modified)
{
modifiableEntity.Entity.UpdatedOn = timestamp;
}
}
return base.SaveChanges();
}
That works great if any direct fields on an IModifiableEntity are updated. That entity's state will then be marked as Modified by EF, and my custom SaveChanges() above will catch it and set the timestamp field correctly.
The problem is, if you only interact with a child collection property, the parent entity is not marked as modified by EF. I know I can manually force that via context.Entry(myEntity).State or just by manually setting the LastModifiedOn field when adding child items in application logic, but that wouldn't be done centrally, and is easy to forget.
I DO NOT want to do this:
public void AddOrderItem(OrderItem orderItem)
{
var order = _myDb.Orders.Single(o => o.Id == orderItem.OrderId);
order.OrderItems.Add(orderItem);
// this works but is very manual and EF infrastructure specific
_myDb.Entry(order).State = EntityState.Modified;
// this also works but is very manual and easy to forget
order.LastModifiedOn = DateTime.Now;
_myDb.SaveChanges();
}
Any way I can do this centrally and inform EF that a "root" entity of a parent-child relationship needs to be marked as having been updated?

How to add entity with existing attached to it

I want to add entity payment object, containing EXISTING Currency object to EF database:
public Payment()
{
int Id {get;set;}
public int Value {get;set;}
public Currency SelectedCurrency{get;set;}
}
public Currency()
{
int Id {get;set;}
string Name;
}
Suppose that I have existing Currency attached to new entity Payment(). When I add such entity Payment(), the error appears
Violation of PRIMARY KEY constraint 'PK_dbo.Currency'. Cannot insert duplicate key in object 'dbo.MwbeCurrency'. The duplicate key value is (GBP).\r\nThe statement has been terminated."}
How to add higher-level entity with attached existing lower-level entity?
My code for adding entity is:
public virtual TEntity Add(TEntity entity)
{
return DbSet.Add(entity);
}
public void SaveChanges()
{
Context.SaveChanges();
}
I suspect you retrieved Currency with a different instance than the one that retrieved Payment and did something like this :
payment.Currency = retrievedCurrency;
Therefore, the Payment context things that Currency is a new object and tries to persist it. Since it already exists, you are getting a PRIMARY KEY violation.
If you want to persist Payment correctly, add the following lines:
if (payment.Currency != null && payment.Currency.Id != 0)
{
context.Entry(payment.Currency).State = EntityState.Unchanged;
}
although it would probably be cleaner if you retrieved Payment and Currency with the same context, so you can persist them appropriately.
Calling DbSet.Add(entity) adds the entire graph for persistence, which means it will go through all the navigation properties of entity and set each one's state to EntityState.Added.
While the other answer might work, a better approach is to change the way you are adding the objects, and be explicit about what entities you are adding / updating / etc.
To do this, change:
public virtual void Add(TEntity entity)
{
DbSet.Add(entity);
}
To:
public virtual void Add(TEntity entity)
{
context.Entry(entity).State = EntityState.Added;
}
This will add only the supplied entity. If one of your navigation properties objects is also new, you call .Add(entity) on it as well.
If you do need to add the entire graph in other situations, you can add an additional method that works the way your original one does, but has a better name to indicate it's function:
public virtual void AddGraph(TEntity entity)
{
DbSet.Add(entity);
}
Good Luck
Update
Additionally, since it looks like you are using a repository, I prefer to disable auto detect changes by setting context.Configuration.AutoDetectChangesEnabled = false; If you modify a property on an entity that you want persisted, you would need to set the state of the entity to modified like so:
public virtual void Update(TEntity entity)
{
context.Entry(entity).State = EntityState.Modified;
}

Adding a new entity to collection in attached entity causes ConcurrencyException

I have simplified the code below to show the root of the problem. My real code is using GenericRepository and UnitOfWork pattern but I get the same exception with this simplified code too.
I am using Entity Framework 6, Code First
It uses the following POCO entities
public class Order
{
public int Id {get;set;}
public virtual List<OrderProducts> OrderProducts {get;set;}
...
}
public class Product
{
public int Id {get;set;}
...
}
public class OrderProduct
{
public int OrderId {get;set;}
public int ProductId {get;set;}
public int Quantity
public virtual Order Order { get; set; }
public virtual Product Product{ get; set; }
}
The user is able to create a new product and add it to the order products on the same screen.
//Pull an order from the database:
var existingOrder = db.Orders.FirstOrDefault(x => x.Id == inputModel.OrderId);
//Iterate the OrderProductInputModels (IMs) in the Inputmodel
foreach (var orderProductIM in inputModel.OrderProductIMs )
{
var orderProduct = existingOrder.OrderProducts.SingleOrDefault(o => o.Id == orderProductIM.Id);
//if its an existing order product (already in db)
if (orderProduct != null)
{
//just update its property values
}
//if it has been added
else
{
//we need to create a new product first
var newProduct= new Product() { <set some properties> };
orderProduct= new OrderProduct()
{
Product=newProduct,
Order=existingOrder
}
//Add the OrderProduct to the order
existingOrder.OrderProducts.Add(orderProduct);
}
db.SaveChanges();
On save changes, I get the following error.
[System.Data.Entity.Infrastructure.DbUpdateConcurrencyException] = {"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."}
Why is this?
I expected entity framework to see that the existingOrders nested properties were newly added and unattached, update the order and create the new OrderProduct and Product.
Should it not be other way around in your if clause as you are checking for null ( then only it is a new order product else update. Issue is here:
//if its an existing order product (already in db)
if (orderProduct == null)
{
//just update its property values
}
//if it has been added
else
{
When you are looping around all the OrderProducts, you are constantly updating the database but the existingOrder object is not getting refreshed. Update that or add all the objects first and then update the database.
Finally solved it by creating a test project and reverse code first engineering the database. Noticed that OrderProduct entity was not generated. On inspecting the database, the primary key was not set. Once I set the primary key in the database, the issue was resolved. Thanks for all the suggestions.

Entity Framework validation with partial updates

I'm using Entity Framework 5.0 with DbContext and POCO entities. There's a simple entity containing 3 properties:
public class Record
{
public int Id { get; set; }
public string Title { get; set; }
public bool IsActive { get; set; }
}
The Title field is always unmodified, and the UI simply displays it without providing any input box to modify it. That's why the Title field is set to null when the form is sent to the server.
Here's how I tell EF to perform partial update of the entity (IsActive field only):
public class EFRepository<TEntity>
{
...
public void PartialUpdate(TEntity entity, params Expression<Func<TEntity, object>>[] propsToUpdate)
{
dbSet.Attach(entity);
var entry = _dbContext.Entry(entity);
foreach(var prop in propsToUpdate)
contextEntry.Property(prop).IsModified = true;
}
}
and the call:
repository.PartialUpdate(updatedRecord, r => r.IsActive);
Calling SaveChanges method, I get the DbEntityValidationException, that tells me, Title is required. When I set dbContext.Configuration.ValidateOnSaveEnabled = false, everything is OK.
Is there any way to avoid disabling validation on the whole context and to tell EF not to validate properties that are not being updated?
Thanks in advance.
If you use partial updates or stub entities (both approaches are pretty valid!) you cannot use global EF validation because it doesn't respect your partial changes - it always validates whole entity. With default validation logic you must turn it off by calling mentioned:
dbContext.Configuration.ValidateOnSaveEnabled = false
And validate every updated property separately. This should hopefully do the magic but I didn't try it because I don't use EF validation at all:
foreach(var prop in propsToUpdate) {
var errors = contextEntry.Property(prop).GetValidationErrors();
if (erros.Count == 0) {
contextEntry.Property(prop).IsModified = true;
} else {
...
}
}
If you want to go step further you can try overriding ValidateEntity in your context and reimplement validation in the way that it validates whole entity or only selected properties based on state of the entity and IsModified state of properties - that will allow you using EF validation with partial updates and stub entities.
Validation in EF is IMHO wrong concept - it introduces additional logic into data access layer where the logic doesn't belong to. It is mostly based on the idea that you always work with whole entity or even with whole entity graph if you place required validation rules on navigation properties. Once you violate this approach you will always find that single fixed set of validation rules hardcoded to your entities is not sufficient.
One of things I have in my very long backlog is to investigate how validation affects speed of SaveChanges operation - I used to have my own validation API in EF4 (prior to EF4.1) based on DataAnnotations and their Validator class and I stopped using it quite soon due to very poor performance.
Workaround with using native SQL has same effect as using stub entities or partial updates with turned off validation = your entities are still not validated but in addition your changes are not part of same unit of work.
In reference to Ladislav's answer, I've added this to the DbContext class, and it now removes all the properties that aren't modified.
I know its not completely skipping the validation for those properties but rather just omitting it, but EF validates per entity not property, and rewriting the entire validation process anew was too much of hassle for me.
protected override DbEntityValidationResult ValidateEntity(
DbEntityEntry entityEntry,
IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
var falseErrors = result.ValidationErrors
.Where(error =>
{
if (entityEntry.State != EntityState.Modified) return false;
var member = entityEntry.Member(error.PropertyName);
var property = member as DbPropertyEntry;
if (property != null)
return !property.IsModified;
else
return false;//not false err;
});
foreach (var error in falseErrors.ToArray())
result.ValidationErrors.Remove(error);
return result;
}
This is a remix of previous #Shimmy response and it's a version that I currently use.
What I've added is the clause (entityEntry.State != EntityState.Modified) return false; in the Where:
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
var falseErrors = result
.ValidationErrors
.Where(error =>
{
if (entityEntry.State != EntityState.Modified) return false;
var member = entityEntry.Member(error.PropertyName);
var property = member as DbPropertyEntry;
if (property != null) return !property.IsModified;
return false;
});
foreach (var error in falseErrors.ToArray())
{
result.ValidationErrors.Remove(error);
}
return result;
}