I have a model that I am retrieving from a cache, which I use on several pages. Part of the model has a property called Breadcrumb - which is a collection of breadcrumb elements and is updated like this:
model.Breadcrumbs.Add(UrlUtilities.Urls.DesignerJoinConfirmation, "Feedback Confirmation", "Thank you for your feedback");
My problem is that even though I am getting the model back from the cache, when I add another breadcrumb after it is returned from the cache (eg for child pages I would add the latest page onto the end), it is then updating the cached model. Is there a way of updating the model and not updating the cache?
I have tried creating a temp model and reassigning the cached object to it like so:
Model model = GetCachedModel();
Model tempModel = new Model();
tempModel = model;
And then updating the tempModel but this also updates the cached object. The only way I have found to stop the cache object being updated is by making the tempModel and then reassigning each property separately from the cached model but surely this negates the need for caching if I have to reassign all the properties each time. Is there a simpler way to update a cached object without updating the cache itself?
I think you are mixing references to objects with the objects. When you retrieve the model from the cache you are really only getting another reference to the same object that the cache is referencing. Thus any changes made via your reference will also be visible via any other references.
There are two approaches to avoiding this:
Clone—copy—the model instance from the cache. This will require assistance from the implementation to do in general. Remember this will need to be a deep copy, any properties and fields of the model that are themselves references will also need the referenced object to be copied. Recursively.
Don't put the breadcrumbs in the model. If difference uses of the same model need different values for this property then it does not belong in the model.
(There are also options using value types—which are copied rather than referenced—but good practice is (1) vale types should be small (not many fields), and (2) immutable.)
Model model = GetCachedModel();
Model tempModel = new Model();
tempModel = model;
That's not going to work. You are creating a reference to a new instance (tempModel) and then overwriting that reference with a reference to the original from the cache.
Related
I am building a control in xamarin forms that binds to a list of objects. To get this binding to work I need to use observable collections (otherwise propertychanged methods don't fire).
I noticed a really frustrating interaction as a result of needing to use OC's as opposed to lists. Whenever the binded OC updates, the values in my controls are automatically updated, even if they are just references of the OC, Here is how i am copying the OC.
//Internal list of events
private List<EventItem> _events;
void OnEventsChanged(ObservableCollection<EventItem> eventsCollection)
{
//Error handle
List<EventItem> events = eventsCollection.ToList();
//Do something
_events = events;
}
The problem comes when the OC updates, I want to check for new/deleted AND altered objects. The issue is that when the OC updates, it is updating the internal list (_events) aswell. This means when I go to do comparisons between the old & new values, they are the same.
Honestly I don't really understand how c# handles copying references of objects around, I had a similar issue a while back with DateTime.Now being calculated as opposed to copying the value of the already initialised object.
var time = DateTime.Now;
await Task.Delay(1000);
var time2 = time; //This is 1 second later than time, not the value of time (which is what I wanted)
I have used Objective-C in the past and that has the concept of MutableCopy where you can assign a new list from an existing one, they have the same values but aren't linked.
How can I do this in C# so that my controls internal list is only updated by me and not the OC?
Thanks
That's perfectly normal. If I have enough time, I'll try to explain it to you.
In a nutshell, the observableList (or a List) is a list of reference to the objects and not a list of objects. The thing is that the objects are not copied inside a list but the list contains a reference to the different objects. That means that if you do something like ToList(), you get another list of references to the exact same objects.
Now to solve your problem. Just create a new list with new objects with something like
var newList = oldList.Select(x => new Model(x)).ToList();
And of course the Model class has a constructor that accept a Model as a parameter and copy the properties.
When you write _events = events;, you create not a new object, but a reference for the same object. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/index .
You should to clone (create a copy of object itself) as it mentioned in comment by #Matt.
I am brand new to ASP.Net MVC. So, keep that in mind. :)
I have a view with nested collections, with fields that get updated by the end user.
I have the updated model data successfully coming back from an AJAX call to my controller.
So... now i have a model with nested collection objects - where each item in collection also carries a collection. (BTW) This is a very low volume app.
I am not currently using EF - strictly using old fashion SQL calls and sprocs.
Is there an easy way to determine which values of my collection have been updated without out drawing out the whole collection from database again and iterating the whole thing? ugh...
You would use Knockout Js and Observable Arrays will help you to track all changes on the View. Knockout tracks and updates all things automatically, you just need create Update() method and mark changable variables as observables.
I have a viewmodel which has some observables and observableArrays.
Depending on the page you are looking at it will fill up a certain observable or observable array. and then map it.
However from time to time there might be a subproperty which has currently a null value in it which i can create a new instance of through an api call (default values and so on server sided handled, seperation of concerns in mind, no Business Logic client sided!).
So when i do the $.get() request i placed it directly in the object and the data shows up in my viewmodel, however it is not mapped to the fields where they are bound to?
Binding works, but only one way (read). What do i miss? The api call (example), which places everything in the vm:
$.get('../../../../../api/addresses/?id=new&subjectid=' + subjectid, null, vm.currentSubject()['CurrentAddressInfo'])
maybe its been a long day, but I just cant figure this out.
I retrieve a large custom object from WCF, and store it in an application variable.
This happens every 20 minutes.
For each web user, I am checking the existence and timeout of this application variable, and if needed, re-query my wcf and build a new object, and re-store it in the application variable.
This all good and well.
Now, I am trying to "make a copy" of this "master" object, modify it, and store it in a session variable, modifying it as needed throughout the session life cycle. (modifying the session variable).
Everytime I modify the session object, the object in the application variable gets modified.
Pseudo
application("mastervar") = object from wcf (obejct type - xcustomclass)
dim mynewobject as new xcustomclass
mynewobject = application("mastervar")
* Modifying mynewobject, also modifies application("mastervar")
I have tried:
session("mynewSessionVar") = application("mastervar")
mynewobject = session("mynewSessionVar")
Modifying mynewobject, modifies application("mastervar")
I have tried:
Manually copying all properties in mastervar object to new object, with a for loop.
mycustomobject = new xcustomclass
mycustomobjectObject as new xcustomclass.object
mymasterobject = application("mastervar")
for each object in mymasterobject.objectslist
mycustomobjectObject = new xcustomclass.object
with mycustomobjectObject
.property = object.property
end with
mycustomobject.objectlist.add(mycustomobjectObject)
next
Same thing, modifying mycustomobject, also modifies application("mastervar")
As I said, maybe its been a long day, but I've been bumping my head against this for hours...
EDIT
Private Function copy_fresh_units(unitsFromWcf As WebResortUnits) As WebResortUnits
Dim myFreshUnits As New WebResortUnits
Dim myFreshUnit As WebResortUnits.qbunit
For Each Unit In unitsFromWcf.resortUnits
myFreshUnit = New WebResortUnits.qbunit
With myFreshUnit
' .Availability = Unit.Availability
.mapDetails = Unit.mapDetails
End With
myFreshUnits.resortUnits.Add(Unit)
Next
return myFreshUnits
End Function
Modifying the availability property in myfreshUnits, it still updates the app var. I have had a look at reference and value types, and it is definitely my issue. But taking this alst edit into account, I know I am missing something, what it is, I am not sure... :-)
You are creating a new reference to the objects and then modifying the object that you reference and expecting your two references to not be the same object?
It sounds like what you really want to do is to clone the objects or make a deepcopy of the objects.
If these are custom objects you will need custom code to make a Clone of them.
When you Clone your object be sure that you create a new object and set all of the value types on that new object from your old object. Then go through and clone all of your reference types and set the reference properties on your clone to point to the clones of the properties you've created.
EDIT: To address your update
The problem is still... everything you are copying is obviously a reference type and it's not being cloned. So there is only one object in existence therefor when you edit either reference it changes the object.
EDIT 2: Serialization will help you
Serializing and deserializing your base object in memory is an easy way to clone it. I generally write a custom clone method that serializes / deserializes the object. That way you have a Clone() method that you will always call and any custom code you need that doesn't get handled properly with serialize / deserialize you can handle in that method.
My guess is that the properties you are copying are not all primitives themselves (int, float, string, etc). When you then alter the corresponding property on the new object, it is then altering the original property (since it's not a primitive and is really an object reference).
Check out https://github.com/JesseBuesking/BB.DeepCopy which is probably a bit of overkill, but if you read what's under The problem this addresses you can see alternative approaches for helping in this situation.
Hello fellow developers.
First of all I apologize beforehand for the wall of text that follows, but after a day going crazy on this, I need to call for help.
I've stumbled across a problem I cannot seem to solve. I'll try to describe the scenario in the best possible way.
Task at hand: in an existing Asp.Net Mvc application, create a lookup table for an integer field, and use the textual value from the lookup in the editing view. When saving, we must first check if the lookup already has a corresponding text value for the same Root ID. If there is, use that. Otherwise, create it and then use it.
The structure:
The data model is a graph of objects where we have the root object, a collection of level A child objects, and every level A child object has a collection of level B child objects, so something like this:
Root (with fields)
Level A child (with fields) x n
Level B child (with fields) x n
The field we have to handle is on the LevelB objects.
There is a single Mvc view that handles the whole data. For collection objects, all fields are named like levelA1levelB1MyField, levelA1levelB2MyField, etc so every single field has unique name during the post. When the post happens, all values are read through a formCollection parameter which has average 120/130 keys. The keys are isolated by splitting them and looping on the numerical part of the names, values are read and parsed to the expected types and assigned to the object graph.
The datalayer part backing the object graph is all stored procedures, and all the mapping (both object to sproc and sproc to object) is hand written. There's a single stored procedure for the read part, which gets multiple datasets, and the method calling it reads the datasets and creates the object graph.
For the saving, there are multiple sprocs, mainly a "CreateRoot" and "UpdateRoot". When the code has to perform such tasks, the following happens:
For create scenario, "CreateRoot" is called, then the sprocs "CreateLevelA" and "CreateLevelB" are called in loop for each element in the graph;
For update scenario, "UpdateRoot" is called, which internally deletes all "LevelA" and "LevelB" items, then the code recreates them calling the aforementioned sprocs in loop.
Last useful piece of information is that the "business objects graph" is used directly as a viewmodel in the view, instead of being mapped to a plain "html friendly" viewmodel. This is maybe what is causing me the most trouble.
So now the textbox on the view handles an "integer" field. That field must now accept a string. The field on LevelB must remain an integer, only with a lookup table (with FK of course) and the text field from the lookup must be used.
The approaches I tried with no success:
My first thought was to change the datatype on the property MyField from integer to string on the object, then change the sprocs accordingly and handle the join at sproc level: I'd have a consistent object for my view, and the read/write sprocs could translate from string to integer and viceversa, but I can't do that because the join keys to retrieve the integer when writing are part of the Root item (as I stated in the first lines of this wall of text), which I don't know in the CreateLevelB sproc, and changing the whole chain of calls to pass those parameters would have a huge impact on the rest of the application, so no good.
My next try was to keep things "as they are" and call some "translation methods": when reading, pass the integer to the view, and there call the translation method to display the text value. When saving, use the posted text to retrieve the integer. The save part would work, I'd have all the parameters I need, but for the read part, I'd have to instantiate the "data access layer" and call its method at View level, and there's no need to explain why that is a very bad choice, so I ruled this out too.
Now I'm out of options (or ideas anyway). Any suggestion to solve this is very welcome, and also if something is not clear enough just point it out and I will edit my post with more accurate information.
Thanks.
This is not a real answer but you could rip out all sprocs and use the updating facilities of an OR mapper. This will resolve all the layering issues. You just update data how you see fit and submit at the end.
I guess this would also make the questions around "should I use an int or a string" go away.
Edit: After reading your comment I thought of the following: Do not implement alternative 1. You rather want to sacrifice code quality in the view than in the data storage model. The last one is more important and more centrally used.
I would not be too concerned with messing up the view by calling the DAL from it or the like. Changes in a view are localized and do not mess up the application's architecture. They just degrade the view.
Maybe you could create a view model in your controller and do the translations between DAL-model and view model? Or is that pattern not allowed?