C# NET Looping over object properties stored in List - c#

At the moment I'm working on funcionality that involves exporting and importing data to Xlsx file. Here's what I want to do: I want to have an attribute I can put above a property like this.
public class MyClass
{
[XlsxColumn("Column 1")]
public string myProperty1 { get; set; }
public int myProperty2 { get; set; }
}
So far I don't have problems, but then I want to "store references" to properties marked with the XlsxColumn attribute. I'm using reflection
to store properties data in List
var propsList = MyClass.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(XlsxColumn)));
I have a list with all properties marked with XlsxColumn (only myProperty1 in this example).
EDIT: The problem is I don't know how to loop over properties in MyClass, but only properties with XlsxColumn attribute (so all PropertyInfo objects stored in propsList variable), without resorting to reflection with each object saved to Xlsx file.
I'm restricted to .NET 4.0.
Thanks for your time.

MyClass.GetProperties() does not work because you have to get the type of the class to invoke the GetProperties method. Otherwise you are invoking a static method called GetProperties defined in the MyClass class.
var propsList = typeof(MyClass).GetProperties().Where(
prop => prop.IsDefined(typeof(XlsxColumnAttribute), false)).ToList();
If you just want the names (IList<string>):
var propsList = typeof(Excel).GetProperties().Where(
prop => prop.IsDefined(typeof(XlsxColumnAttribute), false))
.Select(prop=> prop.Name)
.ToList();
to use .Where you have to include System.Linq

I must say that I am not sure if this is the solution you are looking for. Because I could not quite make out what your question is. Well I have tried to provide an answer as much as I could figure out.
I went for a static class for CachingPropetyProvider but you can go for an instance class and use a dependency injection library and use it as a Singleton too. Moreover, I have written extensive comments so It is as self explanatory as possible.
Let us define MyClass. I also deliberately changed it a little bit.
public class MyClass
{
[XlsxColumn("Column 1")]
public string MyProperty1 { get; set; }
[XlsxColumn("Column 2")]
public int MyProperty2 { get; set; }
}
I also defined a MetaInfo class to hold the cached information.
public class MetaInfo {
/// <summary>
/// Immutable class for holding PropertyInfo and XlsxColumn info.
/// </summary>
/// <param name="info">PropertyInfo</param>
/// <param name="attr">XlsxColumn</param>
public MetaInfo(PropertyInfo info, XlsxColumn attr) {
PropertyInfo = info;
Attribute = attr;
}
/// <summary>
/// PropertyInfo. You may want to access the value inside the property.
/// </summary>
public PropertyInfo PropertyInfo { get; }
/// <summary>
/// Attribute. You may want to access information hold inside the attribute.
/// </summary>
public XlsxColumn Attribute { get; }
}
And lastly the main guy. This guy is responsible for providing all the data about classes
public class CachingPropProvider {
/// <summary>
/// Holds the meta information for each type.
/// </summary>
private static readonly ConcurrentDictionary<Type, List<MetaInfo>> TypeCache;
/// <summary>
/// Static constructor is guaranteed to run only once.
/// </summary>
static CachingPropProvider() {
//Initialize the cache.
TypeCache = new ConcurrentDictionary<Type, List<MetaInfo>>();
}
/// <summary>
/// Gets the MetaInfo for the given type. Since We use ConcurrentDictionary it is thread safe.
/// </summary>
/// <typeparam name="T">Type parameter</typeparam>
public static IEnumerable<MetaInfo> GetCachedStuff<T>() {
//If Type exists in the TypeCache, return the cached value
return TypeCache.GetOrAdd(typeof(T),Factory);
}
/// <summary>
/// Factory method to use to extract MetaInfo when Cache is not hit.
/// </summary>
/// <param name="type">Type to extract info from</param>
/// <returns>A list of MetaInfo. An empty List, if no property has XlsxColumn attrbiute</returns>
private static List<MetaInfo> Factory(Type #type) {
//If Type does not exist in the TypeCahce runs Extractor
//Method to extract metainfo for the given type
return #type.GetProperties().Aggregate(new List<MetaInfo>(), Extractor);
}
/// <summary>
/// Extracts MetaInfo from the given property info then saves it into the list.
/// </summary>
/// <param name="seedList">List to save metainfo into</param>
/// <param name="propertyInfo">PropertyInfo to try to extract info from</param>
/// <returns>List of MetaInfo</returns>
private static List<MetaInfo> Extractor(List<MetaInfo> seedList,PropertyInfo propertyInfo) {
//Gets Attribute
var customattribute = propertyInfo.GetCustomAttribute<XlsxColumn>();
//If custom attribute is not null, it means it is defined
if (customattribute != null)
{
//Extract then add it into seed list
seedList.Add(new MetaInfo(propertyInfo, customattribute));
}
//Return :)
return seedList;
}
}
Finally let us see how to use the solution. It is pretty straightforward actually.
//Has 2 values inside
var info = CachingPropProvider.GetCachedStuff<MyClass>();

