I am using standard notification pattern with INotifyPropertyChanged. Here is my sample property:
private string fanart;
public string Fanart
{
get { return fanart; }
set
{
if (fanart != value)
{
fanart = value;
NotifyPropertyChanged("Fanart");
}
}
}
Here, I expect the setter to skip everything if new value is equal to the current value. However, when I check using debugger, fanart always equals null and consequently, the event is always fired. Any reason for the filed (and property) to be null?
Edit: This property is part of class called PlayerItem. PlayerItem is part of another class PlayerState. All these classes extend from NotifyBase. A method RefreshPlayerState periodically refreshes the PlayerState. I just noticed that new instance of PlayerItem is created every time player state is refreshed. What could be causing this?
Edit2: Creation of new instance is expected since I am deserializing the object from a JSON response. Now, how do I prevent setter from setting the value if older values are equal to values deserialized from the JSON? myObj != value will always return true since both are different objects although contained data is same.
This code is part of project here. Feel free to look in for more details.
"fanart always equals null" means most likely, you are recreating instances of the class that owns this field (which I assume is your View Model). You should keep the reference to your bound VM and work on that instance.
Edit: Currently I am working with a Windows 7 machine so I could not see your code. But you are removing those players somewhere; those that you create at PlayerHelper and Player classes.
Related
I find myself a bit lost on this one. I honestly can't see the error if it's just a class structure doesn't match JSON error. But I doubt it since it's the very same class structure I'm using to create the JSON.
If anyone can point me in the right direction, I'd be most greateful.
I've created a dotnetfiddle to avoid clutching the question with huge pieces of code. Here's the link: Fiddle
I generate that JSON with a console application that gets info on the DB schema. I use a common project with all the entities defined in it to load the data in memory and then generate the JSON from that structure. Then I use the same project with the same entities on another application to compare another DB schema to the JSON log. That application is unable to deserialize the JSON. Tried to provide a minimal example with a single class and as you can see on the fiddle...that doesn't deserialize either.
It is my understanding that ObservableCollections should in fact serialize and deserialize without issues, and that INotifyPropertyChange should not cause issues (as long as you're not trying to fire an event with a null reference). So...anyone has any idea what's going on here?.
EDIT: Forgot to mention. Notice how only the base type string gets deserialized...so it IS running some deserialization, just not of classes like ObservableCollection or user classes. Maybe that helps somehow to pinpoint the issue.
EDIT2: Added a trace writer and the JSON.Net trace is detecting the right type for the objects, so I'm guessing the issue is on converting types or initializing some of the types
The problem is in how your property getters work combined with the default ObjectCreationHandling setting in Json.Net. Allow me to explain:
By default, if a reference property has an existing (non-null) value during deserialization, Json.Net tries to reuse the existing instance and populate it instead of creating a new instance. To find out whether the property has a value, Json.Net calls the getter. In your case, the getter returns a new instance when the backing field is null, but, critically, it does not set the backing field to the new instance:
get { return _header ?? new StoredProcedureDataHeader(); }
Json.Net then populates the new instance. Because the backing field was never set to the new instance, that instance ultimately gets thrown away. Json.Net never calls your setter because it assumes that your object already has a reference to the new instance, since it got that instance from the getter. Then, when you next call that getter after deserialization, you get a new, empty instance back instead of what you were expecting.
There are two ways to fix the problem:
Change your getters to set the backing field whenever a new instance is created, for example:
get
{
if (_header == null)
{
_header = new StoredProcedureDataHeader();
}
return _header;
}
OR
Change the ObjectCreationHandling setting to Replace to force Json.Net to always create new instances when deserializing. Json.Net will then call the setter and not the getter, which I think is what you want.
var settings = new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Replace
};
var data = JsonConvert.DeserializeObject<StoredProcedureData>(json, settings);
In your case, I would actually recommend that you apply both fixes. If you don't fix your getters (option 1), you could run into a similar issue elsewhere in your code. For example, you might have something like this:
var data = new StoredProcedureData();
data.Header.SPName = "foo";
if (data.Header.SPName == "foo")
{
Console.WriteLine("foo");
}
else
{
Console.WriteLine("oops");
}
Guess which value will be printed?
And option 2 will protect against possibly unexpected results if you happen to have initialized a collection somewhere to have a default set of values. For example, if you had something like this:
public StoredProcedureData()
{
_funcRef = new ObservableCollection<string>();
_funcRef.Add("Initialize");
}
then when you deserialize, you will get the default values plus the values from the JSON, which is probably not what you want. Setting ObjectCreationHandling to Replace will ensure that you will end up with just the values which were deserialized from the JSON.
I'm not sure why I should use RaisePropertyChanging, when notifying the view fx:
private LoggingLvl _myLoggingLvl;
public LoggingLvl MyLoggingLvl
{
get { return _myLoggingLvl; }
set
{
RaisePropertyChanging("MyLoggingLvl");
_myLoggingLvl = value;
RaisePropertyChanged("MyLoggingLvl");
}
}
why is it recommended to use RaisePropertyChanging?
Using INotifyPropertyChanging would allow consuming code a chance to consume the previous value of a property, before a change is applied. This is not frequently going to be useful, but there are cases where it might be: if you imagine a property that represents an "active object," this event would allow you to trigger code that would fire when the object is de-activated.
As a contrived example, consider a UI where the change in value of a field is required to display in a specific way: the old value should "float" off the screen leaving the new value behind. If a model class implemented INotifyPropertyChanging, a viewmodel class could attach to this event in order to cache the old value for use in the float animation. This allows the model class to represent the current state, while the viewmodel can maintain all values necessary to drive the UI.
I have never listened before that RaisePropertyChanging is recommended. You can use it if you need to notify to "outside world" that specified property is going to changed.
This can be useful, for example, when some parts of your application should validate the changing property state against other parameters of your system, so may be also signal that it's not a subject to change, as following Single Responsibility Principle your class may not be aware of states of other instances of types of your application.
The INotifyPropertyChanging interface is used to notify clients, typically binding clients, that a property value is changing.
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanging.aspx
assume for a long running validation task on your property. hope take 5 seconds for updating the property value. meanwhile some other property are are looking for it these properties are treated under race condition. and wait under a queue and one property changed notification done then the first object will get change to get or set the property value.
let me give you some examples.
I've got a browser sending up JSON but it only includes the properties of a given model that have been changed. So once the WCF DataContractJsonSerializer does it's work I have an object that will have perhaps only the ID and Description fields populated.
Attaching this to the DbContext as is will result in the description field being updated but all the other fields being set to their types default value in the database. This is because if WCF doesn't see the property specified in the JSON then it'll skip over it, meaning the property in the instance will just use the types default value as per the auto-implemented property.
So this means that I need to decide on which fields have been passed up without having access to the JSON itself. In fact it could be XML on the wire so all I can work from is this partly serialized object.
What seems most logical is to use null as the special value that means this property hasn't been serializd over. So in the constructor of the POCO model I set all the properties to null.
In the Update method I then use this serialized object as a stub. I have to walk each property and if the value isn't set to null then I set it's state to modified. As far as I can tell this is working without any side effects but I'm just not sure that this is the way to do something like this.
One limitation it does add is that the client can no longer intentionally set a property to null as that update would be lost. One way around this is to have a special int value that can be set to represent null in the database and perhaps an empty string to represent null in the database and have code in the update to look for these special values and then set the entity property to null. Far from ideal and likely to be prone to bugs.
Here is the code I currently have to process the update. I would really really appreciate advice as to a better, perhaps more obvious way of doing this.
To Summerise: How can I tell which properties on an model instance have been set by the DataContractSerializer/DataContractJsonSerializer and which are just using default values from it's constructor. Using special values is problematic as the client might want to set something to an empty string, or to 0 or -1 or indeed to null.
public T Update(T obj)
{
var entity = ds.Attach(obj);
// For each property in the model
foreach (var p in typeof(T).GetProperties())
{
// Get the value of the property
var v = p.GetValue(obj, null);
// Assume null means that the property wasn't passed from the client
if (v == null)
continue;
// Set this property on the entity to modified unless it's ID which won't change
if (p.Name != "ID")
dc.Entry(entity).Property(p.Name).IsModified = true;
}
dc.SaveChanges();
return entity;
}
UPDATE: Using Hammerstein's answer below to have self tracked models, I've updated my update function as below. Unfortunately due to my use of the Required attribute on the models for pre-save validation EF throws a wobbly when using a stub instance that contains nulls for the non modified values. You would think that EF would realise as part of it's validation that some fields are set to not modified but alas it doesn't so I'm forced to do a read and then update that. Actually this might be a good candidate for a separate question to post to try and avoid the read.
public virtual T Update(T obj)
{
var entity = ds.Find(obj.ID);
((TrackedModel)obj).Modified.ForEach(p => {
var prop = dc.Entry(entity).Property(p.PropertyName);
prop.CurrentValue = p.NewValue;
prop.IsModified = true;
});
dc.SaveChanges();
return entity;
}
My solution to this problem was a tracked change model, I created an abstract base class that had a list of strings, then for each property on my model, I called a method NotifyChanged( "MyProperty") which added the property to the list.
Because model binding will only set the fields that have been posted back, you should get an accurate list of fields that changed.
Then I loop through the list, and set the values on my entity.
Not clean, but it worked for my purpose.
Update: My solution did require me to get away from auto-properties and to hand write them. In the setter, after setting the value, I call NotifyChanged. I'm using this with MVC regular model binding, I don't think I have a working example of passing an object as JSON and deserializing. You could look at JSON.NET, controlling the serialization/deserialization I believe you can tell it to ignore default property values etc.
I've got a WPF app that's using INotifyPropertyChanged to signal property updates. What I like about it is that it gives you the ability to signal a property change without invoking all of the setter code. This is handy when you've got two linked fields that update each other or when you've got code that saves changes on every setter and you don't want to trigger the save multiple times for what is essentially one change by the user.
However the obvious weakness of this approach is that a mistake in the property name or on which viewmodel you send the notification to will mess you up and only show up and runtime.
I looked into dependency properties but was unable to find anything that lets you do a "soft update": let the UI know that the property has changed but avoid calling all the code that would normally run when a UI causes a change in the viewmodel.
Is there some way I can get a property system that allows soft updates and catches notification problems at compile-time?
If the underlying problem is mistakes in the property name, there are compile-time solutions, like making the "property name" parameter an expression tree instead of a string.
http://michaelsync.net/2009/04/09/silverlightwpf-implementing-propertychanged-with-expression-tree
The Caliburn.Micro library also includes this approach in its PropertyChangedBase class.
I like Foson's solution for "early binding" vis a vis the property names and eliminating the need to keep a string copy of your property name around. This is the core of the implementation I, personally, use:
public virtual string GetName<T>(Expression<Func<T>> expression)
{
return GetMemberName(expression);
}
/// <summary>Abstraction for actually finding the name of the target of the expression</summary>
private static string GetMemberName<T>(Expression<Func<T>> expression)
{
if (expression != null)
{
var myMemberExpression = expression.Body as MemberExpression;
if (myMemberExpression != null && myMemberExpression.Member != null)
{
return myMemberExpression.Member.Name;
}
}
return string.Empty;
}
This code resides in a class called NameResolver, which my ViewModelBase class wraps with
NotifyChange(Expression<Func<T>> expression)
Then client code looks something like:
private int _myBindable;
public int MyBindable { get { return _myBindable; } set { _myBindable = value; NotifyChange(() => MyBindable); }
As to the notion of separating GUI notification from updating underlying stuff, you can invoke those NotifyChange() methods elsewhere than the property setter when whatever happens in your code that should trigger UI update happens. So, GUI sets your property, which triggers some logic, but you don't raise change notification there -- you raise it from wherever, specifically, you want to inform the UI about something.
I have a property of type IEnumerable<SomeClassIWrote> in a user control. When I use this control in a GUI, the .Designer.cs file contains the line:
theObject.TheProperty = new SomeClassIWrote[0];
Which for some reason causes a compiler warning:
Object of type 'SomeClassIWrote[]' cannot be converted to type
'System.Collections.Generic.IEnumerable`1[SomeClassIWrote]'.
Which is a mystery to me because I pass arrays as IEnumerables all the time, and the compiler has never complained.
For what it's worth, I have a default value of null specified for the property, but I got the same error before I set a default value.
How can I fix this so Visual Studio doesn't complain and ask me to ignore and continue every time I pull up the designer?
Code for the property:
[DefaultValue(null)]
public IEnumerable<SomeClassIWrote> TheProperty {
get {
return _theProperty;
}
set {
if (value == null) {
_theProperty = new SomeClassIWrote[] { };
}
else {
_theProperty = value;
}
}
}
First up, do you WANT to be able to set it in the designer?
If not, add the following attributes:
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
If you DO want to set in the designer, I'd start out by trying your class as a SomeClassIWrote[], to see if that works.
That aside, is it that important to use an IEnumerable here? As you say, you can pass arrays as IEnumerables.
I suspect there's probably some restrictions inside the designer, which wiser people than me know about...
And if you really DO want an IEnumerable property, you can expose your array as an IEnumerable, but keep your array as a designer-friendly backing field.