Prevent update on certain fields

45 views
Skip to first unread message

Dannie F

unread,
Nov 17, 2015, 10:29:38 AM11/17/15
to SharpRepository
Hi,

I have some audit fields, CreatedAt, CreatedBy, ModifiedAt and ModifiedBy. I would like to prevent the update of the CreatedAt and CreatedBy fields when updating. I figured the best way to do this is by using the OnUpdatingExecuting hook to run code like this:

 db.Entry(model).State = EntityState.Modified;
 db.Entry(model).Property(x => x.Token).IsModified=false;
 db.SaveChanges();

However, I can't seem to find a way to access the DbContext in the attribute. The Dbcontext property  in the EFRepository class is protected.

I have thought of a few workarounds but I don't really like any of them

  1. I refresh the values of the entity from the db, then apply only the changed values so that the correct CreatedAt and CreatedBy will be saved. (Or cache the original values so that I can set the CreatedAt and CreatedBy fields before updating)
  2. Created a custom base repository class that inherits from IRepository and override the update method. All other repositories that have audit fields will have to use the base repsoitory
  3. Use service locator pattern to get the dbContext into the attribute. As in this example: http://fairwaytech.com/2014/01/simple-auditing-as-a-cross-cutting-concern-with-sharprepository/
Has anyone encountered this problem? How do you deal with it? In general, how to deal with situations where you need access to the DbContext?

Jeff Treuting

unread,
Nov 17, 2015, 12:43:03 PM11/17/15
to Dannie F, SharpRepository