Related

Automapper is not transferring my collection items

I am using AutoMapper 4.x.
I have a couple of classes as follows:
/// <summary>
/// All service outputs need to descend from this class.
/// </summary>
public class OzCpAppServiceOutputBase : IOzCpAppServiceOutputBase
{
private readonly OzCpResultErrors _OzCpResultErrors;
public OzCpAppServiceOutputBase()
{
_OzCpResultErrors = new OzCpResultErrors();
}
public OzCpResultErrors ResultErrors
{
get { return _OzCpResultErrors; }
}
public bool ResultSuccess
{
get { return _OzCpResultErrors.Messages.Count == 0; }
}
}
/// <summary>
/// Return from the booking service when a simple booking is made.
/// </summary>
public class OzCpSimpleManualCruiseBookingOutput : OzCpAppServiceOutputBase
{
public int OzBookingId { get; set; }
}
}
public class SimpleManualCruiseBookingOutput : OzCpSimpleManualCruiseBookingOutput
{
}
My issue comes in when I call AutoMapper to translate between OzCpSimpleManualCruiseBookingOutput and SimpleManualCruiseBookingOutput is that the ResultErrors is cleared.
public SimpleManualCruiseBookingOutput SimpleManualCruiseBooking(SimpleManualCruiseBookingInput aParams)
{
OzCpSimpleManualCruiseBookingOutput result = _PlatformBookingService.SimpleManualBooking(Mapper.Map<OzCpSimpleManualCruiseBookingInput>(aParams));
//**TESTING
result.ResultErrors.AddFatalError(1, "Oh Dear!!!!");
//**As soon as I perform the mapping the ResultErrros collection loses the item I have added above
return Mapper.Map<SimpleManualCruiseBookingOutput>(result);
}
I am guessing it is because it is a read only property, but I cannot figure out how to make it transfer the collection.
Any help greatly appreciated.
EDIT
I have also tried adding the items in the collection myself so changing my mapping from:
Mapper.CreateMap<OzCpSimpleManualCruiseBookingOutput, SimpleManualCruiseBookingOutput>();
to using the after map function as follows:
Mapper.CreateMap<OzCpSimpleManualCruiseBookingOutput, SimpleManualCruiseBookingOutput>()
.AfterMap((src, dst) => dst.ResultErrors.Messages.AddRange(src.ResultErrors.Messages));
but this then results in the destination having TWO items in the list instead of 1 viz:
which are both the same entry of "Oh Dear!!!!"
SOLUTION
Using the private setter approach suggested by DavidL (and an upgrade to Automapper 4.x) meant I got the required behaviour. So this is what I ended up with:
/// <summary>
/// Defines the contract for all output DTO's to platform
/// application services.
/// </summary>
/// <seealso cref="OzCpAppServiceOutputBase" />
public interface IOzCpAppServiceOutputBase : IOutputDto
{
/// <summary>
/// Contains a list of errors should a call to an application service fail.
/// </summary>
OzCpResultErrors ResultErrors{ get; }
/// <summary>
/// When TRUE the underlying call to the application service was successful, FALSE
/// otherwise. When FALSE see ResultErrors for more information on the error condition.
/// </summary>
bool ResultSuccess { get; }
}
public class OzCpAppServiceOutputBase : IOzCpAppServiceOutputBase
{
public OzCpAppServiceOutputBase()
{
ResultErrors = new OzCpResultErrors();
}
/// <remarks>The private setter is here so that AutoMapper works.</remarks>
public OzCpResultErrors ResultErrors { get; private set; }
public bool ResultSuccess
{
get { return ResultErrors.Messages.Count == 0; }
}
}
So while needing to add a private setter "just for" AutoMapper that is a small price to pay to have this work and not use complicated mappings to deal with the issue.
With the current inheritance structure, AutoMapper will NOT be able to do what you want it to do. Since your destination structure has the same properties as your source structure, the properties are also readonly. AutoMapper will not map to readonly properties that do not have a setter declared.
You have a few options:
Make the property setter explicitly private. This answer suggests that later versions of AutoMapper support this functionality. In this case it works for 4.x.
Make the property setter internal, so that only members of this assembly can set it. Since latest versions of AutoMapper will map to private setters, they should also map to internal setters.
Make the property settable.
Downcast the object instead of mapping (you've mentioned you don't want to do this because your object structures will eventually diverge).
Shadow the property on the destination object with a public setter. Ugly and a good source of strange bugs.
public class SimpleManualCruiseBookingOutput : OzCpSimpleManualCruiseBookingOutput
{
public new OzCpResultErrors ResultErrors { get; set; }
}
Create a helper that maps your read-only properties via reflection. DO NOT DO THIS!
PropertyInfo nameProperty = aParams.GetType().GetProperty ("ResultErrors");
FieldInfo nameField = nameProperty.GetBackingField ();
nameField.SetValue (person, aParams.ResultErrors);

