How to check whether model have some changes in C# MVC - c#

I have appSourceInfoModel taken from Database, now i am passing ViewModel i.e. reviewerAppsBacklogViewModel and if reviewerAppsBacklogViewModel and appSourceInfoModel are same then do not update database if there are changes then Update. I am doing, first assigning to variable var DBappSourceInfoModel = appSourceInfoModel; then I assigning some values to appSourceInfoModel then comparing the initially saved model DBappSourceInfoModel and appSourceInfoModel. But, assigning some values to appSourceInfoModel also change values in the initially saved model DBappSourceInfoModel. All of the code can be found below.
AppSourceInfo appSourceInfoModel = _appSourceInfoRepository.Get(a => a.Review.ReviewId == reviewId);
var DBappSourceInfoModel = appSourceInfoModel; //Initially save Model in var
appSourceInfoModel.Cost = reviewerAppsBacklogViewModel.Cost;
appSourceInfoModel.InProgress = true;
appSourceInfoModel.PegiRating = reviewerAppsBacklogViewModel.PegiRating;
appSourceInfoModel.Rating = reviewerAppsBacklogViewModel.AverageUserReviewsRating;
appSourceInfoModel.DownloadCounter = reviewerAppsBacklogViewModel.NoofDownloadsFromSource;
appSourceInfoModel.ReviewCounter = reviewerAppsBacklogViewModel.NoofReviewOfSource;
appSourceInfoModel.StoreCategory = reviewerAppsBacklogViewModel.StoreCategory;
var IsAppSourceInfoModelChanged = !DBappSourceInfoModel.Equals(appSourceInfoModel);
if (IsAppSourceInfoModelChanged)
{
_appSourceInfoRepository.Update(appSourceInfoModel);
}
I have Solved it using this simple Code in My Model i.e. AppSourceInfo
public object Clone()
{
return this.MemberwiseClone();
}
and change the following code
var DBappSourceInfoModel = appSourceInfoModel; //Initially save Model in var
to
var DBappSourceInfoModel = (AppSourceInfo) appSourceInfoModel.Clone();

You need to perform a Copy (shallow probably sufficient)
var DBappSourceInfoModel = appSourceInfoModel;
Is simply creating a reference to the same object. Implement ICloneable on the DBappSourceInfoModel then use Clone method,
Your Clone method needs to copy all the info to the new Object, also performing Deep Copies on internal references if needed,
This will copy all the details to the other object and create two separate objects,
look here for reference:
https://msdn.microsoft.com/en-us/library/system.icloneable%28v=vs.110%29.aspx
EDIT
Just to be clear, you also need to use the IComparable interface to define how the objects are compared for equality,
https://msdn.microsoft.com/en-us/library/system.icomparable%28v=vs.110%29.aspx

Related

Changing one property in multidimensional array changes other array's properties too

I have a multidimensional array called SensorGetResult. In some cases this array can have a single array in it. If that happens, I must copy this one array and add it again, so I must have 2 arrays now. Then I need to change these array's dateTime property. This is my code:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
sensorGet.SensorGetResult[0].dateTime = end; //It works correctly including this line
sensorGet.SensorGetResult[1].dateTime = start; //At this line both array's dateTime property changes
Why can't I assign dateTime properties to each array individually?
It looks like you are using a reference type for your helperArray.
When the following code executes:
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
What actually happens is you take a the first element of SensorGetResult which is a reference to the object (which I believe you intend to copy) and append the reference to the list thus resulting in a list which has two references to the same object in the memory.
If you want it to make a copy of the object, you have to implement that by yourself. Usually this means creating a new object of the same type and copying all the properties.
var objectToCopy = sensorGet.SensorGetResult[0];
var helperArray = new WhatEverTypeIsYourHelperArray {
Property1 = objectToCopy.Property1,
Property2 = objectToCopy.Property2,
// etc.
};
sensorGet.SensorGetResult.Add(helperArray);
But you have to be aware if any of the properties is furthermore a reference type, you need to do this recursively for all the properties.
If WhatEverTypeIsYourHelperArray is type you own, you could utilize Object.MemberwiseClone method and make it all easier for yourself. You can do this by implementing a method like the following. As a note, MemberwiseClone is a protected method hence the need of a new method in your class.
public WhatEverTypeIsYourHelperArray Clone() {
return (WhatEverTypeIsYourHelperArray)this.MemberWiseClone();
}
But even the MemberwiseClone() method doesn't copy reference types for you, rather just copies the pointers to the objects which means that all the properties of reference type of both the original and the cloned object will point to the same objects in the memory.
SensorGetResult row seems to be a reference type.
So when you wrote
var helperArray = sensorGet.SensorGetResult[0];
sensorGet.SensorGetResult.Add(helperArray);
you actually said that new row in SensorGetResult will point to the same object as the first one.
You can implement method like below:
public SensorGetResultRow Clone()
{
return new SensorGetResultRow (this.field1, this.field2, etc...)
//or if you use parameterless constructor
return new SensorGetResultRow ()
{
field1 = this.field1
//etc.
}
}
and use it:
var helperArray = sensorGet.SensorGetResult[0].Clone();

