Cannot Infer type in property getter - c#

I am working on a bindable base class that implements INotifyPropertyChanged and IDataErrorInfo so that I can write properties that bind to WPF with change notification and allow me to use DataAnnotations validation.
Kudos to this article: https://code.msdn.microsoft.com/windowsdesktop/Validation-in-MVVM-using-12dafef3 which I have copied from shamelessly
although the article is great, it doesn't take advantage of CallerMemberName so I'm trying to clean things up a bit.
One nifty thing the sample author did was to write SetValue and GetValue methods that store all private property values in a dictionary, which allows you to skip storing the property value in a private field in the class. The author used four functions to do this:
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(Expression<Func<T>> propertySelector, T value)
{
string propertyName = GetPropertyName(propertySelector);
SetValue<T>(propertyName, value);
}
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(string propertyName, T value)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
_values[propertyName] = value;
NotifyPropertyChanged(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(Expression<Func<T>> propertySelector)
{
string propertyName = GetPropertyName(propertySelector);
return GetValue<T>(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
object value;
if (!_values.TryGetValue(propertyName, out value))
{
value = default(T);
_values.Add(propertyName, value);
}
return (T)value;
}
I have replaced these four functions with the following two:
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(T value, [CallerMemberName] string propertyName = "")
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
_values[propertyName] = value;
NotifyPropertyChanged(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>([CallerMemberName] string propertyName = "")
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
object value;
if (!_values.TryGetValue(propertyName, out value))
{
value = default(T);
_values.Add(propertyName, value);
}
return (T)value;
}
I think it's an improvement because it eliminates a few functions and simplifies calling the methods.
A property using the original functions is implemented as follows:
[Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
public int Age
{
get { return GetValue(() => Age); }
set { SetValue(() => Age, value); }
}
I would like to implement the same property in mine as shown below:
[Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
public int Age
{
get { return GetValue(); }
set { SetValue(value); }
}
The only problem is that GetValue gives me the error:
The type arguments for method ___.GetValue(string)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
So I have to implement it this way:
[Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
public int Age
{
get { return GetValue<int>(); }
set { SetValue(value); }
}
Any Ideas? I can't see why the original code could infer the type but my code can't.

You can make GetValue's return type dynamic and it will be coerced back to the property type without error.

Related

How to check for no valid numbers in WPF with DataAnnotations

I'm working on a WPF application with MVVM pattern in which i use DataAnnotations for validation.
So i implemented a solution like that in this article.
Then i tried to add a property to my viewmodel - called "Age" - which shall only accept numbers and have a range between 1 and 100.
[RegularExpression(#"^[0-9]{1,3}$", ErrorMessage = "Please enter a valid number")]
[Range(1, 100, ErrorMessage = "Age should be between 1 to 100")]
/// <summary>
/// Gets or sets the age.
/// </summary>
public int Age
{
get { return GetValue(() => Age); }
set { SetValue(() => Age, value); }
}
And on my WPF window i got a textbox which is bound to Age:
<TextBox x:Name="tbx_age"
ToolTip="The age"
Text="{Binding Age, NotifyOnSourceUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
</TextBox>
As i'm starting the application the textbox is preallocated with zero ("0").
When i replace the zero in the textbox by "1a" i get the error message "The value '1a' cannot be converted".
This is no regular message from my code and i can't explain where it's coming from.
Have i made a mistake in the regular expression or something else?
I have uploaded my testproject to GitHub:
Repository
The project which i mean is named "Validation_DataAnnotations".
Thanks in advance!
This is the class PropertyChangedNotification which i use for notification and validation:
public abstract class PropertyChangedNotification : INotifyPropertyChanged, IDataErrorInfo
{
#region Fields
private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
#endregion
#region Protected
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(Expression<Func<T>> propertySelector, T value)
{
string propertyName = GetPropertyName(propertySelector);
SetValue<T>(propertyName, value);
}
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(string propertyName, T value)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
_values[propertyName] = value;
NotifyPropertyChanged(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(Expression<Func<T>> propertySelector)
{
string propertyName = GetPropertyName(propertySelector);
return GetValue<T>(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
object value;
if (!_values.TryGetValue(propertyName, out value))
{
value = default(T);
_values.Add(propertyName, value);
}
return (T)value;
}
/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name="propertyName">This instance property to validate.</param>
/// <returns>Relevant error string on validation failure or <see cref="System.String.Empty"/> on validation success.</returns>
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
string error = string.Empty;
var value = GetValue(propertyName);
var results = new List<System.ComponentModel.DataAnnotations.ValidationResult>(1);
var result = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
results);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
return error;
}
#endregion
#region Change Notification
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
string propertyName = GetPropertyName(propertySelector);
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion // INotifyPropertyChanged Members
#region Data Validation
string IDataErrorInfo.Error
{
get
{
throw new NotSupportedException("IDataErrorInfo.Error is not supported, use IDataErrorInfo.this[propertyName] instead.");
}
}
string IDataErrorInfo.this[string propertyName]
{
get
{
return OnValidate(propertyName);
}
}
#endregion
#region Privates
private string GetPropertyName(LambdaExpression expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
throw new InvalidOperationException();
}
return memberExpression.Member.Name;
}
private object GetValue(string propertyName)
{
object value;
if (!_values.TryGetValue(propertyName, out value))
{
var propertyDescriptor = TypeDescriptor.GetProperties(GetType()).Find(propertyName, false);
if (propertyDescriptor == null)
{
throw new ArgumentException("Invalid property name", propertyName);
}
value = propertyDescriptor.GetValue(this);
_values.Add(propertyName, value);
}
return value;
}
#endregion
#region Debugging
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
}
As an int property can never be set to anything else than an int value so your property setter will never be invoked if you set the Text property of the TextBox to "1a". No regular expression or data annotation in the world will solve this.
What you can do to customize the error message that appears when the WPF runtime tries to convert the value "1a" to an int before the property is set is to use a ValidationRule:
<TextBox>
<TextBox.Text>
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:StringToIntValidationRule ValidationStep="RawProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
...
</TextBox>
public class StringToIntValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
int i;
if (int.TryParse(value.ToString(), out i))
return new ValidationResult(true, null);
return new ValidationResult(false, "Please enter a valid integer value.");
}
}
There is a full example and more information about this available in the following blog post: https://blog.magnusmontin.net/2013/08/26/data-validation-in-wpf/
Note that it is not the responsibility of the view model to verify that its Age property is set to an int value. This is the responsibility of the control or the view.
You are binding your TextBox.TextProperty against the int-Property 'Age', thus WPF will already need to convert the string to an integer, in order to assign the value to your property.
If you want to do all the conversion yourself, you can bind to a string-Property instead.
Alternatively, you could just set ValidatesOnDataErrors=False in your binding, then non-numeric values will be silently ignored, numeric values will change the bound property.

C# Flagged Enums: how to append value

So, I have a flagged enum:
[Flags]
public enum EmailResult
{
ISPUSuccess = 1,
STSSuccess = 2
}
What I want to do is, via conditonals, set an enum var to one, the other, or both.
As in,
If (ISPU){
Result = EmailResult.ISPUSuccess
}
If (STS){
Result += EmailResult.STSSuccess
}
Like adding.. so it would effectively be
Result = EmailResult.ISPUSuccess | EmailResult.STSSuccess
You use the |= operator to set your values in an enum marked with the [Flags] attribute.
[Flags]
public enum EmailResult
{
None = 0,
ISPUSuccess = 1,
STSSuccess = 2
}
EmailResult result = EmailResult.None;
if(.... your condition ....)
result |= EmailResult.ISPUSuccess;
if( .... other condition ...)
result |= EmailResult.STSSuccess;
Console.WriteLine(result); // -> ISPUSuccess, STSSuccess
if((result & EmailResult.STSSuccess) != EmailResult.None)
..... flag is set ...
Notice that I have added another enum with value 0 to use in conditions where I need to check the current state of a particular flag.
You should use | (or |=) when working with flags. Imagine
Result = EmailResult.ISPUSuccess;
if (someCondition)
Result |= EmailResult.STSSuccess;
if (someOtherCondition)
Result |= EmailResult.STSSuccess;
if both someCondition and someOtherCondition are true you'll have a right result:
1 | 2 | 2 == 3 == 11 (binary)
in case of += however
Result = EmailResult.ISPUSuccess;
if (someCondition)
Result += EmailResult.STSSuccess;
if (someOtherCondition)
Result += EmailResult.STSSuccess;
you'll have
1 + 2 + 2 == 5 == 101 (binary)
which is wrong (please, notice that the second bit in the 101 is reset now).
I use the following generic struct for dealing with enums:
/// <summary>
/// This helper allows for easier manipulation of <see cref="Enum" /> objects with the <see cref="FlagsAttribute" />.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "Flags",
Justification = "This struct acts as a wrapper around Enum Flags, and relates to the term in question.")]
public struct Flags<T> : IEquatable<Flags<T>>, IEquatable<T>
{
#region Fields
private T _value;
#endregion
#region Constructors
/// <summary>
/// This constructor assures that the generic type of <typeparamref name="T" /> is first valid flags enum type.
/// </summary>
/// <exception cref="ArgumentException">Thrown if the type of <typeparamref name="T" /> is not a valid flag enum type.</exception>
[SuppressMessage("Microsoft.Usage", "CA2207:InitializeValueTypeStaticFieldsInline",
Justification = "This static constructor is not initializing any field values, instead, it is performing Type validation on the generic type T that cannot be otherwise performed.")]
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters",
MessageId = "DMFirmy.Library.Core.Utilities.Throw.InvalidArgumentIf(System.Boolean,System.String,System.String)",
Justification = "This library will not be localized.")]
static Flags()
{
var type = typeof(T);
Throw.InvalidArgumentIf(!type.IsEnum, "Flags<T> can only be used with enum types", nameof(T));
Throw.InvalidArgumentIf(type.GetCustomAttributes(typeof(FlagsAttribute), false).Length == 0, "Flags<T> can only be used with enum types with the Flags attribute", nameof(T));
}
private Flags(T value)
{
_value = value;
}
#endregion
#region Properties
private long ValueInLong
{
get { return GetLongValue(_value); }
set { _value = (T)Enum.ToObject(typeof(T), value); }
}
/// <summary>
/// Gets an enumerable collection of each of the individual flags that compose this instance.
/// </summary>
public IEnumerable<T> AllFlags
{
get
{
var values = (T[])Enum.GetValues(typeof(T));
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var f in values)
{
if (this[f]) yield return f;
}
}
}
#endregion
#region Indexers
/// <summary>
/// This indexer is used to add or check values within the stored flags.
/// </summary>
/// <param name="flags">The flags to search for.</param>
/// <returns>True if the flag is contained, flase otherwise.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
[IndexerName("Item")]
public bool this[T flags]
{
get
{
var flagsInLong = GetLongValue(flags);
return (ValueInLong & flagsInLong) == flagsInLong;
}
set
{
var flagsInLong = GetLongValue(flags);
if(value)
{
ValueInLong |= flagsInLong;
}
else
{
ValueInLong &= ~flagsInLong;
}
}
}
#endregion
#region Methods
/// <summary>
/// Returns true if the given <paramref name="flags" /> are completely contained.
/// </summary>
/// <param name="flags">The flags to check that the underlying value contains.</param>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
public bool Contains(T flags)
{
return this[flags];
}
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
[SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider",
MessageId = "System.Convert.ToInt64(System.Object)",
Justification = "This library will not be localized.")]
private static long GetLongValue(T flags)
{
return Convert.ToInt64(flags);
}
/// <summary>
/// Indicates whether this instance and first specified object are equal.
/// </summary>
/// <param name="obj">Another object to compare to.</param>
/// <returns>
/// <c>true</c> if <paramref name="obj" /> and this instance are the same type and represent the same value;
/// otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
// ReSharper disable once CanBeReplacedWithTryCastAndCheckForNull
if(obj is T)
{
return Equals((T)obj);
}
if(obj is Flags<T>)
{
return Equals((Flags<T>)obj);
}
return false;
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns><c>true</c> if the current object is equal to the other parameter; otherwise, <c>false</c>.</returns>
public bool Equals(Flags<T> other)
{
return Equals(other._value);
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns><c>true</c> if the current object is equal to the other parameter; otherwise, <c>false</c>.</returns>
public bool Equals(T other)
{
return Equals(_value, other);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return _value.GetHashCode();
}
/// <summary>
/// Returns the string representation of the underlying value.
/// </summary>
/// <returns>The string representation of the underlying value</returns>
public override string ToString()
{
return _value.ToString();
}
#endregion
#region Operators
/// <summary>
/// Implicit conversion from <see cref="Flags{T}" /> to the underlying <typeparamref name="T" /> value.
/// </summary>
/// <param name="flags">The <see cref="Flags{T}" /> value.</param>
/// <returns>
/// The <typeparamref name="T" /> value represented by <paramref name="flags" />
/// </returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
public static implicit operator T(Flags<T> flags)
{
return flags._value;
}
/// <summary>
/// Implicit conversion from the underlying <typeparamref name="T" /> to first <see cref="Flags{T}" /> value.
/// </summary>
/// <param name="flags">The <typeparamref name="T" /> value.</param>
/// <returns>The value of <paramref name="flags" /> as first <see cref="Flags{T}" />.</returns>
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms",
MessageId = "flags",
Justification = "This struct is first wrapper around Enum Flags, and relates to the term in question.")]
public static implicit operator Flags<T>(T flags)
{
return new Flags<T>(flags);
}
/// <summary>
/// Compares two <see cref="Flags{T}" /> instances to determine if they have equal values.
/// </summary>
/// <param name="first">The first <see cref="Flags{T}" />.</param>
/// <param name="second">The second <see cref="Flags{T}" />.</param>
/// <returns><c>true</c> if <paramref name="first" /> and <paramref name="second" /> are equal, <c>false</c> otherwise.</returns>
public static bool operator ==(Flags<T> first, Flags<T> second)
{
return first.Equals(second);
}
/// <summary>
/// Compares two <see cref="Flags{T}" /> instances to determine if they do not have equal values.
/// </summary>
/// <param name="first">The first <see cref="Flags{T}" />.</param>
/// <param name="second">The second <see cref="Flags{T}" />.</param>
/// <returns>
/// <c>true</c> if <paramref name="first" /> and <paramref name="second" /> are not equal, <c>false</c> if they
/// are.
/// </returns>
public static bool operator !=(Flags<T> first, Flags<T> second)
{
return !(first == second);
}
#endregion
}
This struct lets you use your flags enum like an indexed enumerable:
[Flags]
enum MyFlags
{
None = 0,
Value1 = 1,
Value2 = 2,
Value3 = 4
}
Flags<MyFlags> flags = MyFlags.Value1 | MyFlags.Value3;
bool hasValue1 = flags[MyFlags.Value1]; // true
bool hasValue2 = flags[MyFlags.Value2]; // false
bool hasValue3 = flags[MyFlags.Value3]; // true
flags[MyFlags.Value2] = true;
flags[MyFlags.Vlaue1] = false;
hasValue1 = flags[MyFlags.Value1]; // false
hasValue2 = flags[MyFlags.Value2]; // true
hasValue3 = flags[MyFlags.Value3]; // true
bool containsFlag = flags.Contains(MyFlags.Value2 | MyFlags.Value3); // true