How to serialize a dictionary to an XML file?

I'm trying to maintain a dictionary of configurations.
Here is my abstract class.
[Serializable]
public abstract class Configuration
{
}
And here is a concrete class (for the moment, I just have only this class).
[Serializable]
public class BinaryProblemConfiguration : Configuration
{
[XmlAttribute]
public decimal MinValue { get; set; }
[XmlAttribute]
public decimal MaxValue { get; set; }
}
I've got a class which contains a Dictionary of configuration levels.
The first parameter is the name of the configuration. When name="" means default configuration.
Level means the difficulty. There are three levels: Easy, Medium and Hard.
And the third one is the configuration.
/// <summary>
/// The abstract level configuration allows descendent classes to configure themselves
/// </summary>
public abstract class LevelConfiguration
{
private Dictionary<string, Dictionary<Levels, Configuration>> _configurableLevels = new Dictionary<string, Dictionary<Levels, Configuration>>();
/// <summary>
/// Adds a configurable level.
/// </summary>
/// <param name="level">The level to add.</param>
/// <param name="problemConfiguration">The problem configuration.</param>
protected void AddConfigurableLevel(string name, Levels level, Configuration problemConfiguration)
{
if (!_configurableLevels.ContainsKey(name))
{
_configurableLevels.Add(name, new Dictionary<Levels, Configuration>());
}
_configurableLevels[name].Add(level, problemConfiguration);
}
/// <summary>
/// Returns all the configurable levels.
/// </summary>
/// <param name="level"></param>
protected void RemoveConfigurableLevel(string name, Levels level)
{
_configurableLevels[name].Remove(level);
}
/// <summary>
/// Returns all the configurable names.
/// </summary>
/// <returns></returns>
public IEnumerable<string> GetConfigurationNames()
{
return _configurableLevels.Keys;
}
/// <summary>
/// Returns all the configurable levels.
/// </summary>
/// <returns></returns>
public IEnumerable<Levels> GetConfigurationLevels(string name)
{
return _configurableLevels[name].Keys;
}
/// <summary>
/// Gets the problem configuration for the specified level
/// </summary>
/// <param name="level">The level.</param>
/// <returns></returns>
public Configuration GetProblemConfiguration(string name, Levels level)
{
return _configurableLevels[name][level];
}
}
This is the class which create some configurations. I'm creating three default configs and two customs.
public class AdditionLevelConfiguration : LevelConfiguration
{
public AdditionLevelConfiguration()
{
AddConfigurableLevel("", Levels.Easy, GetEasyLevelConfiguration());
AddConfigurableLevel("", Levels.Medium, GetMediumLevelConfiguration());
AddConfigurableLevel("", Levels.Hard, GetHardLevelConfiguration());
AddConfigurableLevel("config2", Levels.Easy, GetEasyLevelConfiguration());
AddConfigurableLevel("config2", Levels.Medium, GetMediumLevelConfiguration());
var configs = this.GetProblemConfiguration("config2", Levels.Medium);
var configs2 = this.GetProblemConfiguration("", Levels.Easy);
}
protected Configuration GetHardLevelConfiguration()
{
return new BinaryProblemConfiguration
{
MinValue = 100,
MaxValue = 1000,
};
}
protected Configuration GetMediumLevelConfiguration()
{
return new BinaryProblemConfiguration
{
MinValue = 10,
MaxValue = 100,
};
}
protected Configuration GetEasyLevelConfiguration()
{
return new BinaryProblemConfiguration
{
MinValue = 1,
MaxValue = 10,
};
}
}
I plan to write these configurations in a XML file. I was thinking of serialize them, but it throws me an error. What should I do?
Another option you may consider is using a DataContractSerializer. I was able to serialize your dictionary of dictionaries this way.
Things to keep in mind if you go this route:
You would have to add different attributes. Specifically, you need DataContract on the type and DataMember on the properties.
You need to ensure all properties have setters, even if the setters ultimately are private.
When creating your DataContractSerializer, you need to ensure it is aware of all your types. Giving it the type of your dictionary takes care of string, Levels, and Configuration, but not BinaryProblemConfiguration. Additional type info can be provided via additional constructor overloads.
Example:
var dict = new Dictionary<string,Dictionary<Levels,Configuration>> ();
var wtr = XmlWriter.Create (Console.Out);
var dcSerializer = new DataContractSerializer (dict.GetType (), new [] {typeof (BinaryProblemConfiguration)});
dcSerializer.WriteObject (wtr, dict);
If you do the above, you could also switch to DataContractJsonSerializer later for a more compact JSON format if you prefer (assuming XML is not a hard requirement, of course).
From what i know classes that implement IDictionary cannot be Xml serialized.
Try this http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx, it worked for me.