Why do I get a reference instead of a copy in this C# return?

I have this part of code:
MeasDataSet dset = new MeasDataSet();
MeasDataPoint point = new MeasDataPoint();
//...
MeasDataSet dset2 = new MeasDataSet(dset._path);
dset2.SaveResults();
point = dset2.GetDataPointAt(dset2.Size - 1);
point.Current = 7566;
dset2.SaveResults();
Where MeasDataPoint and Set are just some classes containing measurement data (point a single point and set a collection of points with additional methods)
When calling SaveResults() it should save the data inside the DataSet to a file, but using the code above doesn't save the old point but instead the altered one (point.Current = 7566;).
So basically point now changes my values inside my data set instead of being a copy what i expected it to be.
GetDataPointAt (method of DatasSet):
public MeasDataPoint GetDataPointAt(int numberOfPoint)
{
return _dataPoints.ElementAt(numberOfPoint);
}
Anyone an idea why it behaves this way?
Is dset._path the entire collection of MeasDataPoint objects? If so, it appears the constructor new MeasDataSet(dset._path) is simply internally storing a reference to your original dset collection of points which means it is manipulating your original collection. See Passing Parameters (C# Programming Guide).
You could extend or modify the MeasDataSet constructor to iterate over the MeasDataPoint objects to create new ones by deep copying when assigning to dset2. Here is a rough idea:
public MeasDataSet(List<MeasDataPoint> path) {
var copiedPath = new List<MeasDataPoint>();
foreach(var point in path) {
copiedPath.Add(new MeasDataPoint() {
Prop1 = point.Prop1,
Prop2 = point.Prop2
});
}
// do something with your newly created deep copy of copiedPath...
}

Object properties changing in C# when saving an old copy

I have this code below where I fetch a object 'yogaSpaceEvent' from the DB as a entity.
I want to save the values before I modify anything and then pass the old values (as a object) and the object with the newly saved values to another method 'getEditedEventEmail' to do some logic. But the 'oldEvent' object has all the new values of 'newEvent'.
YogaSpaceEvent yogaSpaceEvent = yogaSpace.YogaSpaceEvents.Where(k => k.YogaSpaceEventId == details.EventId).First();
if (yogaSpaceEvent == null)
throw new Exception("You Don't Have A Yoga Space Event With This ID!");
YogaSpaceEvent oldEvent = yogaSpaceEvent; // save all the old values in this object
var displayedTime = details.StartTime.GetType().GetMember(details.StartTime.ToString()).First().GetCustomAttribute<DisplayAttribute>().Name;
yogaSpaceEvent.EventDateTime = details.EventDate.AddHours(DateTime.Parse(displayedTime).Hour).AddMinutes(DateTime.Parse(displayedTime).Minute);
yogaSpaceEvent.Time = details.StartTime;
yogaSpaceEvent.Duration = details.Duration;
yogaSpaceEvent.Style = details.Style;
yogaSpaceEvent.DateUpdated = DateTime.Now;
YogaSpaceEvent newEvent = yogaSpaceEvent; // save all the new values in this object to compare with the old object
Tuple<string, string> editedEventEmail = _emailingService.GetEditedEventEmail(oldEvent, newEvent);
Assuming YogaSpaceEvent is a reference type:
YogaSpaceEvent oldEvent = yogaSpaceEvent;
Just copies off the reference. Both references still point to the same object. If after that line you had written:
yogaSpaceEvent = new YogaSpaceEvent();
Then you would get the behavior you are looking for as the two variables would be pointing at different objects. You can also do a "deep copy" into oldevent but the techniques for doing so in the general case are a bit more involved (for simple cases, MemberwiseClone is fine).
The general case of deep copying is is discussed further in: How do you do a deep copy of an object in .NET (C# specifically)?
Please test this:
YogaSpaceEvent oldEvent = new YogaSpaceEvent{
property1 = yogaSpaceEvent.property1,
property2 = yogaSpaceEvent.property2,
.
.
.
};

Add to list when list type is not known until runtime

I have method which accepts an object. This object I know is a List<T> however T may vary between children of a base class at any one time when being passed into the method.
So if my base class is MonthType, and I have children called BlockMonthType and AreaMonthType the object passed in could be anyone of List<BlockMonthType> or List<AreaMonthType>.
I want to be able to add items to this object however when I cast it it seems to make a copy and the original object is not updated.
I'm doing this to cast:
var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();
Now I want to create a new item and add it to the list
// where ObjectType is a Type variable containing BlockMonthType
var newObject = (BlockMonthType)Activator.CreateInstance(graphObject.Source.ObjectType);
objectList.Add(newObject);
// and carry on the world is good
This works in so far as objectList has a newObject added. However the original variable isn't updated so when I leave the method it's back to it's original state. I know the object is a List<> when passed in as I can see it in the debugger as such.
Is there anyway I can accomplish this?
Here is a cut down version of the method I'm using it in.
public TraverseGraphResult Write(ObjectGraph graphObject)
{
var objectList = ((IEnumerable<MonthType>)graphObject.Source.Object).ToList();
var newObject = (MonthType)Activator.CreateInstance(rule.ObjectType);
newObject.Month = rule.Month;
objectList.Add(newObject);
// Other stuff as well is done but that's the crux of it
}
Hopefully this gives it more context. The method is being used to try and navigate a large object tree with many class types. I'm trying to add a new class type handler which will deal with adding and removing items from a list.
// This is being used in a recursive method to loop down a object's property tree
// .. more code here
// where properties is a List<PropertyInfo>
foreach (var pInfo in properties)
{
if (IsList(pInfo.PropertyType))
{
var enumerable = (IEnumerable)pInfo.GetValue(currentObjectGraph.Source.Object, null);
var sourceEnumerator = enumerable.GetEnumerator();
var graph = new ObjectGraph(enumerable, pInfo.Name);
// this part is made up but essentially the code looks up a list of objects that can deal with this
// particular one and returns it. We then call the write method on that object
var something = GetInterfaceHandlerForObject(enumerable);
something.Write(graph);
}
}
You should make your method generic:
public void MyMethod<T>(List<T> objectList) where T:class, new()
{
objectList.Add(new T());
...
}
Casting is rarely ever necessary when you use generics. Also, your ToList() is causing a new copy of the list to be created.
One drawback to this approach is that T needs to have an empty constructor. If you need to construct an object with parameters you could instead pass in a Func<T>. You can then call it passing in a lambda expression like: (x) => new BlockMonthType(someParameter, orAnother).
I ended up resolving this by storing the underlying List T type in the ObjectGraph object and casting to that when required.
var objectList = ((IEnumerable)graphObject.Source.Object).Cast(monthAllocationRule.ListType);
Without the correct cast objectList was either null or a copy of the list. Now I can add to objectList and know it's added to the source object.
Probably not idea as Ian mentioned above but did the trick.

Strange Reflection problem - can't get it

please help me understand what's going on in here and whether it should work like that ?
I have a generic list of objects from a CMS:
For example List<MyCMS.Articles.Article> myArticles = articles.All;
Later I output the contents of the list in a JSON format (for CMS UI - table list).
Now a single record would include:
article.Title
article.Alias
article.Guid
article.Description
+
article.SeoProperties.TitleOverride
article.SeoProperties.H1Tag
article.StateProperties.IsActive
article.StateProperties.Channels
etc...
as you can see an Article object has an additional class property - with common properties (used on other object types in the CMS)
I also use a filter class that does some filter operations with LINQ on the collection to return me only articles within a certain channel, for example...
So the problem is that when I serialize the collection as JSON - there are only a few "columns" that I really need to display in my table list, while I have no need in other fields - especially, possibly long fields such as "description" (lazy loaded from file system), etc... - I serialize with DataContractJsonSerializer...
I need a way to control what fields will be included in the JSON result... What I do is I use reflection to set property values to null if I don't need the property and
decorate class properties with [DataMember(IsRequired = false, EmitDefaultValue = false)] attributes... - it should work well - but - as soon as I go over (even cloned!!) collection of final objects to strip off the fields = set value to "null" - property value becomes null - application wide - in all collections of such objects... eh ?
Some demo code in here:
void Page_Load() {
MyCms.Content.Games games = new MyCms.Content.Games();
List<MyCms.Content.Games.Game> allGames = games.All;
MyCms.Content.Games games2 = new MyCms.Content.Games();
List<MyCms.Content.Games.Game> allGamesOther = games2.All;
Response.Write("Total games: " + allGames.Count + "<br />");
//This is our fields stripper - with result assigned to a new list
List<MyCms.Content.Games.Game> completelyUnrelatedOtherIsolated_but_notSureList = RemoveUnusedFields(allGamesOther);
List<MyCms.Content.Games.Game> gamesFiltered = allGames.Where(g=>g.GamingProperties.Software=="89070ef9-e115-4907-9996-6421e6013993").ToList();
Response.Write("Filtered games: " + gamesFiltered.Count + "<br /><br />");
}
private List<MyCms.Content.Games.Game> RemoveUnusedFields(List<MyCms.Content.Games.Game> games)
{
List<MyCms.Content.Games.Game> result = new List<MyCms.Content.Games.Game>();
if (games != null && games.Count > 0)
{
//Retrieve a list of current object properties
List<string> myContentProperties = MyCms.Utils.GetContentProperties(games[0]);
MyCms.PropertyReflector pF = new MyCms.PropertyReflector();
foreach (MyCms.Content.Games.Game contentItem in games)
{
MyCms.Content.Games.Game myNewGame = (MyCms.Content.Games.Game)contentItem.Clone();
myNewGame.Images = "wtf!"; //just to be sure we do set this stuff not only null
pF.SetValue(myNewGame, "GamingProperties.Software", ""); //set one property to null for testing
result.Add(myNewGame);
}
}
return result;
}
Objects are set to their "Default values" (basically, null, in most cases) with this:
private object GetDefaultValue(Type type)
{
if (type.IsValueType)
{
try
{
return Activator.CreateInstance(type);
}
catch {
return null;
}
}
return null;
}
Quite probably your having trouble with differentiating between a shallow copy and a deep copy.
If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
With a deep copy when you clone an object and that object has a field of reference type, a new clone of that object is created and assigned to the field (vs just referencing the first object). Thus you have to completely different objects that share nothing.
That means if you're using clone and some of the properties are in fact subproperties (that is, properties of an instance inside the original object) you're changing it application wide, because you're acting upon a reference, not a new instance of the subobject.
You have more information about it in
http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx
You could create a kind of modelview classes with the necessary fields and use something like Automapper to fill them. By this way, you will have a nice code, easy to maintain and highly customizable.

Categories