TempData keep() vs peek() - asp.net-mvc

What is the difference between keep() and peek()?
MSDN says:
keep(): marks the specified key in the dictionary for retention.
peek(): returns an object that contains the element that is
associated with the specified key, without marking the key for
deletion.
I can't get really what the difference is, don't they both keep a value for another request?

When an object in a TempDataDictionary is read, it will be marked for deletion at the end of that request.
That means if you put something on TempData like
TempData["value"] = "someValueForNextRequest";
And on another request you access it, the value will be there but as soon as you read it, the value will be marked for deletion:
//second request, read value and is marked for deletion
object value = TempData["value"];
//third request, value is not there as it was deleted at the end of the second request
TempData["value"] == null
The Peek and Keep methods allow you to read the value without marking it for deletion. Say we get back to the first request where the value was saved to TempData.
With Peek you get the value without marking it for deletion with a single call, see msdn:
//second request, PEEK value so it is not deleted at the end of the request
object value = TempData.Peek("value");
//third request, read value and mark it for deletion
object value = TempData["value"];
With Keep you specify a key that was marked for deletion that you want to keep. Retrieving the object and later on saving it from deletion are 2 different calls. See msdn
//second request, get value marking it from deletion
object value = TempData["value"];
//later on decide to keep it
TempData.Keep("value");
//third request, read value and mark it for deletion
object value = TempData["value"];
You can use Peek when you always want to retain the value for another request. Use Keep when retaining the value depends on additional logic.
You have 2 good questions about how TempData works here and here
Hope it helps!

Just finished understanding Peek and Keep and had same confusion initially. The confusion arises becauses TempData behaves differently under different condition. You can watch this video which explains the Keep and Peek with demonstration https://www.facebook.com/video.php?v=689393794478113
Tempdata helps to preserve values for a single request and CAN ALSO preserve values for the next request depending on 4 conditions”.
If we understand these 4 points you would see more clarity.Below is a diagram with all 4 conditions, read the third and fourth point which talks about Peek and Keep.
Condition 1 (Not read):- If you set a “TempData” inside your action and if you do not read it in your view then “TempData” will be persisted for the next request.
Condition 2 ( Normal Read) :- If you read the “TempData” normally like the below code it will not persist for the next request.
string str = TempData["MyData"];
Even if you are displaying it’s a normal read like the code below.
#TempData["MyData"];
Condition 3 (Read and Keep) :- If you read the “TempData” and call the “Keep” method it will be persisted.
#TempData["MyData"];
TempData.Keep("MyData");
Condition 4 ( Peek and Read) :- If you read “TempData” by using the “Peek” method it will persist for the next request.
string str = TempData.Peek("Td").ToString();
Reference :- http://www.codeproject.com/Articles/818493/MVC-Tempdata-Peek-and-Keep-confusion

TempData is also a dictionary object that stays for the time of an
HTTP Request. So, TempData can be used to maintain data between one
controller action to the other controller action.
TempData is used to check the null values each time. TempData contain
two method keep() and peek() for maintain data state from one
controller action to others.
When TempDataDictionary object is read, At the end of request marks
as deletion to current read object.
The keep() and peek() method is used to read the data without deletion
the current read object.
You can use Peek() when you always want to hold/prevent the value for
another request. You can use Keep() when prevent/hold the value
depends on additional logic.
Overloading in TempData.Peek() & TempData.Keep() as given below.
TempData.Keep() have 2 overloaded methods.
void keep() : That menace all the data not deleted on current request completion.
void keep(string key) : persist the specific item in TempData with help of name.
TempData.Peek() no overloaded methods.
object peek(string key) : return an object that contain items with specific key without making key for deletion.
Example for return type of TempData.Keep() & TempData.Peek() methods as given below.
public void Keep(string key) {
_retainedKeys.Add(key); }
public object Peek(string key) {
object value = values;
return value; }

don't they both keep a value for another request?
Yes they do, but when the first one is void, the second one returns and object:
public void Keep(string key)
{
_retainedKeys.Add(key); // just adds the key to the collection for retention
}
public object Peek(string key)
{
object value;
_data.TryGetValue(key, out value);
return value; // returns an object without marking it for deletion
}

Keep() method marks the specified key in the dictionary for retention
You can use Keep() when prevent/hold the value depends on additional logic.
when you read TempData one’s and want to hold for another request then use keep method, so TempData can available for next request as above example.

Related

Swift: How do I store an array of object by reference from completion handler (closure)?