Where are the CreatedAt and CreatedBy fields getting updated in the first place?  I have used the approach in the article you linked to (your #3 below) on a few projects and it's worked great for me.  The AuditAttribute is in charge of setting the Created values and the Modified values, so you don't have to undo any of them, but it controls all of that for you.


Is it not possible to control the setting of those values?  If that is out of your control and they get set when they shouldn't, I'm not sure that is the responsibility of the repository to fix, but if it needs to be, then I guess you could get access to the DbContext by created a base class that your repositories inherit from that give you access to that property.  The downside there is that you are locking yourself into EF being the underlying data access layer.


You could also do a Get from the repository to get the original values and replace them, but that obviously is not very performant and does an extra DB call.




From: sharpre...@googlegroups.com <sharpre...@googlegroups.com> on behalf of Dannie F <debbie...@gmail.com>
Sent: Tuesday, November 17, 2015 7:29 AM
To: SharpRepository
Subject: [SharpRepository] Prevent update on certain fields
 
--
You received this message because you are subscribed to the Google Groups "SharpRepository" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sharpreposito...@googlegroups.com.
To post to this group, send email to sharpre...@googlegroups.com.
Visit this group at http://groups.google.com/group/sharprepository.
To view this discussion on the web visit https://groups.google.com/d/msgid/sharprepository/9719047b-4e24-4188-ac90-80dcae5800af%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dannie F

unread,
Nov 17, 2015, 2:11:27 PM11/17/15
to SharpRepository, debbie...@gmail.com
Hi Jeff,

Thanks for replying so quickly. I wonder if I am missing something obvious since you have not encountered my problem in your projects.

My model is updated by creating an instance of my entity from my view model. When I call the update method of the repository, then all the fields are updated, this includes the CreatedAt and CreatedBy fields. If I don't get those values from the database (or cache those values somewhere when i first retrieve the values), so that I can set those values in the entity when updating, then the those values will be updated in the database and set to null. 

In the example in the link provided, the value is first retrieved from the database, the properties changed, then an update executed. So that scenario works fine because the createdAt and createdBy properties have the correct values from the database before the update. But I am trying to avoid having to get the data first before doing an update.

So without first doing a get, how can I prevent those fields from being updated?

Thanks.

Dannie F

unread,
Nov 17, 2015, 2:17:33 PM11/17/15
to SharpRepository, debbie...@gmail.com
I went ahead and implemented the 3rd option i stated and that seems to be working OK. 

Another question i have though is: Will I still benefit from caching etc if I override the OnUpdateExecuting and return false?

Jeff Treuting

unread,
Nov 17, 2015, 3:49:56 PM11/17/15
to Dannie F, SharpRepository

If you return false from OnUpdateExecuting then the update won't actually complete and so the cache will not get updated, but the database shouldn't get updated either.  Returning false, essentially stops the update from happening.  This is designed so that the hooks can check values and stop the Add or Update from completing, which can be useful in some cases.


Most of the time you will be returning true, because you want the Add or Update to actually happen.


Jeff Treuting
Director of Software Delivery 

 
Fairway Technologies Inc. | Our People, Your Solution.®

  (858) 454-4471 x209
  (310) 428-5426

fairwaytech.com | 
   




Sent: Tuesday, November 17, 2015 11:17 AM
To: SharpRepository
Cc: debbie...@gmail.com
Subject: Re: [SharpRepository] Prevent update on certain fields
 

Dannie F

unread,
Nov 17, 2015, 4:45:49 PM11/17/15
to SharpRepository, debbie...@gmail.com
It seems my current implementation will not work if I also want to enable caching. I am currently using the below code. I am returning false because I am essentially performing the update manually


        public override bool OnUpdateExecuting<T, TKey>(T entity, RepositoryActionContext<T, TKey> context)
       
{
           
if (db != null)
           
{
               
IAuditable auditableEntity = entity as IAuditable;


               
if (auditableEntity != null)
               
{
                    db
.Entry(auditableEntity).State = EntityState.Modified;                  
                    db
.Entry(auditableEntity).Property(x => x.CreatedAt).IsModified = false;
                    db
.Entry(auditableEntity).Property(x => x.CreatedBy).IsModified = false;
                    db
.SaveChanges();


                   
return false;
               
}
           
}


           
return true;
       
}

Not sure you saw my first reply. I was explaining that:

My model is updated by creating an instance of my entity from my view model. When I call the update method of the repository, then all the fields are updated, this includes the CreatedAt and CreatedBy fields. If I don't first get those values from the database before updating (or cache those values somewhere when i first retrieve the values), then the those values will be updated in the database and set to null. Is there any way to avoid this without first retrieving the values just before updating or saving the values in session or some other cache?

Thanks for all your help.

Jeff Treuting

unread,
Nov 17, 2015, 4:55:31 PM11/17/15
to Dannie F, SharpRepository

I think I'm missing a key piece to the puzzle.


You said "My model is updated by creating an instance of my entity from my view model. When I call the update method of the repository, then all the fields are updated, this includes the CreatedAt and CreatedBy fields."


When you pass the model into the repository, are the value for CreatedAt and CreatedBy already updated to something?  I'm assuming that the view model is not passing those values back in, so it feels like they should be the default values, either NULL or 1/1/0001 (I think that is the default for DateTime).  So when do those get set if you aren't specifically setting them yourself?  Is it updating them at the database level maybe?


The idea behind the AuditAttribute is that it will take care of managing the values of the Created and Modified fields so that you don't do it or worry about it anywhere else.  No one else should be touching those values and then you can set them in the attribute based on if it is an update or insert, and then you don't have to tie into the DbContext directly at all because you are just updating properties on the model that is then going to b saved by the underlying DbContext.


Jeff Treuting
Director of Software Delivery 

 
Fairway Technologies Inc. | Our People, Your Solution.®

  (858) 454-4471 x209
  (310) 428-5426

fairwaytech.com | 
   



Sent: Tuesday, November 17, 2015 1:45 PM

To: SharpRepository
Cc: debbie...@gmail.com
Subject: Re: [SharpRepository] Prevent update on certain fields
--
You received this message because you are subscribed to the Google Groups "SharpRepository" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sharpreposito...@googlegroups.com.
To post to this group, send email to sharpre...@googlegroups.com.
Visit this group at http://groups.google.com/group/sharprepository.

Dannie F

unread,
Nov 17, 2015, 5:09:45 PM11/17/15
to SharpRepository, debbie...@gmail.com
The CreatedAt and CreatedBy are not updated to anything. They have their default values as you surmised. (The CreatedAt is NULL the CreatedBy is 1/1/0001). So if I then repo.Update(entity), the repo will try to updated values in the db to NULL and 1/1/0001/

I understand that in the Audit attribute, I can set these fields in the OnAddExecuting method, but in OnUpdateExecuting, these fields will be set to NULL and 1/1/0001/ because they are not set when converting from the view model to the entity. (The view model does not have these fields and I wouldn't store them there because of the possibility that these values can be changed on the client). So in my OnUpdateExecuting method, I am setting LastModifiedAt  and LastModifiedBy, but the CreatedAt is NULL the CreatedBy is 1/1/0001 so those are the values that will be saved in the db when updating. So, this is why i wanted to prevent these fields from being updating during a repo Update call.

Hope my problem is clearer.

Jeff Treuting

unread,
Nov 17, 2015, 6:46:21 PM11/17/15
to Dannie F, SharpRepository

I see what you are saying now.


Have you tried, not calling SaveChanges() where you are and instead flag those fields as not modified and return true.  Then SaveCHanges() will get called by the repository later on and the caching will happen as well.  


You will need to test if the updated cached values have the proper values for the Created properties as they might not in this case, but I'm not sure off the top of my head.


Jeff Treuting
Director of Software Delivery 

 
Fairway Technologies Inc. | Our People, Your Solution.®

  (858) 454-4471 x209
  (310) 428-5426

fairwaytech.com | 
   



Sent: Tuesday, November 17, 2015 2:09 PM

Dannie F

unread,
Nov 18, 2015, 2:17:19 PM11/18/15
to SharpRepository, debbie...@gmail.com
I had tried what you suggested, but that doesn't work as the repository code is expecting the entity to be detached. If I detach it after setting the the field properties as not modified, then the context still tries to update those fields. I suspect that this is because the update method in the repository code sets the entity state as modified again, which resets all fields to be modified.

Would love a more elegant way to do this, but i think i will have to refresh the entity before updating. Do you think the possibility to specify which fields should be excluded or included as an overload of the update method could be added as a future enhancement?
Reply all
Reply to author
Forward
0 new messages