I am putting together a wizard that has multiple pages that the user is shown. I need one page to be able access data from a user choice on a previous page. My idea was to just pass in a parameter by reference into the constructors of both pages, and then assign a property to that parameter, but the change isn't persisting between pages. I'm assuming it means I am incorrectly using ref.
I cannot pass data directly to the methods themselves as they are controlled by the wizard host.
Host initialization:
WizardHost host = new WizardHost();
using (host)
{
host.Text = Migration.Properties.Resources.AppName;
host.ShowFirstButton = false;
host.ShowLastButton = false;
host.WizardCompleted += new WizardHost.WizardCompletedEventHandler(this.Host_WizardCompleted);
Reference<DBManip> dbControllerRef = new Reference<DBManip>();
bool exportPathActive = false;
host.WizardPages.Add(1, new Page1());
host.WizardPages.Add(2, new Page2(dbControllerRef));
host.WizardPages.Add(3, new Page3(dbControllerRef, ref exportPathActive));
host.WizardPages.Add(4, new Page4(dbControllerRef, ref exportPathActive));
host.WizardPages.Add(5, new Page5());
host.LoadWizard();
host.ShowDialog();
Page where ref is linked with property:
public Page3(Reference<DBManip> dbControllerRef, ref bool exportPathActive)
{
this.InitializeComponent();
this.DBControllerRef = dbControllerRef;
this.Page3Body.Text = Migration.Properties.Resources.Page3Body;
this.ExportPathActiveRef = exportPathActive;
}
public Reference<DBManip> DBControllerRef
{
get;
private set;
}
If I modify exportPathActive in the constructor that modification is preserved in the next page, but a property that is assigned to the passed parameter doesn't preserve the reference. I'm pretty new to C#, so this is probably something silly I'm missing, but I can't find it on Google or looking around SO.
I have decided on just making a class called PersistentData with a property called ExportPathActive, and then passing that. It works well and I can expand it to hold more data if needed. I'll wait to approve this in case a more elegant approach is posted.
The Class:
/// <summary>
/// A store to pass data between pages.
/// </summary>
public class PersistentData
{
/// <summary>
/// Initializes a new instance of the <see cref="PersistentData"/> class.
/// </summary>
public PersistentData()
{
this.ExportPathActive = false;
}
/// <summary>
/// Gets or sets a value indicating whether [export path active].
/// </summary>
/// <value>
/// <c>true</c> if [export path active]; otherwise, <c>false</c>.
/// </value>
public bool ExportPathActive { get; set; }
}
Related
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>();
First of all, I have been looking online for a Settings Design Pattern but I haven't been able to find a solution that works in my case or only cryptic answers like Use Dependency Injection.
The Problem
I have a large ASP .NET solution which is deployed to multiple production environments. Many decisions in code are taken based on settings which have been implemented in the Web.config file (a few hundreds). At the moment they are all accessed using the NameValueCollection ConfigurationManager.AppSettings. I want to move these settings in the database such that I can create a user interface and modify them more easily afterwards.
I would also prefer for the settings to remain accessible from the Web.config such that if something happens with the database, the application won't break.
Solution Architecture
Basically, the solution consists of the following projects:
UI (this is were the Web.config file resides)
BLL
DAL
Integration Project 1 (the application interracts with other applications. This project uses configuration settings like web addresses, authentication information etc. which at the moment are passed as parameters in the methods - which I personally find very ugly)
Integration Project 2
...
The Code
The Setting class (the database table is practically the same). SettingType is an enum which defines the type of the Setting: Int, String, Bool and I defined for the UI.
public class Setting
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
public int Id { get; set; }
/// <summary>
/// Gets or sets the name of the setting.
/// </summary>
/// <value>
/// The name of the setting.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the type of the setting.
/// </summary>
/// <value>
/// The type of the setting.
/// </value>
public SettingType Type { get; set; }
/// <summary>
/// Gets or sets the setting value.
/// </summary>
/// <value>
/// The setting value.
/// </value>
public object Value { get; set; }
/// <summary>
/// Gets or sets the setting description.
/// </summary>
/// <value>
/// The setting description.
/// </value>
public string Description { get; set; }
/// <summary>
/// Gets or sets the setting key.
/// </summary>
/// <value>
/// The setting key.
/// </value>
public string Key { get; set; }
/// <summary>
/// Gets or sets the default value
/// </summary>
/// <value>
/// The default value
/// </value>
public string Default { get; set; }
}
The SettingHelper class (which does everything at the moment, will split functionality in the future):
public static class SettingHelper
{
// Settings collection
private static List<Setting> _settings;
/// <summary>
/// Reloads the settings.
/// </summary>
/// <exception cref="System.Exception"></exception>
public static void LoadSettings()
{
_settings = new List<Setting>();
// Code which loads the settings from the database
}
private static Setting GetSetting(string key)
{
try
{
// if settings are not loaded, we reload them from the database
if (_settings == null || _settings.Count == 0)
LoadSettings();
var value = from Setting setting in _settings
where setting != null && setting.Key == key
select setting;
return value.FirstOrDefault();
}
catch (Exception)
{
return null;
}
}
public static void SetSetting(string key, object newValue, int userID)
{
var currentSetting = GetSetting(key);
if (currentSetting != null && !Convert.ToString(currentSetting.Value).ToUpper().Equals(Convert.ToString(newValue).ToUpper()))
{
// Code which updates the setting value
}
}
// For the UI
public static IEnumerable<Setting> GetAllSettings()
{
if (_settings == null)
LoadSettings();
return _settings;
}
// To change back swiftly to the Web.config in case there are errors in production - will remove in the future
public static bool SettingsFromDataBase
{
get
{
if (HttpContext.Current.Application["SettingsFromDataBase"] == null)
HttpContext.Current.Application["SettingsFromDataBase"] = ConfigurationManager.AppSettings["SettingsFromDataBase"];
bool settingsFromDataBase;
if (bool.TryParse(HttpContext.Current.Application["SettingsFromDataBase"] as string, out settingsFromDataBase))
return settingsFromDataBase;
else return false;
}
}
public static T ObjectToGenericType<T>(object obj)
{
if (obj is T)
{
return (T)obj;
}
else
{
try
{
return (T)Convert.ChangeType(obj, typeof(T));
}
catch (InvalidCastException)
{
return default(T);
}
}
}
public static T GetSetting<T>(string key)
{
if (SettingsFromDataBase)
{
var setting = GetSetting(key);
return setting != null
? ObjectToGenericType<T>(setting.Value)
: ObjectToGenericType<T>(ConfigurationManager.AppSettings[key]);
}
if (HttpContext.Current.Application[key] == null)
HttpContext.Current.Application[key] = ConfigurationManager.AppSettings[key];
return (T)HttpContext.Current.Application[key];
}
// The actual settings which will be used in the other projects
public static string StringSetting
{
get { return GetSetting<string>("StringSetting"); }
}
public static bool BoolSetting
{
get { return GetSetting<bool>("BoolSetting"); }
}
Note: Likely there are improvements to this class using Reflection, but I would prefer to avoid it (apart from that conversion).
Questions
At the moment the SettingHelper resides in the UI project (in order to be able to access the settings from the Web.config). How can I transmit these settings in the BLL or Integration projects?
Also, do you think this implementation with all the settings stored in a static list is the way to go? It feels unoptimal.
Any help or discussion on improvements is appreciated. Thank you.
As far as I know, currently PRISM allows to pass strings, but doesn't allow to pass objects. I would like to know what are the ways of overcoming this issue.
I want to pass a list collection. The UriQuery isn't usefull in my case, what should I do in this case?
Prism 5 and 6 : The NavigationParameters class can now be used to pass object parameters during navigation, using the overloads of the RequestNavigate method of a Region or RegionManager instance.
I have my own technique.
I extract the hash code of the object and save it in a Dictionary, with the hash code as the key and the object as the value of the pair.
Then, I attach the hash code to the UriQuery.
After, I only have to get the hash code that comes from the Uri on the target view and use it to request the original object from the Dictionary.
Some example code:
Parameter repository class:
public class Parameters
{
private static Dictionary<int, object> paramList =
new Dictionary<int, object>();
public static void save(int hash, object value)
{
if (!paramList.ContainsKey(hash))
paramList.Add(hash, value);
}
public static object request(int hash)
{
return ((KeyValuePair<int, object>)paramList.
Where(x => x.Key == hash).FirstOrDefault()).Value;
}
}
The caller code:
UriQuery q = null;
Customer customer = new Customer();
q = new UriQuery();
Parameters.save(customer.GetHashCode(), customer);
q.Add("hash", customer.GetHashCode().ToString());
Uri viewUri = new Uri("MyView" + q.ToString(), UriKind.Relative);
regionManager.RequestNavigate(region, viewUri);
The target view code:
public partial class MyView : UserControl, INavigationAware
{
// some hidden code
public void OnNavigatedTo(NavigationContext navigationContext)
{
int hash = int.Parse(navigationContext.Parameters["hash"]);
Customer cust = (Customer)Parameters.request(hash);
}
}
That's it.
You can create an PRISM event with 'object' getter/setter. Rise event with your object casted or not casted to 'object' inside event (depends if event implementation 'shared' like in famous 'Infrastructure' projects) and then Navigate to Region. In ViewModel that implement Region - Subscribe() to above event, receive it and store locally and then just wait for 'OnNavigatedTo' function call. When OnNavigatedTo function called you already have the object/class/struct and can run the ViewModel.
For example - Event class:
namespace CardManagment.Infrastructure.Events
{
using Microsoft.Practices.Prism.Events;
/// <summary>
/// Event to pass 'Selected Project' in between pages
/// </summary>
public class SelectedProjectViewEvent : CompositePresentationEvent<SelectedProjectViewEvent>
{
public object SelectedPorject { get; set; }
}
}
'Calling' class
/// <summary>
/// Called when [back to project view].
/// </summary>
/// <param name="e">The e.</param>
public void OnBackToProjectView(CancelEditProjectEvent e)
{
eventAggregator.GetEvent<SelectedProjectViewEvent>().Publish(new SelectedProjectViewEvent()
{
SelectedPorject = selectedProject
});
regionManager.RequestNavigate(WellKnownRegionNames.ProjectViewRegion, new System.Uri("ProjectDetailsView", System.UriKind.Relative));
}
And this on 'Receiver' class
/// <summary>
/// Called when the implementer has been navigated to.
/// </summary>
/// <param name="navigationContext">The navigation context.</param>
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (this.SelectedProject == null) // <-- If event received untill now
this.ShouldBeVisible = false;
else
this.ShouldBeVisible = true;
}
You can also check out how to pass objects if you are using an IOC and want to use constructor injection.
https://stackoverflow.com/a/20170410/1798889
I am experiencing some weird behavior that disappears/reappears based on whether this dictionary is a new instance of the object, or the old instance of the object. Let me provide all the code first.
/// <summary>
/// Removes a control from monitoring/Session/Database based on ID.
/// </summary>
public static void Remove<T>(ICormantControl<T> control)
{
_logger.InfoFormat("Removing {0}", control.ID);
SerializableDictionary<string, T> states = new SerializableDictionary<string,T>(GetStates<SerializableDictionary<string, T>>());
((IDictionary)states).Remove(control.ID);
SetStates(states);
}
/// <summary>
/// Retrieves information on an object. If the object is cached to Session then the
/// cached object is retrieved. Else, it is retrieved from the database.
/// </summary>
/// <typeparam name="T"> The type of object expected to get back.</typeparam>
/// <returns> Collection of data for the specific object type.</returns>
public static T GetStates<T>() where T : new()
{
T states = new T();
string stateName = GetStateNameFromType(typeof(T));
if (!Equals(SessionRepository.Instance.GetSession(stateName), null))
{
states = (T)SessionRepository.Instance.GetSession(stateName);
}
else
{
XmlSerializer serializer = new XmlSerializer(states.GetType());
string data = DatabaseRepository.Instance.GetWebLayoutData(stateName);
if (!string.IsNullOrEmpty(data))
{
byte[] dataAsArray = Convert.FromBase64String(data);
MemoryStream stream = new MemoryStream(dataAsArray);
states = (T)serializer.Deserialize(stream);
}
SessionRepository.Instance.SetSession(stateName, states);
}
return states;
}
public static void SetStates<T>(T states) where T : new()
{
string stateName = GetStateNameFromType(typeof(T));
SessionRepository.Instance.SetSession(stateName, states);
if (shouldWriteToDatabase) DatabaseRepository.Instance.SaveToDatabase(stateName, states);
}
/// <summary>
/// Recreates the page state recursively by creating a control and looking for its known children.
/// </summary>
/// <param name="pane"> Pane having children added to it.</param>
private void RegeneratePaneChildren(CormantRadPane pane)
{
_logger.InfoFormat("Initializing paneToResize children for paneToResize {0}", pane.ID);
foreach (var splitterState in StateManager.GetStates<SerializableDictionary<string, RadSplitterSetting>>())
{
RadSplitterSetting splitterSetting = splitterState.Value;
if (!splitterSetting.ParentID.Contains(pane.ID)) continue;
CormantRadSplitter splitter = new CormantRadSplitter(splitterSetting);
pane.UpdatePanel.ContentTemplateContainer.Controls.AddAt(0, splitter); //Visibility will fight with splitter if you don't re-add like this.
RegenerateSplitterChildren(splitter);
}
}
/// <summary>
/// Recreates the page state recursively by creating a control and looking for its known children.
/// </summary>
/// <param name="splitter"> Splitter having children added to it. </param>
public void RegenerateSplitterChildren(RadSplitter splitter)
{
_logger.InfoFormat("Initializing splitter children for splitter {0}", splitter.ID);
foreach (var paneState in StateManager.GetStates<SerializableDictionary<string, RadPaneSetting>>()
.Where(paneState => paneState.Value.ParentID.Contains(splitter.ID)))
{
RadPaneSetting paneSetting = paneState.Value;
CormantRadPane pane = new CormantRadPane(paneSetting);
StyledUpdatePanel updatePanel = pane.CreateUpdatePanel(paneSetting.UpdatePanelID);
pane.Controls.Add(updatePanel);
splitter.Controls.Add(pane);
RegeneratePaneChildren(pane);
InsertSplitBar(splitter);
}
}
The key line to look at in all of this is: SerializableDictionary<string, T> states = new SerializableDictionary<string,T>(GetStates<SerializableDictionary<string, T>>());
If this line of code is modified such that it does not create a new instance of states (instead using the object saved in Session) my code gets 'desynched' and I experience odd behavior with my Regeneration methods. An object that is supposed to have 'ObjectA' as a parent instead has 'ObjectB' as a parent.
There's a lot of collection-modification going on... I'm removing a control from states and re-saving it...but I can't see where I do anything explicitly incorrect in this code. Yet, I still feel that I should be able to express the above line of code without creating a new instance of the object.
If anyone sees an obvious blunder I'd love to hear it. Thanks.
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