I'm refactoring some objects that are serialized to XML but need to keep a few properties for backwards compatibility, I've got a method that converts the old object into the new one for me and nulls the obsolete property. I want to use the Obsolete attribute to tell other developers not to use this property but it is causing the property to be ignored by the XmlSerializer.
Similar Code:
[Serializable]
public class MySerializableObject
{
private MyObject _oldObject;
private MyObject _anotherOldObject;
private MyObject _newBetterObject;
[Obsolete("Use new properties in NewBetterObject to prevent duplication")]
public MyObject OldObject
{
get { return _oldObject; }
set { _oldObject = value; }
}
[Obsolete("Use new properties in NewBetterObject to prevent duplication")]
public MyObject AnotherOldObject
{
get { return _anotherOldObject; }
set { _anotherOldObject = value; }
}
public MyObject NewBetterObject
{
get { return _anotherOldObject; }
set { _anotherOldObject = value; }
}
}
Any ideas on a workaround? My best solution is to write obsolete in the XML comments...
Update: I'm using .NET 2.0
EDIT: After reading a MS Connect article, it appears that .Net 2.0 has a 'feature' where it makes ObsoleteAttribute equivalent to XmlIgnoreAttribute without any notification in the documentation. So I'm going to revise my answer to say that the only way to have your cake and eat it too in this instance is to follow #Will's advice and implement serialization manually. This will be your only future proof way of including Obsolete properties in your XML. It is not pretty in .Net 2.0, but .Net 3.0+ can make life easier.
From XmlSerializer:
Objects marked with the Obsolete Attribute no longer serialized
In the .NET Framework 3.5 the XmlSerializer class no longer serializes objects that are marked as [Obsolete].
Another workaround is to subscribe to XmlSerializer.UnknownElement, when creating the serializer for the datatype, and then fix old data that way.
http://weblogs.asp.net/psteele/archive/2011/01/31/xml-serialization-and-the-obsolete-attribute.aspx
Maybe consider to have the method for subscribing as a static method on the class for datatype.
static void serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
if( e.Element.Name != "Hobbies")
{
return;
}
var target = (MyData) e.ObjectBeingDeserialized;
foreach(XmlElement hobby in e.Element.ChildNodes)
{
target.Hobbies.Add(hobby.InnerText);
target.HobbyData.Add(new Hobby{Name = hobby.InnerText});
}
}
I have struggled with this a lot - there is no solution other than doing serialization manually or using another serializer.
However, instead of writing shims for each obsolete property which quickly becomes a pain, you could consider adding an Obsolete prefix to property names (e.g. Foo becomes ObsoleteFoo. This will not generate a compiler warning like the attribute will, but at least it's visible in code.
1) WAG: Try adding the XmlAttributeAttribute to the property; perhaps this will override the ObsoleteAttribute
2) PITA: Implement IXmlSerializable
Yes I agree with marking things with the name "Obsolete" we do this with Enum values
/// <summary>
/// Determines the swap file location for a cluster.
/// </summary>
/// <remarks>This enum contains the original text based values for backwards compatibility with versions previous to "8.1".</remarks>
public enum VMwareClusterSwapFileLocation
{
/// <summary>
/// The swap file location is unknown.
/// </summary>
Unknown = 0,
/// <summary>
/// The swap file is stored in the virtual machine directory.
/// </summary>
VmDirectory = 1,
/// <summary>
/// The swap file is stored in the datastore specified by the host.
/// </summary>
HostLocal = 2,
/// <summary>
/// The swap file is stored in the virtual machine directory. This value is obsolete and used for backwards compatibility.
/// </summary>
[XmlElement("vmDirectory")]
ObseleteVmDirectory = 3,
/// <summary>
/// The swap file is stored in the datastore specified by the host. This value is obsolete and used for backwards compatibility.
/// </summary>
[XmlElement("hostLocal")]
ObseleteHostLocal = 4,
}
You may try the following workaround:
add a method named
ShouldSerializeOldObject ()
{
return true;
}
ShouldSerializeAnotherOldObject ()
{
return true
}
this may override the obsolete Attribute
Related
Okay, so I am creating a Utility AI framework. For this to work I need a class that can change a lot depending on the situation I am sure, and I hope that there is a way to use polymorphism or some sort of design pattern to solve my issue.
Let me show you what I mean
I have an action for the sake of example let's say I have the following action Attack Target
This action can have a number of considerations that will vary a lot but all implement the same interface:
public interface IConsideration
{
/// <summary>
/// A unique named identifier for this consideration.
/// </summary>
string NameId { get; }
/// <summary>
/// The weight of this consideration.
/// </summary>
float Weight { get; set; }
/// <summary>
/// If true, then the output of the associated evaluator is inverted, in effect, inverting the
/// consideration.
/// </summary>
bool IsInverted { get; set; }
/// <summary>Calculates the utility given the specified context.</summary>
/// <param name="context">The context.</param>
/// <param name="value"></param>
/// <returns>The utility.</returns>
float Consider<T>(BaseAiContext context, T value);
}
The above is my current implementation it doesn't really solve the issue I have
As you can see the "most" important method of this interface is the Consider
and here lies the issue preferably I should be able to pass data to this class in a way that I can control.
For the sake of example let's say one consideration I have is Move To Location here I want to send the following parameters:
Location of target
Weapon type (ranged / melee)
location list
The above is just an example to prove my point. There is another issue with this - how can I pass the correct parameters when I finally have them? say that I have the following code:
public List<IConsideration> considerations;
float targetDistance = 2;
for (int i = 0; i < considerations.Count; i++)
{
float AxisScore = considerations[i].Consider(BaseAiContext,targetDistance );
}
Since I have to use the Interface type I am unable to know exactly which values to parse as parameters.
To sum it up:
How can i "parameterize" my class in a generic way?
How can I distinguish these parameterizations so I can provide a consideration with the correct values?
As #MarcRasmussen requested an example
As each implementation of your interface might consume different sets of arguments one way to solve it would be to kind of have key-value storage like a dictionary.
There are plenty of improvements to be made like, using ENUMS instead of strings, and having a static manager class for things like that to add/modify/remove settings.
This is a quick example and not tested, with the information available.
public class MoveToTarget : IConsideration
{
//method is changed to have generic return type and accept dictionary for settings
float Consider(BaseAiContext context, Dictionary<string,object> settings){
//make sure required keys exist
if(!settings.ContainsKey("DESTINATION"))
throw new ApplicationException("Missing key DESTINATION");
if(!settings.ContainsKey("SPEED"))
throw new ApplicationException("Missing key SPEED");
// retrieve you required settings, at this stage since you cast an object, you should check the type ... this problem would be solved if you have (as further below mentioned) a specific settings class for all your implementations. this way you ensure type safety too.
Point destination = (Point)settings["DESTINATION"];
float speed = (float)settings["SPEED"];
// and perform whatever logic you need. etc.
}
}
public List<IConsideration> considerations;
//this should be probably static and globally available (?) probably better to have a singleton manager class to deal with that.
Dictionary<string,object> Settings = new Dictionary<string,object>()
{
{"SPEED", 1.0f},
{"RANDOM", new Random()},
{"DESTINATION", new Point()},
{"XYZ", "XYZ"},
//etc.
}
//use foreach unless you need to have access to the index
foreach(var consideration in considerations)
{
float AxisScore = consideration.Consider(BaseAiContext, Settings);
}
some other improvements could be to have a specific settings class for each of your implementations like MoveToTargetSettings and then instead of havin ambiguous "KEYS" in a dictionary you can retrieve the specific settings by its class etc. like var settings = settingDictionary["MoveToTargetSettins"] as MoveToTargetSettings
I think for anything better more details are required, happy to discuss and answer any further questions outside of SO as that will be off-topic :)
Sorry if I've worded the question a bit odd! Basically, I have a serializable class that has a single field at this current time, but will definitely gain more in the future as we add features to the system. The serialization process will be used for both passing instances to a WCF service, and for reading/writing it from/to file. Of course, the latter may be a big problem if I'm continually updating the class with extra fields.
Luckily, I think I've solved the problem by adding a try/catch block around the field setter in the constructor, which I'll also do for any other fields that are added to the class.
/// <summary>
/// Represents launch options supplied to an executable.
/// </summary>
[Serializable]
public sealed class ExecutableLaunchOptions : ISerializable
{
/// <summary>
/// Creates a new set of executable launch options.
/// </summary>
public ExecutableLaunchOptions() { }
/// <summary>
/// Creates a new set of executable launch options via deserialization.
/// </summary>
/// <param name="info">the serialization information.</param>
/// <param name="context">the streaming context.</param>
public ExecutableLaunchOptions(
SerializationInfo info, StreamingContext context) : this()
{
// Get the value of the window style from the serialization information.
try { this.windowStyle = (ProcessWindowStyle)info.GetValue(nameof(this.windowStyle), typeof(ProcessWindowStyle)); } catch { }
}
// Instance variables.
private ProcessWindowStyle windowStyle = ProcessWindowStyle.Normal;
/// <summary>
/// Gets or sets the window style to apply to the executable when it launches.
/// </summary>
public ProcessWindowStyle WindowStyle
{
get { return this.windowStyle; }
set { this.windowStyle = value; }
}
/// <summary>
/// Gets the information required for the serialization of this set of launch options.
/// </summary>
/// <param name="info">the serialization information.</param>
/// <param name="context">the streaming context.</param>
public void GetObjectData(
SerializationInfo info, StreamingContext context)
{
// Add the value of the window style to the serialization information.
info.AddValue(nameof(this.windowStyle), this.windowStyle, typeof(ProcessWindowStyle));
}
}
I'm guessing this will allow me to retain backwards-compatibility with files containing instances of previous versions of the class when I deserialize them, as the code will simply throw and subsequently catch exceptions for each field that doesn't exist in the serialization information, leaving their values at their default. Am I correct here, or am I missing something?
Serializing objects with the FormatterAssemblyStyle.Simple formatter will allow you to read old versions of your serialized objects. Marking new fields with [OptionalField] will allow old versions of your app to open new serialization files without throwing. So should you use them? No. Nooo no noooo no no no.
Serialization was designed for exchanging data between processes, it was not, and is not, a data persistence mechanism. The format has changed in the past, and may change in the future, so it is unsafe to use it for persistent data you expect to open again in the future with a new .NET version.
The BinaryFormatter algorithm is proprietary, so it will be very difficult to write non-.NET applications using such data.
Serialization is inefficient for data that contains multiple properties because it requires deserializing the entire object to access any field. This is especially problematic if the data contains large data like images.
If your data does not require random access, I suggest serializing it to a text format like JSON or XML. If the data is large, you should consider compressing the text-encoded data.
If you require random-access to data you should investigate data stores like MySQL or SQL Server CE.
You can use SerializationInfo.GetEnumerator() to loop through the name-value pairs contained in the SerializationInfo to look for items that might only be conditionally present:
public ExecutableLaunchOptions(
SerializationInfo info, StreamingContext context) : this()
{
// Get the value of the window style from the serialization information.
var enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (current.Name == "windowStyle" && current.ObjectType == typeof(ProcessWindowStyle))
{
this.windowStyle = (ProcessWindowStyle)current.Value;
}
}
}
Note that this class is so old (i.e. from c# 1.0) that, despite having a GetEnumerator() method, it doesn't actually implement IEnumerable.
If you need to do this often, you can introduce an extension method like so:
public static class SerializationInfoExtensions
{
public static IEnumerable<SerializationEntry> AsEnumerable(this SerializationInfo info)
{
if (info == null)
throw new NullReferenceException();
var enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
}
And then do:
public ExecutableLaunchOptions(
SerializationInfo info, StreamingContext context) : this()
{
foreach (var current in info.AsEnumerable())
{
if (current.Name == "windowStyle" && current.ObjectType == typeof(ProcessWindowStyle))
{
this.windowStyle = (ProcessWindowStyle)current.Value;
}
}
}
Which is a bit more modern and readable.
I have a shared object between threads that is used to hold file state information. The object that holds the information is this class:
/// <summary>
/// A synchronized dictionary class.
/// Uses ReaderWriterLockSlim to handle locking. The dictionary does not allow recursion by enumeration. It is purly used for quick read access.
/// </summary>
/// <typeparam name="T">Type that is going to be kept.</typeparam>
public sealed class SynchronizedDictionary<U,T> : IEnumerable<T>
{
private System.Threading.ReaderWriterLockSlim _lock = new System.Threading.ReaderWriterLockSlim();
private Dictionary<U, T> _collection = null;
public SynchronizedDictionary()
{
_collection = new Dictionary<U, T>();
}
/// <summary>
/// if getting:
/// Enters read lock.
/// Tries to get the value.
///
/// if setting:
/// Enters write lock.
/// Tries to set value.
/// </summary>
/// <param name="key">The key to fetch the value with.</param>
/// <returns>Object of T</returns>
public T this[U key]
{
get
{
_lock.EnterReadLock();
try
{
return _collection[key];
}
finally
{
_lock.ExitReadLock();
}
}
set
{
Add(key, value);
}
}
/// <summary>
/// Enters write lock.
/// Removes key from collection
/// </summary>
/// <param name="key">Key to remove.</param>
public void Remove(U key)
{
_lock.EnterWriteLock();
try
{
_collection.Remove(key);
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// Enters write lock.
/// Adds value to the collection if key does not exists.
/// </summary>
/// <param name="key">Key to add.</param>
/// <param name="value">Value to add.</param>
private void Add(U key, T value)
{
_lock.EnterWriteLock();
if (!_collection.ContainsKey(key))
{
try
{
_collection[key] = value;
}
finally
{
_lock.ExitWriteLock();
}
}
}
/// <summary>
/// Collection does not support iteration.
/// </summary>
/// <returns>Throw NotSupportedException</returns>
public IEnumerator<T> GetEnumerator()
{
throw new NotSupportedException();
}
/// <summary>
/// Collection does not support iteration.
/// </summary>
/// <returns>Throw NotSupportedException</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotSupportedException();
}
}
I call this dictionary like this:
SynchronizedDictionary _cache = new SynchronizedDictionary();
Other threads can be spawned and use the thread like this:
_cache["key"];
The dictionary can be modified at runtime. I see no problem here. Or am I wrong?
The problem, in my eyes, lies in the enumerator, because I want to make an enumerator that iterates over the collection. How do I do this? I have thought of these three solutions:
Making a Enumerator like this:
http://www.codeproject.com/Articles/56575/Thread-safe-enumeration-in-C
(but using ReaderWriterLockSlim)
Expose the lock object, like SyncRoot does (but with
ReaderWriterLockSlim), so a caller calls the enter and exit read methods.
Use a database (SQLite fx) instead, holding the information.
The problem with number 1) is:
it uses the contructor to entry read mode. What if the
GetEnumerator() is call manually, not using the foreach? And forget
calling dispose.
I do not know if this is a good coding style. Even though I like the
code.
If the caller uses a foreach, I do not know what the caller might do
between the instantiation of the enumerator and the call to dispose.
If I have understood the documentation I have read correct this can
end up blocking the writer as long as there is one reader left doing
some heavy work.
The problem with number 2) is:
I do not like exposing this. I know that the .NET API does it, but
do not like it.
It is up to the caller to enter and exit properly
There is no problem with 3) I my eyes. But I am doing this small project as a spare time project and I want to learn more about multi-threading and reflection, so I want to keep this as a last option.
The reason why I want to iterate over the collection at runtime is that I want to find the values, that matches some criteria.
Maybe it is just me that have invented a problem?
I know of ConcurrentDictionary, but I do not want to use this. I am using this project as a playground. Playing with threading and reflection.
EDIT
I have been asked what it is that I am reading and writing. And I am going to tell this in this edit. I am reading and writing this class:
public class AssemblyInformation
{
public string FilePath { get; private set; }
public string Name { get; private set; }
public AssemblyInformation(string filePath, string name)
{
FilePath = filePath;
Name = name;
}
}
I am doing alot of reads, and almost no writes at runtime. Maybe I will do 2000 and 1 write. There is not going to be alot of object either, maybe 200.
I'll treat your questions as a request for feedback which helps you learn. Let me address the three solutions you have already identified:
Yes, this is why such a design should never be exposed as an API to a 3rd-party (or even other developers). It is tricky to use correctly. This codeproject article has some nasty advice.
Much better because this model would be explicit about locking, not implicit. However this violates separation of concerns in my opinion.
Not sure what you mean here. You could have a Snapshot() method on your dictionary which does a read-only copy which can be safely passed around and read. This is a different trade-off than solution 1.
There is a different solution entirely: Use an immutable dictionary. Such a dictionary could be passed around, read and enumerated safely even under concurrent write access. Such dictionaries/maps are commonly implemented using trees.
I'll elaborate more on a key point: You need to think about the concurrent system as a whole. You cannot make you app correct by making all components thread-safe (in your case a dictionary). You need to define, what you are using the dictionary for.
You say:
The reason why I want to iterate over the collection at runtime is
that I want to find the values, that matches some criteria.
You you have concurrent writes happening to the data and want to get a consistent snapshot atomically from the dictionary (maybe to shot some progress report in the UI?). Now that we know this goal, we can devise a solution:
You could add a Clone method to your dictionary which clones all data while taking the read-lock. This will give the caller a fresh object which it can then enumerate over independently. This would be a clean and safely exposable API.
Instead of implementing IEnumerable directly I would add a Values property (like Dictionary.Values):
public IEnumerable<T> Values {
get {
_lock.EnterReadLock();
try {
foreach (T v in _collection.Values) {
yield return v;
}
} finally {
_lock.ExitReadLock();
}
}
}
In C#, I have a class which has a derived property that should be serialized via XML. However, XML serialization (by default) doesn't serialize read=only properties. I can work around this by defining an empty setter like so:
public virtual string IdString
{
get { return Id.ToString("000000"); }
set { /* required for xml serialization */ }
}
But is there a cleaner more semantically correct way, short of writing my own ISerializable implementation?
Honestly this doesn't seem too bad to me as long as it is documented
You should probably throw an exception if the setter is actually called:
/// <summary>
/// Blah blah blah.
/// </summary>
/// <exception cref="NotSupportedException">Any use of the setter for this property.</exception>
/// <remarks>
/// This property is read only and should not be set.
/// The setter is provided for XML serialisation.
/// </remarks>
public virtual string IdString
{
get
{
return Id.ToString("000000");
}
set
{
throw new NotSupportedException("Setting the IdString property is not supported");
}
}
In short, no. With XmlSerializer you can either implement IXmlSerializable (which is non-trivial), or write a basic DTO (that is fully read-write) and then translate from the DTO model to your main model.
Note that in some cases DataContractSerializer is a viable option, but it doesn't offer the same control over the XML. However, with DCS you can do:
[DataMember]
public int Id { get; private set; }
With C# 8, obsoleting set is allowed, so you can do this:
public virtual string IdString
{
get { return Id.ToString("000000"); }
[Obsolete("Only used for xml serialization", error: true)]
set { throw new NotSupportedException(); }
}
This will error if anyone uses the setter accidentally.
To take the solution a little further to allow deserialization to work as well...
public class A
{
private int _id = -1;
public int Id
{
get { return _id; }
set
{
if (_id < 0)
throw new InvalidOperationException("...");
if (value < 0)
throw new ArgumentException("...");
_id = value;
}
}
}
This will allow Id to be set exactly one time to a value greater than or equal to 0. Any attempts to set it after will result in InvalidOperationException. This means that XmlSerializer will be able to set Id during deserialization, but it will never be able to be changed after. Note that if the property is a reference type then you can just check for null.
This may not be the best solution if you have a lot of read-only properties to serialize/deserialize as it would require a lot of boilerplate code. However, I've found this to be acceptable for classes with 1-2 read-only properties.
Still a hack, but this is at least a little more robust.
I've got a WCF DataContract that looks like the following:
namespace MyCompanyName.Services.Wcf
{
[DataContract(Namespace = "http://mycompanyname/services/wcf")]
[Serializable]
public class DataContractBase
{
[DataMember]
public DateTime EditDate { get; set; }
// code omitted for brevity...
}
}
When I add a reference to this service in Visual Studio, this proxy code is generated:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.3082")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://mycompanyname/services/wcf")]
public partial class DataContractBase : object, System.ComponentModel.INotifyPropertyChanged {
private System.DateTime editDateField;
private bool editDateFieldSpecified;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public System.DateTime EditDate {
get {
return this.editDateField;
}
set {
this.editDateField = value;
this.RaisePropertyChanged("EditDate");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool EditDateSpecified {
get {
return this.editDateFieldSpecified;
}
set {
this.editDateFieldSpecified = value;
this.RaisePropertyChanged("EditDateSpecified");
}
}
// code omitted for brevity...
}
As you can see, besides generating a backing property for EditDate, an additional <propertyname>Specified property is generated. All good, except that when I do the following:
DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;
new MyServiceClient.Update(new UpdateRequest(myDataContract));
the EditDate was not getting picked up by the endpoint of the service (does not appear in the transmitted XML).
I debugged the code and found that, although I was setting EditDate, the EditDateSpecified property wasn't being set to true as I would expect; hence, the XML serializer was ignoring the value of EditDate, even though it's set to a valid value.
As a quick hack I modified the EditDate property to look like the following:
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order=0)]
public System.DateTime EditDate {
get {
return this.editDateField;
}
set {
this.editDateField = value;
// hackhackhack
if (value != default(System.DateTime))
{
this.EditDateSpecified = true;
}
// end hackhackhack
this.RaisePropertyChanged("EditDate");
}
}
Now the code works as expected, but of course every time I re-generate the proxy, my modifications are lost. I could change the calling code to the following:
DataContractBase myDataContract = new DataContractBase();
myDataContract.EditDate = DateTime.Now;
myDataContract.EditDateSpecified = true;
new MyServiceClient.Update(new UpdateRequest(myDataContract));
but that also seems like a hack-ish waste of time.
So finally, my question: does anyone have a suggestion on how to get past this unintuitive (and IMO broken) behavior of the Visual Studio service proxy generator, or am I simply missing something?
It might be a bit unintuitive (and caught me off guard and reeling, too!) - but it's the only proper way to handle elements that might or might not be specified in your XML schema.
And it also might seem counter-intuitive that you have to set the xyzSpecified flag yourself - but ultimately, this gives you more control, and WCF is all about the Four Tenets of SOA of being very explicit and clear about your intentions.
So basically - that's the way it is, get used to it :-) There's no way "past" this behavior - it's the way the WCF system was designed, and for good reason, too.
What you always can do is catch and handle the this.RaisePropertyChanged("EditDate"); event and set the EditDateSpecified flag in an event handler for that event.
try this
[DataMember(IsRequired=true)]
public DateTime EditDate { get; set; }
This should omit the EditDateSpecified property since the field is specified as required
Rather than change the setters of the autogenerated code, you can use an extension class to 'autospecify' (bind the change handler event). This could have two implementations -- a "lazy" one (Autospecify) using reflection to look for fieldSpecified based on the property name, rather than listing them all out for each class in some sort of switch statement like Autonotify:
Lazy
public static class PropertySpecifiedExtensions
{
private const string SPECIFIED_SUFFIX = "Specified";
/// <summary>
/// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically set any xxxSpecified fields when a property is changed. "Lazy" via reflection.
/// </summary>
/// <param name="entity">the entity to bind the autospecify event to</param>
/// <param name="specifiedSuffix">optionally specify a suffix for the Specified property to set as true on changes</param>
/// <param name="specifiedPrefix">optionally specify a prefix for the Specified property to set as true on changes</param>
public static void Autospecify(this INotifyPropertyChanged entity, string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null)
{
entity.PropertyChanged += (me, e) =>
{
foreach (var pi in me.GetType().GetProperties().Where(o => o.Name == specifiedPrefix + e.PropertyName + specifiedSuffix))
{
pi.SetValue(me, true, BindingFlags.SetField | BindingFlags.SetProperty, null, null, null);
}
};
}
/// <summary>
/// Create a new entity and <see cref="Autospecify"/> its properties when changed
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="specifiedSuffix"></param>
/// <param name="specifiedPrefix"></param>
/// <returns></returns>
public static T Create<T>(string specifiedSuffix = SPECIFIED_SUFFIX, string specifiedPrefix = null) where T : INotifyPropertyChanged, new()
{
var ret = new T();
ret.Autospecify(specifiedSuffix, specifiedPrefix);
return ret;
}
}
This simplifies writing convenience factory methods like:
public partial class MyRandomClass
{
/// <summary>
/// Create a new empty instance and <see cref="PropertySpecifiedExtensions.Autospecify"/> its properties when changed
/// </summary>
/// <returns></returns>
public static MyRandomClass Create()
{
return PropertySpecifiedExtensions.Create<MyRandomClass>();
}
}
A downside (other than reflection, meh) is that you have to use the factory method to instantiate your classes or use .Autospecify before (?) you make any changes to properties with specifiers.
No Reflection
If you don't like reflection, you could define another extension class + interface:
public static class PropertySpecifiedExtensions2
{
/// <summary>
/// Bind the <see cref="INotifyPropertyChanged.PropertyChanged"/> handler to automatically call each class's <see cref="IAutoNotifyPropertyChanged.Autonotify"/> method on the property name.
/// </summary>
/// <param name="entity">the entity to bind the autospecify event to</param>
public static void Autonotify(this IAutoNotifyPropertyChanged entity)
{
entity.PropertyChanged += (me, e) => ((IAutoNotifyPropertyChanged)me).WhenPropertyChanges(e.PropertyName);
}
/// <summary>
/// Create a new entity and <see cref="Autonotify"/> it's properties when changed
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T Create<T>() where T : IAutoNotifyPropertyChanged, new()
{
var ret = new T();
ret.Autonotify();
return ret;
}
}
/// <summary>
/// Used by <see cref="PropertySpecifiedExtensions.Autonotify"/> to standardize implementation behavior
/// </summary>
public interface IAutoNotifyPropertyChanged : INotifyPropertyChanged
{
void WhenPropertyChanges(string propertyName);
}
And then each class themselves defines the behavior:
public partial class MyRandomClass: IAutoNotifyPropertyChanged
{
public void WhenPropertyChanges(string propertyName)
{
switch (propertyName)
{
case "field1": this.field1Specified = true; return;
// etc
}
}
}
The downside to this is, of course, magic strings for property names making refactoring difficult, which you could get around with Expression parsing?
Further information
On the MSDN here
In her answer, Shreesha explains that:
"Specified" fields are only generated on optional parameters that are structs. (int, datetime, decimal etc). All such variables will have additional variable generated with the name Specified.
This is a way of knowing if a parameter is really passed between the client and the server.
To elaborate, an optional integer, if not passed, would still have the dafault value of 0. How do you differentiate between this and the one that was actually passed with a value 0 ? The "specified" field lets you know if the optional integer is passed or not. If the "specified" field is false, the value is not passed across. If it true, the integer is passed.
so essentially, the only way to have these fields set is to set them manually, and if they aren't set to true for a field that has been set, then that field will be missed out in the SOAP message of the web-service call.
What I did in the end was build a method to loop through all the members of the object, and if the property has been set, and if there is a property called name _Specified then set that to true.
Ian,
Please ignore my previous answers, was explaining how to suck eggs. I've voted to delete them.
Could you tell me which version of Visual Studio you're using, please?
In VS2005 client - in the generated code, I get the <property>Specified flags, but no event raised on change of values. To pass data I have to set the <property>Specified flag.
In Visual Web Developer 2008 Express client - in the generated code, I get no <property>Specified flags, but I do get the event on change of value.
Seems to me that this functionality has evolved and the Web Dev 2008 is closer to what you're after and is more intuitive, in that you don't need to set flags once you've set a value.
Bowthy
Here's a simple project that can modify the setters in generated WCF code for optional properties to automatically set the *Specified flags to true when setting the related value.
https://github.com/b9chris/WcfClean
Obviously there are situations where you want manual control over the *Specified flag so I'm not recommending it to everyone, but in most simple use cases the *Specified flags are just an extra nuisance and automatically setting them saves time, and is often more intuitive.
Note that Mustafa Magdy's comment on another answer here will solve this for you IF you control the Web Service publication point. However, I usually don't control the Web Service publication and am just consuming one, and have to cope with the *Specified flags in some simple software where I'd like this automated. Thus this tool.
Change proxy class properties to nullable type
ex :
bool? confirmed
DateTime? checkDate