Achieve different SELECT and INSERT behavior on NHibernate IUserType

Is there a way to separate the Create (INSERT) behavior from the SELECT behavior.
Let's say I have a database with a column that will return a string looking like this
Predecessors = "1,3,4,5"
In my application I want to use this string like an int array by implementing an IUserType
public interface IIntArray
{
int[] Items { get; set; }
}
public class IntArray : IIntArray
{
public int[] Items { get; set; }
public IntArray(string item)
{
if(string.IsNullOrEmpty(item))
return;
Items = System.Array.ConvertAll<string, int>(item.Split(new[] {','}), int.Parse);
// for older .net versions use the code below
// Items = Array.ConvertAll<string, int>(item.ToString().Split(new[] { ',' }), delegate(string str) { return int.Parse(str); });
}
}
public class IntArrayType : IUserType
{
#region Implementation of IUserType
/// <summary>
/// Compare two instances of the class mapped by this type for persistent "equality"
/// ie. equality of persistent state
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public new bool Equals(object x, object y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.GetType() == y.GetType();
}
/// <summary>
/// Get a hashcode for the instance, consistent with persistence "equality"
/// </summary>
public int GetHashCode(object x)
{
return x.GetHashCode();
}
/// <summary>
/// Retrieve an instance of the mapped class from a resultset.
/// Implementors should handle possibility of null values.
/// </summary>
/// <param name="rs">a IDataReader</param>
/// <param name="names">column names</param>
/// <param name="owner">the containing entity</param>
/// <returns></returns>
/// <exception cref="HibernateException">HibernateException</exception>
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var value = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (value == null || (string.IsNullOrEmpty(value.ToString())))
{
return null;
}
return new IntArray(value.ToString());
}
/// <summary>
/// Write an instance of the mapped class to a prepared statement.
/// Implementors should handle possibility of null values.
/// A multi-column type should be written to parameters starting from index.
/// </summary>
/// <param name="cmd">a IDbCommand</param>
/// <param name="value">the object to write</param>
/// <param name="index">command parameter index</param>
/// <exception cref="HibernateException">HibernateException</exception>
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
}
else
{
var state = (IIntArray)value;
((IDataParameter)cmd.Parameters[index]).Value = state.GetType().Name;
}
}
/// <summary>
/// Return a deep copy of the persistent state, stopping at entities and at collections.
/// </summary>
/// <param name="value">generally a collection element or entity field</param>
/// <returns>a copy</returns>
public object DeepCopy(object value)
{
return value;
}
/// <summary>
/// During merge, replace the existing (<paramref name="target" />) value in the entity
/// we are merging to with a new (<paramref name="original" />) value from the detached
/// entity we are merging. For immutable objects, or null values, it is safe to simply
/// return the first parameter. For mutable objects, it is safe to return a copy of the
/// first parameter. For objects with component values, it might make sense to
/// recursively replace component values.
/// </summary>
/// <param name="original">the value from the detached entity being merged</param>
/// <param name="target">the value in the managed entity</param>
/// <param name="owner">the managed entity</param>
/// <returns>the value to be merged</returns>
public object Replace(object original, object target, object owner)
{
return original;
}
/// <summary>
/// Reconstruct an object from the cacheable representation. At the very least this
/// method should perform a deep copy if the type is mutable. (optional operation)
/// </summary>
/// <param name="cached">the object to be cached</param>
/// <param name="owner">the owner of the cached object</param>
/// <returns>a reconstructed object from the cachable representation</returns>
public object Assemble(object cached, object owner)
{
return cached;
}
/// <summary>
/// Transform the object into its cacheable representation. At the very least this
/// method should perform a deep copy if the type is mutable. That may not be enough
/// for some implementations, however; for example, associations must be cached as
/// identifier values. (optional operation)
/// </summary>
/// <param name="value">the object to be cached</param>
/// <returns>a cacheable representation of the object</returns>
public object Disassemble(object value)
{
return value;
}
/// <summary>
/// The SQL types for the columns mapped by this type.
/// </summary>
public SqlType[] SqlTypes { get { return new[] { NHibernateUtil.String.SqlType }; } }
/// <summary>
/// The type returned by <c>NullSafeGet()</c>
/// </summary>
public Type ReturnedType { get { return typeof(IntArray); } }
/// <summary>
/// Are objects of this type mutable?
/// </summary>
public bool IsMutable { get { return false; } }
#endregion
}
In my NHibernate class I mapped to property like this
public virtual IntArray Predecessors { get; set; }
And the hbm mapping
<property name="Predecessors" type="Example.IntArrayType, Example" />
The IntArray class does it's job when reading data but when trying to put something back this doesn't work. What I would like to do is to somehow force the IntArray property to render the values of IntArray.Items to a comma separated string
string magic = string.Join(",", Predecessors.Items);
Thanks
Something like this should do the trick
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var ints = value as IntArray;
if(ints != null && ints.Items != null)
{
NHibernate.NHibernateUtil.StringClob.NullSafeSet(cmd, string.Join(", ", ints.Items), index);
}
}