There's a API callback returns a Json format result. The requirement in short is that I need to keep calling this API and keeps implement Breadth First Search on the results it returns.
Imaging it's a map with many nodes and connections. Every time I call API call for a node, it gonna return me list of its connected nodes. All I need now, is an array, which saves all the node that has been visited to avoid repeated visits.
But this is Swift and I am new to it. I was using Array and pass as inout inside the completion handler. But there's an error: escaping closures can only capture inout parameters explicitly by value which means I cannot do it like this.
Now you may ask why the array I have has to be stored by reference. Because the API call is async, which means I have to wait until it comes back to keep progressing Breadth First Search, means I have to pass this array by reference in order to do the recursion.
What other solutions I may have?
Swift Arrays are value types (not reference types) so you will need to store you array in an object. You can then pass the object to your handler and set the array content inside the object which is carried as a reference in the closures.

Determine which entity properties have been modified in BeforeEntitySave

Using a custom EFContextProvider, I want to check which properties have been modified on an entity before it saves, so that I can implement:
Security: The client has permission to change only certain properties of an entity.
Auditing: Whenever certain properties are changed, the change needs to be logged.
There are suggestions on SO to use OriginalValuesMap to determine the modified properties, see here and here. If the original value differs from the new value, the property has been modified. However, these original values are supplied by the client, and thus can be forged to match the new values, bypassing this check.
The first SO question I linked suggests this is not an issue, because if the original values are forged in such a way, those properties won't be saved anyway:
For any other "unchanged" property, which we are not using in any way, we don't need to worry if it has been tampered with because, even if it has, the tampered value will not be persisted to the database
This is untrue however, as long as all modified properties on the entity have their original values forged. For example, the following code will bypass server-side security checks based on OriginalValuesMap and still save to the database:
manager.fetchEntityByKey('Employee', 42).then(function (result) {
var employee = result.entity;
employee.Salary(1000000); // do you think HR will notice?
delete employee.entityAspect.originalValues.Salary;
return manager.saveChanges();
});
When Breeze .NET receives the entity, it adds the entity to an Entity Framework context in Modified state, and with no properties marked as modified, Entity Framework's behaviour is to save all the supplied property values to the database.
IMO this is a security bug in EFContextProvider.HandleModified, where it overrides the EF entity state to Modified (there is even a comment in that method warning not to do so). In any case, what is the correct way to determine which properties have changed and are about to be saved?
In your Context intercept Save and check if it is legal save or not. For the sake of explanation, let's say you want to save entity of type RestrictedClass and you defined table RestrictedClasses which imitates table in your database.
public override int SaveChanges()
{
foreach (
var entry in
this.ChangeTracker.Entries()
.Where((e => (e.State == (EntityState) Breeze.WebApi.EntityState.Modified))))
{
if (entry.Entity.GetType() == typeof(RestrictedClass))
{
var entity = entry.Entity as RestrictedClass;
var originalEntities = RestrictedClasses.Where(e => e.Id = entity.Id).toList();
if (originalEntities.Count == 0) continue; // user is trying to add, illegal since it says it's modified, you do different check for EntityState.Added
var originalEntity = originalEntities[0]; // there should be only one, unique ID
//.... now you check differences between entity and originalEntity and decide whether it's legal or not based on user role.

Sending NSNotifications to all objects of a class

I have an object that can be selected by a user click. With the current requirements of the app, at any time, there is no more than one of these items selected at any point during app execution.
I implemented a mechanism to enforce this, as follows:
Each of these objects has a unique identifier as a property.
When each object is created, it subscribes to the NSNotificationCenter listening for the MY_OBJECT_SELECTED notification.
When each object is selected, it posts the MY_OBJECT_SELECTED notification, with its unique Id as part of the userInfo dictionary.
Then, when each object receives the notification, it checks to see if its id is the same as the one in the userInfo. If it is, it does nothing, but if it isn't, it sets itself to unselected.
Is this a decent approach to the problem? If not, how would you do it?
It is a decent way of doing it, although it is not very efficient. The more objects you have, the more time you spend comparing IDs. The easiest way is to store your object pointers and IDs in a map table (or similar) and remember the last selected object. Whenever you select a new object, you clear the selection flag of the last selected object, then look up the new object and set its selection flag. It requires you to keep a collection of your objects, though.
The time required to update selections with this approach is independent of the number of objects you have.
If the object is spread all over the app,i.e. if it is a member in various classes. You can have a global object of same type and assign it to only that object which has been touched. In steps it will be like:
Have a global variable of object type.
At any object touch assign globalObject = currentObject;
do all operations on globalObject throughout app like calling methods and modifying object members(have a check for nil to ensure safety).
Reassign to different object with new touch.

Hydrate related objects

I am looking for a simple way to hydrate a related object. A Note belongs to a Document and only owners of a Document can add Notes so when a user tries to edit a Note, I need to hydrate the related Document in order to find out if the user has access to it. In my Service layer I have the following:
public void editNote(Note note)
{
// Get the associated Document object (required for validation) and validate.
int docID = noteRepository.Find(note.NoteID).DocumentID;
note.Document = documentRepository.Find(docID);
IDictionary<string, string> errors = note.validate();
if (errors.Count > 0)
{
throw new ValidationException(errors);
}
// Update Repository and save.
noteRepository.InsertOrUpdate(note);
noteRepository.Save();
}
Trouble is, noteRepository.InsertOrUpdate(note) throws an exception with "An object with the same key already exists in the ObjectStateManager." when the repository sets EntityState.Modified. So a number of questions arise:
Am I approaching this correctly and if so, how do I get around the exception?
Currently, the controller edit action takes in a NoteCreateEditViewModel. Now this does have a DocumentID field as this is required when creating a new Note as we need to know which Document to attach it to. But for edit, I cannot use it as a malicious user could provide a DocumentID to which they do have access and thus edit a Note they don't own. So should there be seperate viewmodels for create and edit or can I just exclude the DocumentID somehow on edit? Or is there a better way to go about viewmodels such that an ID is not required?
Is there a better way to approach this? I have read that I should just have a Document repository as an aggregate and lose the Note repository but am not sure if/how this helps.
I asked a similar question related to this but it wasn't very clear so hoping this version will allow someone to understand and thus point me in the right direction.
EDIT
Based on the information provided by Ladislav Mrnka and the answer detailed here: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key, it seems that my repository method need to be like the following:
public void InsertOrUpdate(Note note)
{
if (note.NoteID == default(int)) {
// New entity
context.Notes.Add(note);
} else {
// Existing entity
//context.Entry(note).State = EntityState.Modified;
context.Entry(oldNote).CurrentValues.SetValues(note);
}
}
But how do I get the oldNote from the context? I could call context.Entry(Find(note.NoteID)).CurrentValues.SetValues(note) but am I introducing potential problems here?
Am I approaching this correctly and if so, how do I get around the exception?
I guess this part of your code loads the whole Node from the database to find DocumentID:
int docID = noteRepository.Find(note.NoteID).DocumentID;
In such case your InsertOrUpdate cannot take your node and attach it to context with Modified state because you already have note with the same key in the context. Common solution is to use this:
objectContext.NoteSet.ApplyCurrentValues(note);
objectContext.SaveChanges();
But for edit, I cannot use it as a malicious user could provide a DocumentID to which they do have access and thus edit a Note they don't own.
In such case you must add some security. You can add any data into hidden fields in your page but those data which mustn't be changed by the client must contain some additional security. For example second hidden field with either signature computed on server or hash of salted value computed on server. When the data return in the next request to the server, it must recompute and compare signature / hash with same salt and validate that the passed value and computed value are same. Sure the client mustn't know the secret you are using to compute signature or salt used in hash.
I have read that I should just have a Document repository as an aggregate and lose the Note repository but am not sure if/how this helps.
This is cleaner way to use repositories but it will not help you with your particular error because you will still need Note and DocumentId.

Update only the changed values on Entity object

how can i automatically update my entity objects changed values and save them to db.
I hava an Action like that
public ActionResult Update()
{
User userToUpdate = new User();
TryUpdateModel<User>(userToUpdate,ValueProvider);
BaseRepository.Context.AttachTo("User",userToUpdate);
BaseRepository.Context.SaveChanges();
return Json("");
}
ValuProvider : has the items that come
from the client as post data.
The problem on this code is the code update all the values but i want to update only the changed values.
How can i find the changed values on my entity object.
You should check out the ObjectContext.ApplyPropertyChanges Method
it is suppose to do what your asking for...
msdn
Two options:
On the View you could know the values that were changed by using Javascript and then you could pass that information to your controller.
You could simply compare the previous values (which you already have since you populated a view) and check each value before updating the DB.
I prefer last option, since at this point you could also check for data validation.
This is really a problem for your data access code, not anything to do with your controller. Pick an ORM that handles this for you and forget about the problem.

Resources