C# combobox like dropdown list in web application

I have comboBox component and i am adding items like comboBox1.Items.Add("Item1"). But i alo need to know some other info about this Item. So if i click "Item1" i need to get "102454".
Can i somehow save 102454 to "Item1" on combobox.
At web aplication there is dropdown list which look like
<select>
<option value="102454">Item1</option>
</select>
and when i click "Item1" i get 102454.
Can i do this in windows desktop applicatin with combobox?
Edit better solution:
Use a KeyValuePair and ValueMember \ DisplayValue:
comboBox1.ValueMember = "Key";
comboBox1.DisplayMember = "Value";
comboBox1.Items.Add(new KeyValuePair<int, string>(102454, "Item1"));
As Kristian points out this can be extended to be even more flexible - you can put whatever object you like into the list of items, and set the value and display members on the combobox to be whatever property path you want.
To get the key back later you can do this:
var item = combobox1.SelectedItem;
int key = ((KeyValuePair<int, string>)item).Key;
You may take a look at the SelectedItem property.
A have created a similar class like Mark Pim suggested; however, mine uses generics.
I certainly would not choose to make the Value property a string type.
public class ListItem<TKey> : IComparable<ListItem<TKey>>
{
/// <summary>
/// Gets or sets the data that is associated with this ListItem instance.
/// </summary>
public TKey Key
{
get;
set;
}
/// <summary>
/// Gets or sets the description that must be shown for this ListItem.
/// </summary>
public string Description
{
get;
set;
}
/// <summary>
/// Initializes a new instance of the ListItem class
/// </summary>
/// <param name="key"></param>
/// <param name="description"></param>
public ListItem( TKey key, string description )
{
this.Key = key;
this.Description = description;
}
public int CompareTo( ListItem<TKey> other )
{
return Comparer<String>.Default.Compare (Description, other.Description);
}
public override string ToString()
{
return this.Description;
}
}
It is also easy to create a non-generic variant of it:
public class ListItem : ListItem<object>
{
/// <summary>
/// Initializes a new instance of the <see cref="ListItem"/> class.
/// </summary>
/// <param name="key"></param>
/// <param name="description"></param>
public ListItem( object key, string description )
: base (key, description)
{
}
}

My C# COM Library Isn't Working Right; Can't Instantiate The Class and a Method Appears As a Property

