/// Serialization
/// Code 2012.05.23, [...] following Jani Giannoudis' examples
/// CodeProject Article "User Settings Applied",
/// http://www.codeproject.com/Articles/25829/User-Settings-Applied
/// </summary>
I'm using the above mentioned codeproject.com Code since a number of years successfully in different projects.
A few days ago, I converted one of those projects from .NET 4.x to .NET 6.0 and the unmodified code immediately stopped working (details below) for example in the following snippet:
// DataGridColumnSetting[] is based on System.Configuration.ApplicationSettingsBase
// https://learn.microsoft.com/en-us/dotnet/api/system.configuration.applicationsettingsbase?view=dotnet-plat-ext-6.0
private DataGridColumnSetting[] OriginalColumnSettings
{
get
{
return LoadValue(
Name,
typeof(DataGridColumnSetting[]),
SettingsSerializeAs.Binary,
null) as DataGridColumnSetting[];
}
}
Throwing a
System.NotSupportedException
HResult=0x80131515
Message=BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.
Source=System.Configuration.ConfigurationManager
StackTrace:
at System.Configuration.SettingsProperty..ctor(String name, Type propertyType, SettingsProvider provider, Boolean isReadOnly, Object defaultValue, SettingsSerializeAs serializeAs, SettingsAttributeDictionary attributes, Boolean throwOnErrorDeserializing, Boolean throwOnErrorSerializing)
at MyNamespace.Serialization.Setting.CreateSettingProperty(String name, Type type, SettingsSerializeAs serializeAs, Object defaultValue) in [...]MyNamespace\Serialization\Setting.cs:line 111
Since the very same code is working well in a .NET 4.8 project, I tried to find hints in the web and found
https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/binaryformatter-serialization-obsolete
(and a few others) also saying
Warning
"The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they're processing to be trustworthy. BinaryFormatter is insecure and can't be made secure."
Actual Question:
Anyone else having the very same issue using the same code (from the above mentioned CodeProject Article "User Settings Applied").
(If not, I would start modifying (my personal flavor of) that code, and if successful post an answer to my question assuming others might hopefully benefit.)
Okay, apparently using JSON instead of Binary works as expected here in an http://www.codeproject.com/Articles/25829/User-Settings-Applied context (in .Net4.x as well as .Net 6).
The basic concept there is having a particular Serialization class for each UI Control one wants to handle. And only some of the article samples are using the deprecated SettingsSerializeAs.Binary, for example the one made for WPF DataGridcontrols. The concept modification that works for me is using (NuGet) Newtonsoft.Json for serialization there.
The typical pattern part where the article author is using SettingsSerializeAs as quoted in the question is now using SettingsSerializeAs.String instead of Binary:
private string OriginalColumnSettings
{
get
{
return LoadValue(
Name,
typeof(string),
SettingsSerializeAs.String,
defaultValue: null) as string;
}
}
Full real world (WPF) Sample for both .Net4.8 as well as .Net6:
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace WhatEverNamespace.Serialization
{
/// <summary>
/// Serialization
/// Code 2012.05.23, [...] following Jani Giannoudis' examples
/// CodeProject Article "User Settings Applied",
/// http://www.codeproject.com/Articles/25829/User-Settings-Applied
/// </summary>
public class DataGridSetting : Setting
{
public static readonly DependencyProperty SettingProperty =
DependencyProperty.RegisterAttached(
"Setting",
typeof(string),
typeof(DataGridSetting),
new FrameworkPropertyMetadata(OnDataGridSettingChanged));
#region Fields
private static readonly Logger log = LogManager.GetCurrentClassLogger();
private readonly DataGrid dataGrid;
private readonly IList<DataGridColumn> dataGridColumns = new List<DataGridColumn>();
private bool isLoaded;
private readonly string name;
private bool useWidth = true;
private bool useDisplayIndex = true;
#endregion Fields
#region Constructors
public DataGridSetting(DataGrid dataGrid) :
this(dataGrid?.Name, dataGrid)
{
}
public DataGridSetting(string name, DataGrid dataGrid)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
this.name = name;
this.dataGrid = dataGrid ?? throw new ArgumentNullException(nameof(dataGrid));
}
#endregion Constructors
#region Properties
public string Name { get { return name; } }
public DataGrid DataGrid { get { return dataGrid; } }
public bool UseWidth
{
get { return useWidth; }
set { useWidth = value; }
}
public bool UseDisplayIndex
{
get { return useDisplayIndex; }
set { useDisplayIndex = value; }
}
public override bool HasChanged
{
get
{
var json = OriginalColumnSettings;
if (string.IsNullOrWhiteSpace(json))
return false;
var originalColumnSettings = JsonConvert.DeserializeObject<DataGridColumnSetting[]>(json);
DataGridColumnSetting[] columnSettings = ColumnSettings;
if (json.Length != columnSettings.Length)
return true;
for (int i = 0; i < originalColumnSettings.Length; i++)
{
if (!originalColumnSettings[i].Equals(columnSettings[i]))
return true;
}
return false;
}
}
private string OriginalColumnSettings
{
get
{
return LoadValue(
Name,
typeof(string),
SettingsSerializeAs.String,
defaultValue: null) as string;
}
}
private DataGridColumnSetting[] ColumnSettings
{
get
{
if (dataGrid?.Columns.Any() != true)
return null;
IList<DataGridColumnSetting> columnSettings =
new List<DataGridColumnSetting>(dataGrid.Columns.Count);
foreach (DataGridColumn dataGridColumn in dataGrid.Columns)
{
int index = dataGridColumns.IndexOf(dataGridColumn);
int displayIndex = dataGridColumn.DisplayIndex;
DataGridColumnSetting columnSetting = new DataGridColumnSetting
{
Index = index,
DisplayIndex = displayIndex,
Width = dataGridColumn.ActualWidth
};
columnSettings.Add(columnSetting);
}
return columnSettings.ToArray();
}
}
#endregion Properties
#region Methods
public static string GetSetting(DependencyObject dependencyObject)
{
return dependencyObject?.GetValue(SettingProperty) as string;
}
public static void SetSetting(DependencyObject dependencyObject, string settingKey)
{
dependencyObject?.SetValue(SettingProperty, settingKey);
}
public override void Load()
{
// Initialized event does not work since it's running too early in a WPF DataGrid
// ("dataGrid.Initialized += DataGridInitialized" in Jani's ListViewSettings.cs)
if (isLoaded == false)
SetupDataGridColumns();
try
{
DataGrid dataGrid = this.dataGrid;
if (dataGrid?.Columns.Any() != true)
return;
var json = OriginalColumnSettings;
var columnSettings = JsonConvert.DeserializeObject<DataGridColumnSetting[]>(json);
if (columnSettings?.Any() != true)
return;
for (int displayIndex = 0; displayIndex < columnSettings.Length; displayIndex++)
{
DataGridColumnSetting columnSetting = columnSettings[displayIndex];
if (columnSetting.Index < 0 || columnSetting.Index >= dataGridColumns.Count)
continue;
DataGridColumn dataGridColumn = dataGridColumns[columnSetting.Index];
if (useWidth)
dataGridColumn.Width = new DataGridLength(columnSetting.Width);
if (useDisplayIndex && columnSetting.Index != columnSetting.DisplayIndex)
dataGridColumn.DisplayIndex = columnSetting.DisplayIndex;
}
}
catch
{
if (ThrowOnErrorLoading)
throw;
}
}
public override void Save()
{
try
{
DataGridColumnSetting[] columnSettings = ColumnSettings;
if (columnSettings == null)
return;
var json = JsonConvert.SerializeObject(columnSettings);
SaveValue(
Name,
typeof(string),
SettingsSerializeAs.String,
json,
null);
}
catch
{
if (ThrowOnErrorSaving)
throw;
}
}
public override string ToString()
{
return string.Concat(name, " (DataGrid)");
}
private void SetupDataGridColumns()
{
dataGridColumns.Clear();
if (dataGrid == null)
return;
if (dataGrid.Columns.Count > 0)
isLoaded = true;
else
return;
for (int i = 0; i < dataGrid.Columns.Count; i++)
{
dataGridColumns.Add(dataGrid.Columns[i]);
}
}
private static void OnDataGridSettingChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
if (!(dependencyObject is DataGrid dataGrid))
{
log.Warn(CultureInfo.InvariantCulture,
"{0}.{1}(): invalid property attachment",
nameof(DataGridSetting),
nameof(OnDataGridSettingChanged));
return;
}
// search on the parent-tree for application settings
ApplicationSettings applicationSettings = FindParentSettings(dependencyObject);
if (applicationSettings == null)
{
log.Warn(CultureInfo.InvariantCulture,
"{0}.{1}(): missing application settings in parent hierarchy",
nameof(DataGridSetting),
nameof(OnDataGridSettingChanged));
return;
}
applicationSettings.Settings.Add(new DataGridSetting(dataGrid));
}
private static ApplicationSettings FindParentSettings(DependencyObject element)
{
while (element != null)
{
if (element.ReadLocalValue(
DependencyPropertySetting.ApplicationSettingsProperty) is ApplicationSettings applicationSettings)
return applicationSettings;
element = LogicalTreeHelper.GetParent(element);
}
return null;
}
#endregion Methods
}
}
Related
We have a setup that used to work and has stopped sometime over the past few months. We use a custom dead-letter queue that is specified in config but basically gets set like this::
MsmqIntegrationBinding msmq = new MsmqIntegrationBinding(
// Defaults to WindowsDomain. We want all messages to be authenticated.
MsmqIntegrationSecurityMode.Transport);
msmq.DeadLetterQueue = DeadLetterQueue.Custom;
msmq.CustomDeadLetterQueue = new Uri("net.msmq://localhost/private/BulkUpdatesDeadLetter");
We've started getting this error, which seems to be pretty clear:
System.InvalidOperationException: A mismatch occurred between the binding and the MSMQ configuration. Messages cannot be sent. The custom dead letter queue specified in the binding must be a transactional queue. Ensure that the custom dead letter queue address is correct and the queue is a transactional queue.
We've verified both that we are pointing to the correct queue in the config and that the queue is transactional. Are there any other issues that might cause this exception to be thrown, or are we just missing something in the obvious?
Update: Had our web ops team delete and recreate the queues and still receiving the error.
The following code is provided by Microsoft corporation in the article BindingsSection.cs source code in C# .NET
namespace System.ServiceModel.Configuration
{
using System.Configuration;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Xml;
using System.Security;
public sealed partial class BindingsSection : ConfigurationSection, IConfigurationContextProviderInternal
{
static Configuration configuration = null;
ConfigurationPropertyCollection properties;
public BindingsSection() { }
Dictionary<string, bindingcollectionelement=""> BindingCollectionElements
{
get
{
Dictionary<string, bindingcollectionelement=""> bindingCollectionElements = new Dictionary<string, bindingcollectionelement="">();
foreach (ConfigurationProperty property in this.Properties)
{
bindingCollectionElements.Add(property.Name, this[property.Name]);
}
return bindingCollectionElements;
}
}
new public BindingCollectionElement this[string binding]
{
get
{
return (BindingCollectionElement)base[binding];
}
}
protected override ConfigurationPropertyCollection Properties
{
get
{
if (this.properties == null)
{
this.properties = new ConfigurationPropertyCollection();
}
this.UpdateBindingSections();
return this.properties;
}
}
[ConfigurationProperty(ConfigurationStrings.BasicHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public BasicHttpBindingCollectionElement BasicHttpBinding
{
get { return (BasicHttpBindingCollectionElement)base[ConfigurationStrings.BasicHttpBindingCollectionElementName]; }
}
// This property should only be called/set from BindingsSectionGroup TryAdd
static Configuration Configuration
{
get { return BindingsSection.configuration; }
set { BindingsSection.configuration = value; }
}
[ConfigurationProperty(ConfigurationStrings.CustomBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public CustomBindingCollectionElement CustomBinding
{
get { return (CustomBindingCollectionElement)base[ConfigurationStrings.CustomBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.MsmqIntegrationBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public MsmqIntegrationBindingCollectionElement MsmqIntegrationBinding
{
get { return (MsmqIntegrationBindingCollectionElement)base[ConfigurationStrings.MsmqIntegrationBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetPeerTcpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetPeerTcpBindingCollectionElement NetPeerTcpBinding
{
get { return (NetPeerTcpBindingCollectionElement)base[ConfigurationStrings.NetPeerTcpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetMsmqBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetMsmqBindingCollectionElement NetMsmqBinding
{
get { return (NetMsmqBindingCollectionElement)base[ConfigurationStrings.NetMsmqBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetNamedPipeBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetNamedPipeBindingCollectionElement NetNamedPipeBinding
{
get { return (NetNamedPipeBindingCollectionElement)base[ConfigurationStrings.NetNamedPipeBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.NetTcpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public NetTcpBindingCollectionElement NetTcpBinding
{
get { return (NetTcpBindingCollectionElement)base[ConfigurationStrings.NetTcpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WSFederationHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WSFederationHttpBindingCollectionElement WSFederationHttpBinding
{
get { return (WSFederationHttpBindingCollectionElement)base[ConfigurationStrings.WSFederationHttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WS2007FederationHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WS2007FederationHttpBindingCollectionElement WS2007FederationHttpBinding
{
get { return (WS2007FederationHttpBindingCollectionElement)base[ConfigurationStrings.WS2007FederationHttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WSHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WSHttpBindingCollectionElement WSHttpBinding
{
get { return (WSHttpBindingCollectionElement)base[ConfigurationStrings.WSHttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WS2007HttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WS2007HttpBindingCollectionElement WS2007HttpBinding
{
get { return (WS2007HttpBindingCollectionElement)base[ConfigurationStrings.WS2007HttpBindingCollectionElementName]; }
}
[ConfigurationProperty(ConfigurationStrings.WSDualHttpBindingCollectionElementName, Options = ConfigurationPropertyOptions.None)]
public WSDualHttpBindingCollectionElement WSDualHttpBinding
{
get { return (WSDualHttpBindingCollectionElement)base[ConfigurationStrings.WSDualHttpBindingCollectionElementName]; }
}
public static BindingsSection GetSection(Configuration config)
{
if (config == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("config");
}
return (BindingsSection)config.GetSection(ConfigurationStrings.BindingsSectionGroupPath);
}
public List<bindingcollectionelement> BindingCollections
{
get
{
List<bindingcollectionelement> bindingCollections = new List<bindingcollectionelement>();
foreach (ConfigurationProperty property in this.Properties)
{
bindingCollections.Add(this[property.Name]);
}
return bindingCollections;
}
}
internal static bool TryAdd(string name, Binding binding, Configuration config, out string bindingSectionName)
{
bool retval = false;
BindingsSection.Configuration = config;
try
{
retval = BindingsSection.TryAdd(name, binding, out bindingSectionName);
}
finally
{
BindingsSection.Configuration = null;
}
return retval;
}
internal static bool TryAdd(string name, Binding binding, out string bindingSectionName)
{
// TryAdd built on assumption that BindingsSectionGroup.Configuration is valid.
// This should be protected at the callers site. If assumption is invalid, then
// configuration system is in an indeterminate state. Need to stop in a manner that
// user code can not capture.
if (null == BindingsSection.Configuration)
{
DiagnosticUtility.DebugAssert("The TryAdd(string name, Binding binding, Configuration config, out string binding) variant of this function should always be called first. The Configuration object is not set.");
DiagnosticUtility.FailFast("The TryAdd(string name, Binding binding, Configuration config, out string binding) variant of this function should always be called first. The Configuration object is not set.");
}
bool retval = false;
string outBindingSectionName = null;
BindingsSection sectionGroup = BindingsSection.GetSection(BindingsSection.Configuration);
sectionGroup.UpdateBindingSections();
foreach (string sectionName in sectionGroup.BindingCollectionElements.Keys)
{
BindingCollectionElement bindingCollectionElement = sectionGroup.BindingCollectionElements[sectionName];
// Save the custom bindings as the last choice
if (!(bindingCollectionElement is CustomBindingCollectionElement))
{
MethodInfo tryAddMethod = bindingCollectionElement.GetType().GetMethod("TryAdd", BindingFlags.Instance | BindingFlags.NonPublic);
if (tryAddMethod != null)
{
retval = (bool)tryAddMethod.Invoke(bindingCollectionElement, new object[] { name, binding, BindingsSection.Configuration });
if (retval)
{
outBindingSectionName = sectionName;
break;
}
}
}
}
if (!retval)
{
// Much of the time, the custombinding should come out ok.
CustomBindingCollectionElement customBindingSection = CustomBindingCollectionElement.GetBindingCollectionElement();
retval = customBindingSection.TryAdd(name, binding, BindingsSection.Configuration);
if (retval)
{
outBindingSectionName = ConfigurationStrings.CustomBindingCollectionElementName;
}
}
// This little oddity exists to make sure that the out param is assigned to before the method
// exits.
bindingSectionName = outBindingSectionName;
return retval;
}
/// <securitynote>
/// Critical - uses SecurityCritical method UnsafeLookupCollection which elevates
/// Safe - does not leak config objects
/// </securitynote>
[SecurityCritical, SecurityTreatAsSafe]
void UpdateBindingSections()
{
ExtensionElementCollection bindingExtensions = ExtensionsSection.UnsafeLookupCollection(ConfigurationStrings.BindingExtensions, ConfigurationHelpers.GetEvaluationContext(this));
// Extension collections are additive only (BasicMap) and do not allow for <clear>
// or <remove> tags, nor do they allow for overriding an entry. This allows us
// to optimize this to only walk the binding extension collection if the counts
// mismatch.
if (bindingExtensions.Count != this.properties.Count)
{
foreach (ExtensionElement bindingExtension in bindingExtensions)
{
if (null != bindingExtension)
{
if (!this.properties.Contains(bindingExtension.Name))
{
ConfigurationProperty property = new ConfigurationProperty(bindingExtension.Name,
Type.GetType(bindingExtension.Type, true),
null,
ConfigurationPropertyOptions.None);
this.properties.Add(property);
}
}
}
}
}
/// <securitynote>
/// Critical - uses SecurityCritical method UnsafeGetAssociatedBindingCollectionElement which elevates
/// Safe - does not leak config objects
/// </securitynote>
[SecurityCritical, SecurityTreatAsSafe]
internal static void ValidateBindingReference(string binding, string bindingConfiguration, ContextInformation evaluationContext, ConfigurationElement configurationElement)
{
// ValidateBindingReference built on assumption that evaluationContext is valid.
// This should be protected at the callers site. If assumption is invalid, then
// configuration system is in an indeterminate state. Need to stop in a manner that
// user code can not capture.
if (null == evaluationContext)
{
DiagnosticUtility.DebugAssert("ValidateBindingReference() should only called with valid ContextInformation");
DiagnosticUtility.FailFast("ValidateBindingReference() should only called with valid ContextInformation");
}
if (!String.IsNullOrEmpty(binding))
{
BindingCollectionElement bindingCollectionElement = null;
if (null != evaluationContext)
{
bindingCollectionElement = ConfigurationHelpers.UnsafeGetAssociatedBindingCollectionElement(evaluationContext, binding);
}
else
{
bindingCollectionElement = ConfigurationHelpers.UnsafeGetBindingCollectionElement(binding);
}
if (bindingCollectionElement == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigInvalidSection,
ConfigurationHelpers.GetBindingsSectionPath(binding)),
configurationElement.ElementInformation.Source,
configurationElement.ElementInformation.LineNumber));
}
if (!String.IsNullOrEmpty(bindingConfiguration))
{
if (!bindingCollectionElement.ContainsKey(bindingConfiguration))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ConfigurationErrorsException(SR.GetString(SR.ConfigInvalidBindingName,
bindingConfiguration,
ConfigurationHelpers.GetBindingsSectionPath(binding),
ConfigurationStrings.BindingConfiguration),
configurationElement.ElementInformation.Source,
configurationElement.ElementInformation.LineNumber));
}
}
}
}
ContextInformation IConfigurationContextProviderInternal.GetEvaluationContext()
{
return this.EvaluationContext;
}
/// <securitynote>
/// RequiresReview - the return value will be used for a security decision -- see comment in interface definition
/// </securitynote>
ContextInformation IConfigurationContextProviderInternal.GetOriginalEvaluationContext()
{
DiagnosticUtility.DebugAssert("Not implemented: IConfigurationContextProviderInternal.GetOriginalEvaluationContext");
return null;
}
}
}
</remove></clear></bindingcollectionelement></bindingcollectionelement></bindingcollectionelement></string,></string,></string,>
I have a .net web application that for all intents and purposes of this question is CRUD with many different domain objects.
A common theme across theses objects is the need to know which value properties have been modified as well as child domain model properties. Currently we have two different systems in place for this.
The value properties is the one I am trying to sort out with this question.
Right now the models all inherit from the PersistableModel base that has these fields and methods of note:
private readonly List<string> _modifiedProperties = new List<string>();
public virtual ModelState State { get; set; }
public IEnumerable<string> ModifiedProperties { get { return _modifiedProperties; } }
protected bool HasModifiedProperties { get { return 0 < _modifiedProperties.Count; } }
public bool WasModified(string propertyName)
{
return _modifiedProperties.Contains(propertyName);
}
public void WasModified(string propertyName, bool modified)
{
if (modified)
{
if (!WasModified(propertyName)) _modifiedProperties.Add(propertyName);
}
else
{
_modifiedProperties.Remove(propertyName);
}
}
Then within each individual model whenever a property is set we also need to call WasModified with a string of the property name and a boolean value.
Obviously this is very tedious and error prone, what I want to do is redesign this base class to automatically add entries to the dictionary when a derived class's property is set.
In my research the closest I've been able to get is to use PostSharp which is out of the question.
While working on a different project I came up with a solution that gets most of the way towards my original goal.
Note that this solution is reliant upon the Dev Express ViewModelBase as its base class, but it wouldn't be hard to make a new base class with the features being used for non Dev Express projects:
Edit: I found an issue with the reset state logic, this update removes that issue.
public abstract class UpdateableModel : ViewModelBase
{
private static readonly MethodInfo GetPropertyMethod;
private static readonly MethodInfo SetPropertyMethod;
private readonly bool _trackingEnabled;
private readonly Dictionary<string, Tuple<Expression, object>> _originalValues;
private readonly List<string> _differingFields;
static UpdateableModel()
{
GetPropertyMethod = typeof(UpdateableModel).GetMethod("GetProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
SetPropertyMethod = typeof(UpdateableModel).GetMethod("SetProperty");
}
protected UpdateableModel(bool isNewModel)
{
_originalValues = new Dictionary<string, Tuple<Expression, object>>();
_differingFields = new List<string>();
if (isNewModel) return;
State = ModelState.Unmodified;
_trackingEnabled = true;
}
public ModelState State
{
get { return GetProperty(() => State); }
set { base.SetProperty(() => State, value); }
}
public new bool SetProperty<T>(Expression<Func<T>> expression, T value)
{
var wasUpdated = base.SetProperty(expression, value);
if (_trackingEnabled && wasUpdated)
{
UpdateState(expression, value);
}
return wasUpdated;
}
/// <summary>
/// Reset State is meant to be called when discarding changes, it will reset the State value to Unmodified and set all modified values back to their original value.
/// </summary>
public void ResetState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = SetPropertyMethod.MakeGenericMethod(type);
genericPropertySetter.Invoke(this, new[]{_originalValues[differingField].Item2});
}
}
/// <summary>
/// Update State is meant to be called after changes have been persisted, it will reset the State value to Unmodified and update the original values to the new values.
/// </summary>
public void UpdateState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = GetPropertyMethod.MakeGenericMethod(type);
var value = genericPropertySetter.Invoke(this, new object[] { _originalValues[differingField].Item1 });
var newValue = new Tuple<Expression, object>(_originalValues[differingField].Item1,value);
_originalValues[differingField] = newValue;
}
_differingFields.Clear();
State = ModelState.Unmodified;
}
private static Type GetFuncType(Expression expr)
{
var lambda = expr as LambdaExpression;
if (lambda == null)
{
return null;
}
var member = lambda.Body as MemberExpression;
return member != null ? member.Type : null;
}
private void UpdateState<T>(Expression<Func<T>> expression, T value)
{
var propertyName = GetPropertyName(expression);
if (!_originalValues.ContainsKey(propertyName))
{
_originalValues.Add(propertyName, new Tuple<Expression,object>(expression, value));
}
else
{
if (!Compare(_originalValues[propertyName].Item2, value))
{
_differingFields.Add(propertyName);
}
else if (_differingFields.Contains(propertyName))
{
_differingFields.Remove(propertyName);
}
State = _differingFields.Count == 0
? ModelState.Unmodified
: ModelState.Modified;
}
}
private static bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Another quick note, the IsNewModel constructor flag serves to differentiate objects create on the UI level which don't need any kind of state tracking, and objects generated from the data access level which will need the state tracking.
I am using ASP.NET Web Forms and C# in my application. I have a class file named Global.cs, in which I define variables using set and get properties. I use those variables anywhere on any pages by instantiating that class object.
Here is my Global.cs file:
using System;
using System.Data;
using System.Linq;
using System.Web;
/// <summary>
/// Contains my site's global variables.
/// </summary>
public static class Global
{
/// <summary>
/// Global variable storing important stuff.
/// </summary>
public static string gDate;
public static string gMobLength;
public static string gDateFormat;
public static string gApplicationNo;
public static string gBranchNo;
public static string gMemId;
public static string gIsEditable="false";
public static string gLoggedInUserName;
public static string ImportantData
{
get
{
return gDate;
}
set
{
gDate = value;
}
}
public static string MobileLength
{
get
{
return gMobLength;
}
set
{
gMobLength = value;
}
}
public static string DateFormat
{
get
{
return gDateFormat;
}
set
{
gDateFormat = value;
}
}
public static string ApplicationNo
{
get
{
return gApplicationNo;
}
set
{
gApplicationNo = value;
}
}
public static string BranchNo
{
get
{
return gBranchNo;
}
set
{
gBranchNo = value;
}
}
}
Is this a proper way of using variables throughout the project? What are the possible pros and cons with this approach and what approach would you guys take for using global variables?
First, I'd recommend using autoimplemented properties.
public static string BranchNo { get; set; }
Simplifies your code a bit. As to whether or not this is a good approach, it depends. Sometimes simple and straight-forward is better, and this falls into that category. If the values should not change once initialized, you may want to use a proper singleton with initialization:
public class Settings
{
private static Settings _current;
private static readonly object _lock = new object();
public static Settings Current
{
get
{
lock(_lock)
{
if (_current == null) throw new InvalidOperationException("Settings uninitialized");
return _current;
}
}
set
{
if (value == null) throw new ArgumentNullException();
if (_current != null) throw new InvalidOperationException("Current settings can only be set once.");
if (_current == null)
{
lock(_lock)
{
if (_current == null) _current = value;
}
}
}
}
public string ImportantData { get; private set; }
// etc.
}
Initializing settings:
Settings.Current = new Settings{ ImportantData = "blah blah blah"};
Accessing:
var data = Settings.Current.ImportantData;
Outside of the two bromides "globals are bad" and "properties are good" ... there's nothing intrinsically wrong with your approach. Go for it!
IMHO .. PSM
The reason why you do not see the variable after you instantiate that class object is because the variables are declared as static. Static variables are meant to be used by that manor ClassName.variableName or ClassName.PropertyName
I have been trying to get the Wolframalpha API for C# working to no avail. I have been trying to use these two resources:
Stack Question
Wolfram API demos
The answer in the post was semi helpful but I can't get anything to compile. I'm new to C# so its a bit overwhelming. I am really having trouble trying to just get it to accept input and then output the result.
If anyone could either help me get this code working so I can work with a valid example or knows of an example project that I can model from it would be appreciated.
This is the code I cut and pasted into a C# (Visual Studio) console project:
namespace WolframAlpha {
using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Net;
using System.IO;
public partial class DefaultPodEntity {
private String _PlainText;
private String _Img;
private String _Title;
private String _ParentTitle;
private Int16 _ParentPosition;
private String _ParentId;
public String PlainText {
get {
return this._PlainText;
}
set {
this._PlainText = value;
}
}
public String Img {
get {
return this._Img;
}
set {
this._Img = value;
}
}
public String Title {
get {
return this._Title;
}
set {
this._Title = value;
}
}
public String ParentTitle {
get {
return this._ParentTitle;
}
set {
this._ParentTitle = value;
}
}
public Int16 ParentPosition {
get {
return this._ParentPosition;
}
set {
this._ParentPosition = value;
}
}
public String ParentId {
get {
return this._ParentId;
}
set {
this._ParentId = value;
}
}
}
public partial class HtmlPodEntity {
private String _Markup;
private String _Title;
private Int16 _Position;
private String _Id;
private String _Css;
private String _Scripts;
public String Markup {
get {
return this._Markup;
}
set {
this._Markup = value;
}
}
public String Title {
get {
return this._Title;
}
set {
this._Title = value;
}
}
public Int16 Position {
get {
return this._Position;
}
set {
this._Position = value;
}
}
public String Id {
get {
return this._Id;
}
set {
this._Id = value;
}
}
public String Css {
get {
return this._Css;
}
set {
this._Css = value;
}
}
public String Scripts {
get {
return this._Scripts;
}
set {
this._Scripts = value;
}
}
}
public partial class PlainTextPodEntity {
private String _PlainText;
private String _Title;
private String _ParentTitle;
private Int16 _ParentPosition;
private String _ParentId;
public String PlainText {
get {
return this._PlainText;
}
set {
this._PlainText = value;
}
}
public String Title {
get {
return this._Title;
}
set {
this._Title = value;
}
}
public String ParentTitle {
get {
return this._ParentTitle;
}
set {
this._ParentTitle = value;
}
}
public Int16 ParentPosition {
get {
return this._ParentPosition;
}
set {
this._ParentPosition = value;
}
}
public String ParentId {
get {
return this._ParentId;
}
set {
this._ParentId = value;
}
}
}
public partial class WolframAlphaFactsContainer : System.Data.Services.Client.DataServiceContext {
public WolframAlphaFactsContainer(Uri serviceRoot) :
base(serviceRoot) {
}
/// <summary>
/// </summary>
/// <param name="Input">Query string Sample Values : weather|msft|derivative of x^4 sin x|SAT scores</param>
/// <param name="Location">Location used for computation Sample Values : Madrid|Springfield, IL</param>
/// <param name="LatitudeLongitude">Latitude/Longitude used for computation Sample Values : 40.42,-3.71|-22.54,-43.12</param>
/// <param name="Width">Width in pixels for images returned Sample Values : 300|500</param>
public DataServiceQuery<DefaultPodEntity> GetImageResults(String Input, String Location, String LatitudeLongitude, Int16? Width) {
if ((Input == null)) {
throw new System.ArgumentNullException("Input", "Input value cannot be null");
}
DataServiceQuery<DefaultPodEntity> query;
query = base.CreateQuery<DefaultPodEntity>("GetImageResults");
if ((Input != null)) {
query = query.AddQueryOption("Input", string.Concat("\'", Input, "\'"));
}
if ((Location != null)) {
query = query.AddQueryOption("Location", string.Concat("\'", Location, "\'"));
}
if ((LatitudeLongitude != null)) {
query = query.AddQueryOption("LatitudeLongitude", string.Concat("\'", LatitudeLongitude, "\'"));
}
if (((Width != null)
&& (Width.HasValue == true))) {
query = query.AddQueryOption("Width", Width.Value);
}
return query;
}
/// <summary>
/// </summary>
/// <param name="Input">Query string Sample Values : weather|msft|derivative of x^4 sin x|SAT scores</param>
/// <param name="Location">Location used for computation Sample Values : Madrid|Springfield, IL</param>
/// <param name="LatitudeLongitude">Latitude/Longitude used for computation Sample Values : 40.42,-3.71|-22.54,-43.12</param>
/// <param name="Width">Width in pixels for images returned Sample Values : 300|500</param>
public DataServiceQuery<HtmlPodEntity> GetHtmlResults(String Input, String Location, String LatitudeLongitude, Int16? Width) {
if ((Input == null)) {
throw new System.ArgumentNullException("Input", "Input value cannot be null");
}
DataServiceQuery<HtmlPodEntity> query;
query = base.CreateQuery<HtmlPodEntity>("GetHtmlResults");
if ((Input != null)) {
query = query.AddQueryOption("Input", string.Concat("\'", Input, "\'"));
}
if ((Location != null)) {
query = query.AddQueryOption("Location", string.Concat("\'", Location, "\'"));
}
if ((LatitudeLongitude != null)) {
query = query.AddQueryOption("LatitudeLongitude", string.Concat("\'", LatitudeLongitude, "\'"));
}
if (((Width != null)
&& (Width.HasValue == true))) {
query = query.AddQueryOption("Width", Width.Value);
}
return query;
}
/// <summary>
/// </summary>
/// <param name="Input">Query string Sample Values : weather|msft|derivative of x^4 sin x|SAT scores</param>
/// <param name="Location">Location used for computation Sample Values : Madrid|Springfield, IL</param>
/// <param name="LatitudeLongitude">Latitude/Longitude used for computation Sample Values : 40.42,-3.71|-22.54,-43.12</param>
/// <param name="Width">Width in pixels for images returned Sample Values : 300|500</param>
public DataServiceQuery<PlainTextPodEntity> GetPlainTextResults(String Input, String Location, String LatitudeLongitude, Int16? Width) {
if ((Input == null)) {
throw new System.ArgumentNullException("Input", "Input value cannot be null");
}
DataServiceQuery<PlainTextPodEntity> query;
query = base.CreateQuery<PlainTextPodEntity>("GetPlainTextResults");
if ((Input != null)) {
query = query.AddQueryOption("Input", string.Concat("\'", Input, "\'"));
}
if ((Location != null)) {
query = query.AddQueryOption("Location", string.Concat("\'", Location, "\'"));
}
if ((LatitudeLongitude != null)) {
query = query.AddQueryOption("LatitudeLongitude", string.Concat("\'", LatitudeLongitude, "\'"));
}
if (((Width != null)
&& (Width.HasValue == true))) {
query = query.AddQueryOption("Width", Width.Value);
}
return query;
}
}
}
This codeplex project claims to cover the latest Wolfram Alpha API and includes a sample:
http://wolframalphaapi20.codeplex.com/
Console applications use a static Main method as their entry point. This routine can normally be found in a file program.cs that is created automatically when a new project for a console application is created.
If the compiler says it can't find Main then it probably was deleted or was never created. Difficult to say without any code to look at. More errors may show when the issue with the Main method was resolved.
I am currently playing with a lib call WolframAlpha.NET. Code source is on github. There is a nuget package (Last published 2019-06-24).
Examples (from readme)
Here is the simplest form of getting data from Wolfram|Alpha:
static void Main(string[] args)
{
//First create the main class:
WolframAlpha wolfram = new WolframAlpha("APPID HERE");
//Then you simply query Wolfram|Alpha like this
//Note that the spelling error will be correct by Wolfram|Alpha
QueryResult results = wolfram.Query("Who is Danald Duck?");
//The QueryResult object contains the parsed XML from Wolfram|Alpha. Lets look at it.
//The results from wolfram is split into "pods". We just print them.
if (results != null)
{
foreach (Pod pod in results.Pods)
{
Console.WriteLine(pod.Title);
if (pod.SubPods != null)
{
foreach (SubPod subPod in pod.SubPods)
{
Console.WriteLine(subPod.Title);
Console.WriteLine(subPod.Plaintext);
}
}
}
}
}
For more examples, take a look at the WolframAlphaNet.Examples and WolframAlphaNet.Tests projects.
You have copy-pasted class definitions (like DefaultPodEntity and WolframAlphaFactsContainer) that allow you to interact with the Wolfram API, but you do not have a definition for the Main() function that defines what your program should be doing with those classes. You will need to add the method definition
static void Main(string[] args)
{
// TODO: call methods of WolframAlphaFactsContainer
}
to one of the classes (e.g. WolframAlphaFactsContainer or a new one, like Program, that is not listed in your question. Once this compiles, you need to replace the TODO comment with C# statements that specify how you are interacting with the WolframAlphaFactsContainer class (e.g. create an instance of that class and call its GetImageResults() method with the proper parameters).
Note: you will need to learn basic C# programming idioms before you can successfully tackle the problem of writing a working, correct program in C# that does what you want to do (as opposed to relying on other people's code).
Note: Read the documentation on Main() and how to pass command line parameters to your program (should you want to do that).
Note: the class WolframAlphaFactsContainer is marked partial, which means there might be other parts of this class (see documentation). If there are, you will need to include those in your code as well.
I know this post is old, but seeing as how it comes up in google near the top:
https://wapiex.codeplex.com/
This is the wrapper I just finished. It includes much more than the other codeplex project. Feel free to use it
I have a datagridView, that is bound to a List. This List is made up of my class which contains 2 public properties, a String Name, and another List CustomList. See below:
public class MyClass2
{
public string Name
{ get; set;}
public string Description
{
get;
set;
}
}
public class MyClass
{
List<MyClass2> myList;
public string Name
{
get;
set;
}
public List<MyClass2> CustomList
{
get { return myList ?? (myList= new List<MyClass2>()); }
}
}
And then in my designer page:
List<MyClass> myClassList = new List<MyClass>();
dataGridView.DataSource = myClassList;
As it is right now, the only column that appears in the grid, is the MyClass:Name column, and the CustomList column does not show up. What I'd like is the CustomList column to show and to display something like "Collection" with the "..." button showing, and when it is clicked to have the "Collection Editor" to popup.
Does anyone know if this is possible and how to enable it? If there's a tutorial or anything that would help me out I'd appreciate that too. Thanks.
Using generics, I think, is a clean solution:
public class Sorter<T>: IComparer<T>
{
public string Propiedad { get; set; }
public Sorter(string propiedad)
{
this.Propiedad = propiedad;
}
public int Compare(T x, T y)
{
PropertyInfo property = x.GetType().GetProperty(this.Propiedad);
if (property == null)
throw new ApplicationException("El objeto no tiene la propiedad " + this.Propiedad);
return Comparer.DefaultInvariant.Compare(property.GetValue(x, null), property.GetValue(y, null));
}
}
Usage example:
string orderBy = "propertyName";
bool orderAsc = true;
List<MyExampleClass> myClassList = someMethod();
if (!string.IsNullOrEmpty(orderBy))
{
myClassList.Sort(new Sorter<MyExampleClass>(orderBy));
if (!orderAsc) myClassList.Reverse();
}
Short answer: Yes, you can do it with some code.
Long answer: To write the code is gonna be a pain in the ass, as you would have to know not only how the DataGridView behaves with custom columns, but you would need to know how to expose design time elements at runtime, which requires quite a bit of plumbing. Extensive knowledge about the PropertyGrid must also be known.
Note: This might a fun component to write. (I might actually tackle it if I get some time)
So using the 'button' approach posted by Dave, and some code that I found that implements the CollectionEditor, I can edit the CustomList in MyClass2
Here's my solution, although not quite as clean as I'd like:
Put this class somewhere:
class MyHelper : IWindowsFormsEditorService, IServiceProvider, ITypeDescriptorContext
{
public static void EditValue(IWin32Window owner, object component, string propertyName)
{
PropertyDescriptor prop = TypeDescriptor.GetProperties(component)[propertyName];
if (prop == null) throw new ArgumentException("propertyName");
UITypeEditor editor = (UITypeEditor)prop.GetEditor(typeof(UITypeEditor));
MyHelper ctx = new MyHelper(owner, component, prop);
if (editor != null && editor.GetEditStyle(ctx) == UITypeEditorEditStyle.Modal)
{
object value = prop.GetValue(component);
value = editor.EditValue(ctx, ctx, value);
if (!prop.IsReadOnly)
{
prop.SetValue(component, value);
}
}
}
private readonly IWin32Window owner;
private readonly object component;
private readonly PropertyDescriptor property;
private MyHelper(IWin32Window owner, object component, PropertyDescriptor property)
{
this.owner = owner;
this.component = component;
this.property = property;
}
#region IWindowsFormsEditorService Members
public void CloseDropDown()
{
throw new NotImplementedException();
}
public void DropDownControl(System.Windows.Forms.Control control)
{
throw new NotImplementedException();
}
public System.Windows.Forms.DialogResult ShowDialog(System.Windows.Forms.Form dialog)
{
return dialog.ShowDialog(owner);
}
#endregion
#region IServiceProvider Members
public object GetService(Type serviceType)
{
return serviceType == typeof(IWindowsFormsEditorService) ? this : null;
}
#endregion
#region ITypeDescriptorContext Members
IContainer ITypeDescriptorContext.Container
{
get { return null; }
}
object ITypeDescriptorContext.Instance
{
get { return component; }
}
void ITypeDescriptorContext.OnComponentChanged()
{ }
bool ITypeDescriptorContext.OnComponentChanging()
{
return true;
}
PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor
{
get { return property; }
}
#endregion
Add a button column to the data grid:
DataGridViewButtonColumn butt = new DataGridViewButtonColumn();
butt.HeaderText = "CustomList";
butt.Name = "CustomList";
butt.Text = "Edit CustomList...";
butt.UseColumnTextForButtonValue = true;
dataGridView.Columns.Add(butt);
dataGridView.CellClick += new DataGridViewCellEventHandler(dataGridView_CellClick);
Then call it in the button handler of the cell click.
if (e.RowIndex < 0 || e.ColumnIndex != dataGridView.Columns["CustomList"].Index)
return;
//get the name of this column
string name = (string)dataGridView[dataGridView.Columns["Name"].Index, e.RowIndex].Value;
var myClassObject= myClassList.Find(o => o.Name == name);
MyHelper.EditValue(this, myClassObject, "CustomList");
I'd still be interested in hearing other approaches, and not having to implement my own CollectionEditor. And I'm still interested in having it look more like what the TabControl uses to add TabPages in the PropertyGrid...by displaying the "..." button...but this might work for now.
What you want to do is add a column template with a button in it:
http://geekswithblogs.net/carmelhl/archive/2008/11/11/126942.aspx
In the handler for the button, get the selected MyClass item from the collection and bind its list property to a grid in your popup.