Mapping System.Drawing.Color

If I don't map Color but map an object that has a Color attribute, FluentNHibernate successfully maps it to a varbinary(max). However, that's extremely inefficient given that realistically Color is just composed of 4 bytes and I am keen on improving it without using a new type to Proxy it.
Internally within Color it is made up of four properties,
long value (cast down to int to represent ARGB)
short state, indicates if this is a known & valid colour
string name, the name of the colour if known.
short knownColor a value to indicate which known colour it is
So I have attempted to map this as follows.
public ColorMapping()
{
CompositeId()
.KeyProperty(c => Reveal.Member<Color, long>("value"))
.KeyProperty(c => Reveal.Member<Color, short>("state"))
.KeyProperty(c => Reveal.Member<Color, string>("name"))
.KeyProperty(c => Reveal.Member<Color, short>("knownColor"));
}
However, on usage I get the following exception,
Class Initialization method DataContextTest.ClassInitialise threw
exception. FluentNHibernate.Cfg.FluentConfigurationException:
FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory.
Check PotentialReasons collection, and InnerException for more detail.
---> FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory.
Check PotentialReasons collection, and InnerException for more detail.
---> NHibernate.MappingException: Could not compile the mapping
document: (XmlDocument) ---> NHibernate.MappingException: Could not
determine type for:
System.Linq.Expressions.Expression1[[System.Func2[[System.Drawing.Color,
System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a],[System.Int64, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089, for columns:
NHibernate.Mapping.Column(Member).
Am I misusing Reveal? If so, how am I meant to use it?
i would map it as UserType which converts it back and forth
// in mapping
Map(x => x.Color).Column("ColorARGB").CustomType<ColorUserType>();
[Serializable]
class ColorUserType : IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
bool IUserType.Equals(object x, object y)
{
var colorX = x as Color;
var colorY = y as Color;
return colorX == null ? colorY = null : colorX.ToArgb() == colorY.ToArgb();
}
public virtual int GetHashCode(object x)
{
var colorX = (Color)x;
return (colorX != null) ? colorX.ToArgb() : 0;
}
public bool IsMutable { get { return false; } }
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
return Color.FromArgb((int)NHibernateUtil.Int32.Get(rs, names[0]));
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.Int32.Set(cmd, ((Color)value).ToArgb(), index);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType { get { return typeof(Color); } }
public SqlType[] SqlTypes { get { return new []{ SqlTypeFactory.Int32 }; } }
}
I went with an ICompositeUserType in the end.
Code as follows,
public class ColorType : ICompositeUserType
{
/// <summary>
/// Get the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"/>
/// <returns>
/// the property value
/// </returns>
public object GetPropertyValue(object component, int property)
{
var color = (Color) component;
if (property == 0)
{
return color.ToArgb();
}
return (int) color.ToKnownColor();
}
/// <summary>
/// Set the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"/>
/// <param name="value">the value to set</param>
public void SetPropertyValue(object component, int property, object value)
{
throw new InvalidOperationException("Color is immutable");
}
/// <summary>
/// Compare two instances of the class mapped by this type for persistence
/// "equality", ie. equality of persistent state.
/// </summary>
/// <param name="x"/><param name="y"/>
/// <returns/>
public new bool Equals(object x, object y)
{
return ReferenceEquals(x, y) ||
x != null && y != null &&
object.Equals(x, y);
}
/// <summary>
/// Get a hashcode for the instance, consistent with persistence "equality"
/// </summary>
public int GetHashCode(object x)
{
return x == null
? 0
: x.GetHashCode();
}
/// <summary>
/// Retrieve an instance of the mapped class from a IDataReader. Implementors
/// should handle possibility of null values.
/// </summary>
/// <param name="dr">IDataReader</param>
/// <param name="names">the column names</param>
/// <param name="session"/>
/// <param name="owner">the containing entity</param>
/// <returns/>
public object NullSafeGet(IDataReader dr, string[] names,
ISessionImplementor session, object owner)
{
var argb = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[0]);
var knownColor = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[1]);
return knownColor != null
? Color.FromKnownColor((KnownColor) knownColor.Value)
: Color.FromArgb(argb.Value);
}
/// <summary>
/// Write an instance of the mapped class to a prepared statement.
/// Implementors should handle possibility of null values.
/// A multi-column type should be written to parameters starting from index.
/// If a property is not settable, skip it and don't increment the index.
/// </summary>
/// <param name="cmd"/>
/// <param name="value"/>
/// <param name="index"/>
/// <param name="settable"/>
/// <param name="session"/>
public void NullSafeSet(IDbCommand cmd, object value, int index,
bool[] settable, ISessionImplementor session)
{
var color = (Color) value;
if (color.IsKnownColor)
{
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
((IDataParameter) cmd.Parameters[index + 1]).Value = (int) color.ToKnownColor();
}
else
{
((IDataParameter) cmd.Parameters[index]).Value = color.ToArgb();
((IDataParameter) cmd.Parameters[index + 1]).Value = DBNull.Value;
}
}
/// <summary>
/// Return a deep copy of the persistent state, stopping at entities and at collections.
/// </summary>
/// <param name="value">generally a collection element or entity field</param>
/// <returns/>
public object DeepCopy(object value)
{
return value;
}
/// <summary>
/// Transform the object into its cacheable representation.
/// At the very least this method should perform a deep copy.
/// That may not be enough for some implementations,
/// method should perform a deep copy. That may not be enough for
/// some implementations, however; for example, associations must
/// be cached as identifier values. (optional operation)
/// </summary>
/// <param name="value">the object to be cached</param>
/// <param name="session"/>
/// <returns/>
public object Disassemble(object value, ISessionImplementor session)
{
return value;
}
/// <summary>
/// Reconstruct an object from the cacheable representation.
/// At the very least this method should perform a deep copy. (optional operation)
/// </summary>
/// <param name="cached">the object to be cached</param>
/// <param name="session"/>
/// <param name="owner"/>
/// <returns/>
public object Assemble(object cached, ISessionImplementor session, object owner)
{
return cached;
}
/// <summary>
/// During merge, replace the existing (target) value in the entity we are merging to
/// with a new (original) value from the detached entity we are merging. For immutable
/// objects, or null values, it is safe to simply return the first parameter. For
/// mutable objects, it is safe to return a copy of the first parameter. However, since
/// composite user types often define component values, it might make sense to recursively
/// replace component values in the target object.
/// </summary>
public object Replace(object original, object target, ISessionImplementor session, object owner)
{
return original;
}
/// <summary>
/// Get the "property names" that may be used in a query.
/// </summary>
public string[] PropertyNames { get { return new[] {"Argb", "KnownColor"}; } }
/// <summary>
/// Get the corresponding "property types"
/// </summary>
public IType[] PropertyTypes
{
get
{
return new IType[]
{
NHibernateUtil.Int32, NHibernateUtil.Int32
};
}
}
/// <summary>
/// The class returned by NullSafeGet().
/// </summary>
public Type ReturnedClass { get { return typeof (Color); } }
/// <summary>
/// Are objects of this type mutable?
/// </summary>
public bool IsMutable { get { return false; } }
}

How to Cast to Generic Parameter in C#?

I'm trying to write a generic method for fetching an XElement value in a strongly-typed fashion. Here's what I have:
public static class XElementExtensions
{
public static XElement GetElement(this XElement xElement, string elementName)
{
// Calls xElement.Element(elementName) and returns that xElement (with some validation).
}
public static TElementType GetElementValue<TElementType>(this XElement xElement, string elementName)
{
XElement element = GetElement(xElement, elementName);
try
{
return (TElementType)((object) element.Value); // First attempt.
}
catch (InvalidCastException originalException)
{
string exceptionMessage = string.Format("Cannot cast element value '{0}' to type '{1}'.", element.Value,
typeof(TElementType).Name);
throw new InvalidCastException(exceptionMessage, originalException);
}
}
}
As you can see on the First attempt line of GetElementValue, I'm trying to go from string -> object -> TElementType. Unfortunately, this does not work for an integer test case. When running the following test:
[Test]
public void GetElementValueShouldReturnValueOfIntegerElementAsInteger()
{
const int expectedValue = 5;
const string elementName = "intProp";
var xElement = new XElement("name");
var integerElement = new XElement(elementName) { Value = expectedValue.ToString() };
xElement.Add(integerElement);
int value = XElementExtensions.GetElementValue<int>(xElement, elementName);
Assert.AreEqual(expectedValue, value, "Expected integer value was not returned from element.");
}
I get the following exception when GetElementValue<int> is called:
System.InvalidCastException : Cannot cast element value '5' to type 'Int32'.
Am I going to have to handle each casting case (or at least the numeric ones) separately?
You could also try the Convert.ChangeType
Convert.ChangeType(element.Value, typeof(TElementType))
From your code, instead of:
return (TElementType)((object) element.Value);
you'd do this:
return (TElementType) Convert.ChangeType(element.Value, typeof (T));
The only caveat here is that TElementType must implement IConvertible. However, if you're just talking about intrinsic types, they all implement that already.
For your custom types, assuming you wanted them in here, you'd have to have your own conversion.
You can't do an implicit or explicit cast from String to Int32, you need use Int32's Parse or TryParse methods for this. You could probably create some nifty extension methods, e.g:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
/// <summary>
/// Provides extension methods for strings.
/// </summary>
public static class StringExtensions
{
#region Methods
/// <summary>
/// Converts the specified string to a <see cref="Boolean"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="Boolean"/>.</returns>
public static bool AsBoolean(this string #string)
{
return bool.Parse(#string);
}
/// <summary>
/// Converts the specified string to a <see cref="Boolean"/> using TryParse.
/// </summary>
/// <remarks>
/// If the specified string cannot be parsed, the default value (if valid) or false is returned.
/// </remarks>
/// <param name="string">The string to convert.</param>
/// <param name="default">The default value for if the value cannot be parsed.</param>
/// <returns>The specified string as a <see cref="DateTime"/>.</returns>
public static bool AsBooleanNonStrict(this string #string, bool? #default = null)
{
bool #bool;
if ((!string.IsNullOrEmpty(#string)) && bool.TryParse(#string, out #bool))
return #bool;
if (#default.HasValue)
return #default.Value;
return false;
}
/// <summary>
/// Converts the specified string to a <see cref="DateTime"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="DateTime"/>.</returns>
public static DateTime AsDateTime(this string #string)
{
return DateTime.Parse(#string);
}
/// <summary>
/// Converts the specified string to a <see cref="DateTime"/> using TryParse.
/// </summary>
/// <remarks>
/// If the specified string cannot be parsed, <see cref="DateTime.MinValue"/> is returned.
/// </remarks>
/// <param name="string">The string to convert.</param>
/// <param name="default">The default value for if the value cannot be parsed.</param>
/// <returns>The specified string as a <see cref="DateTime"/>.</returns>
public static DateTime AsDateTimeNonStrict(this string #string, DateTime? #default = null)
{
DateTime datetime;
if ((!string.IsNullOrEmpty(#string)) && DateTime.TryParse(#string, out datetime))
return datetime;
if (#default.HasValue)
return #default.Value;
return DateTime.MinValue;
}
/// <summary>
/// Converts the specified string to a <see cref="TEnum"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="TEnum"/>.</returns>
public static TEnum AsEnum<TEnum>(this string #string) where TEnum : struct
{
return (TEnum)Enum.Parse(typeof(TEnum), #string);
}
/// <summary>
/// Converts the specified string to a <see cref="TEnum"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="TEnum"/>.</returns>
public static TEnum AsEnumNonStrict<TEnum>(this string #string, TEnum #default) where TEnum : struct
{
TEnum #enum;
if ((!string.IsNullOrEmpty(#string)) && Enum.TryParse(#string, out #enum))
return #enum;
return #default;
}
/// <summary>
/// Converts the specified string to a <see cref="Int32"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="Int32"/>.</returns>
public static int AsInteger(this string #string)
{
return int.Parse(#string);
}
/// <summary>
/// Converts the specified string to a <see cref="Int32"/> using TryParse.
/// </summary>
/// <remarks>
/// If the specified string cannot be parsed, the default value (if valid) or 0 is returned.
/// </remarks>
/// <param name="string">The string to convert.</param>
/// <param name="default">The default value for if the value cannot be parsed.</param>
/// <returns>The specified string as a <see cref="Int32"/>.</returns>
public static int AsIntegerNonStrict(this string #string, int? #default = null)
{
int #int;
if ((!string.IsNullOrEmpty(#string)) && int.TryParse(#string, out #int))
return #int;
if (#default.HasValue)
return #default.Value;
return 0;
}
/// <summary>
/// Converts the specified string to a <see cref="bool"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="DateTime"/>.</returns>
public static bool? AsNullableBolean(this string #string)
{
bool #bool;
if ((string.IsNullOrEmpty(#string)) || !bool.TryParse(#string, out #bool))
return null;
return #bool;
}
/// <summary>
/// Converts the specified string to a <see cref="DateTime"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="DateTime"/>.</returns>
public static DateTime? AsNullableDateTime(this string #string)
{
DateTime dateTime;
if ((string.IsNullOrEmpty(#string)) || !DateTime.TryParse(#string, out dateTime))
return null;
return dateTime;
}
/// <summary>
/// Converts the specified string to a <see cref="DateTime"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="DateTime"/>.</returns>
public static TEnum? AsNullableEnum<TEnum>(this string #string) where TEnum : struct
{
TEnum #enum;
if ((string.IsNullOrEmpty(#string)) || !Enum.TryParse(#string, out #enum))
return null;
return #enum;
}
/// <summary>
/// Converts the specified string to a <see cref="Int32"/>
/// </summary>
/// <param name="string">The string to convert.</param>
/// <returns>The specified string as a <see cref="Int32"/>.</returns>
public static int? AsNullableInteger(this string #string)
{
int #int;
if ((string.IsNullOrEmpty(#string)) || !int.TryParse(#string, out #int))
return null;
return #int;
}
#endregion
}
I typically use these quite often.
In C# you can't cast string object to Int32. For example this code produces compilation error:
string a = "123.4";
int x = (int) a;
Try to use Convert class or Int32.Parse and Int32.TryParse methods if you want such functionality.
And yes, you should handle numeric casting separately, if you want to cast string to int.
string also implements IConvertible, so you can do the following.
((IConvertible)mystring).ToInt32(null);
You can even throw some extension methods around it to make it cleaner.

Categories

Resources