I'm writing a COM DLL in C# to handle the import and export of X.12 format documents, so I would be able to use it in an Access database and a custom program for handling EDI with my company. I've gotten a DLL to compile but with disappointing results, and I'm wondering if I'm missing something; COM "from scratch" is new ground to me (I've made a ribbon for Excel before but a wizard handled all of that).
I've read this article on MSDN and came across this question here to get my DLL and TLB to compile and register. This is the skeleton of my X12Segment class and the interface for COM visibility:
using System;
using System.Collections;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace X12
{
[Guid("28A76274-05EE-45B2-A8EF-ADD5A5B351DE"),
ComVisible(true)]
public interface IX12Segment
{
[DispId(1)]
string SegmentType { get; set; }
[DispId(2)]
string[] Fields { get; set; }
[DispId(3)]
char FieldDelimiter { get; set; }
[DispId(4)]
char SegmentDelimiter { get; set; }
[DispId(5)]
string ToString(char sep, char eol);
[DispId(6)]
string ToString();
[DispId(7)]
Type GetFieldEnum();
}
[Guid("B321599A-E5EC-4510-A021-E9A8B4D6293E"),
ClassInterface(ClassInterfaceType.None),
ComVisible(true)]
public class X12Segment : IX12Segment
{
private string _type;
protected ArrayList _fields;
protected short _minFields = -1;
protected short _maxFields = -1;
protected short[][] _fieldSizes = { new short[] { -1, -1 } };
protected char _sep;
protected char _eol;
public enum Field { }
/// <summary>
/// Creates a new X.12 segment of the supplied type,
/// optionally with a supplied number of fields or
/// values.
/// </summary>
/// <param name="segType">The type of segment (eg "ISA", "GS")</param>
/// <param name="fields">Each string is a field
/// within the segment</param>
public X12Segment(string segType, params string[] fields)
: this(segType)
{
//Do cool stuff
}
/// <summary>
/// Creates a new X.12 segment from a string.
/// </summary>
/// <param name="segType">The type of segment (eg "ISA", "GS")</param>
/// <param name="segment">The string to parse</param>
public X12Segment(string segType, string segment)
: this(segType)
{
//Do cool stuff
}
/// <summary>
/// Creates a new X.12 segment of the supplied type,
/// optionally with a supplied number of fields or
/// values.
/// </summary>
/// <param name="segType">The type of segment (eg "ISA", "GS")</param>
/// <param name="fieldCount">The number of fields
/// in this segment</param>
public X12Segment(string segType, int fieldCount) : this(segType)
{
//Do cool stuff
}
/// <summary>
/// Creates a new X.12 segment of the supplied type,
/// optionally with a supplied number of fields or
/// values.
/// </summary>
/// <param name="segType">The type of segment (eg "ISA", "GS")</param>
public X12Segment(string segType) : this()
{
//Do cool stuff
}
public X12Segment()
{
//Do cool stuff
}
/// <summary>
/// Gets or sets the segment type.
/// </summary>
public string SegmentType
{
get;
set;
}
/// <summary>
/// Gets or sets all of the fields in the segment,
/// in the form of an array of strings.
/// </summary>
public string[] Fields
{
get;
set;
}
/// <summary>
/// Gets or sets the character used to seperate fields in the segment.
/// </summary>
public char FieldDelimiter
{
get;
set;
}
/// <summary>
/// Gets or sets the character denoting the end of the segment.
/// </summary>
public char SegmentDelimiter
{
get;
set;
}
/// <summary>
/// Generates an X.12 formatted segment.
/// </summary>
/// <param name="sep">The field delimiter to use.</param>
/// <param name="eol">The segment delimiter to use.</param>
/// <returns>An X.12 formatted string.</returns>
public string ToString(char sep, char eol)
{
//Do cool stuff
}
/// <summary>
/// Generates an X.12 formatted segment.
/// </summary>
/// <returns>An X.12 formatted string.</returns>
public override string ToString()
{
//Do cool stuff
}
/// <summary>
/// Returns the Type associated with the Field enumeration of this object.
/// </summary>
/// <returns>A System.Type of this object's Field enumeration.</returns>
public virtual Type GetFieldEnum()
{
//Do cool stuff
}
}
}
Now, when I open up VBA and add the reference, the class shows up in IntelliSense. However, when I Dim a variable with the X12Segment type, then put in the dot operator, the IntelliSense window that pops up shows me that ToString() is a property, not a method. Also, ToString's overload shows up as ToString_2. When I attempt Set seg = New X12Segment, VBA tells me that it's an invalid use of the New keyword.
What am I missing here?
Update
I've revised my code as per the comments and answer below, and I have solutions for New not working and my ToString overload appearing funky in IntelliSense. A new problem arises, though; trying to access Fields gives me errors. seg.Fields = someStringArray gets me an error saying "Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic".
A [ComVisible] class must have a parameterless constructor. COM doesn't have a mechanism to pass arguments to a constructor. You provided only constructors that take arguments which is why VBA doesn't let you use the New keyword.
COM also doesn't support method overloading. You need to give the methods distinct names. If you don't then the type library exporter will take care of it automatically. Thus ToString_2().

WCF service proxy not setting "FieldSpecified" property

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

Categories