I have created class that intend to check is Generic T meets all the required specifications. Need help to agregate them and return boolean value.
Here is the base specification class
/// <summary>
/// Base spec
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class BaseSpecification<T>
{
public abstract Expression<Func<T, bool>> ToExpression();
/// <summary>
/// Meets
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public bool Meets(T entity)
{
Func<T, bool> predicate = ToExpression().Compile();
return predicate(entity);
}
}
And concrete specification that inhereits from base:
public class DeviceIsActiveSpecification : BaseSpecification<Device>
{
public override Expression<Func<Device, bool>> ToExpression()
{
//Устройство поддерживает выполнение команд (Активное)
return device => device.DeviceActivityType == DeviceActivityType.Active;
}
}
And the validator class that has to check is T meets all the cpecifications:
public class SpecificationValidator<T>
{
/// <summary>
/// Cpec list
/// </summary>
private readonly IList<BaseSpecification<T>> _specifications2Meet;
public SpecificationValidator()
{
_specifications2Meet = new List<BaseSpecification<T>>();
}
/// <summary>
/// Add cpec
/// </summary>
/// <typeparam name="TSecification"></typeparam>
/// <returns></returns>
public SpecificationValidator<T> Add<TSecification>() where TSecification : BaseSpecification<T>, new()
{
return Add(new TSecification());
}
/// <summary>
///
/// </summary>
/// <param name="specification"></param>
/// <returns></returns>
SpecificationValidator<T> Add(BaseSpecification<T> specification)
{
if (specification == null) throw new ArgumentNullException(nameof(specification));
_specifications2Meet.Add(specification);
return this;
}
/// <summary>
/// Meets all
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public bool MeetsAllSpecifications (T source)
{
??? Need help here to agregate "Meets"
}
}
Intended usage:
var validator = new SpecificationValidator<Device>()
.Add<DeviceIsActiveSpecification>()
.Add<CommunicationDeviceSpecification>()
.MeetsAllSpecifications(device);
Any help is appreciated! Thanks!
Maybe I missed something, but it seems you can simply use LINQ's All here:
public bool MeetsAllSpecifications (T source)
{
return specifications2Meet.All(spec => spec.Meets(source));
}
Related
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
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);
}
}
I'm setting up a generic repository in my application and I'm having troubles with some LINQ queries.
With a non-generic one, this is what I would do:
private IObjectSet<T> _objectSet;
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
var r = (from n in _objectSet
select n.Group_ID).Distinct();
objectGroups = r.OrderBy(n => n.Value).ToList();
foreach (object value in objectGroups)
{
intGroups.Add((int?)value);
}
return intGroups;
}
Since this is a generic one of type T, when typing "n.", IntelliSense is not listing any options since the type of n is not explicitly defined (right?).
So here's what I have so far:
public interface IRepository<T> : IDisposable where T : class
{
IQueryable<T> Fetch();
IEnumerable<T> GetAll();
IEnumerable<T> GetAll(bool activeOnly);
IEnumerable<T> GetAll(string groupID, bool activeOnly);
IEnumerable<T> Find(Func<T, bool> predicate);
T Single(Func<T, bool> predicate);
T First(Func<T, bool> predicate);
List<int?> GetGroups();
int Add(T entity);
void Delete(T entity);
void Attach(T entity);
void SaveChanges();
void SaveChanges(SaveOptions options);
}
public class DataRepository<T> : IRepository<T> where T : class
{
/// <summary>
/// The context object for the database
/// </summary>
private ObjectContext _context;
private IEnumerable<T> _previousEntries;
private string _PKName;
/// <summary>
/// The IObjectSet that represents the current entity.
/// </summary>
private IObjectSet<T> _objectSet;
public DataRepository()
{
switch (typeof(T).Name)
{
case "CRM_Patient":
_context = new TheseEntities();
_PKName = "key_patient";
break;
case "CRM_Account":
_context = new ThoseEntities();
_PKName = "accountnumber";
break;
case "CRM_Supplier":
_context = new OtherEntities();
_PKName = "supplierid";
break;
default:
_context = new OtherEntities();
break;
}
_objectSet = _context.CreateObjectSet<T>();
_previousEntries = this.GetAll();
}
/// <summary>
/// Gets all records as an IQueryable
/// </summary>
/// <returns>An IQueryable object containing the results of the query</returns>
public IQueryable<T> Fetch()
{
return _objectSet;
}
/// <summary>
/// Gets all records as an IEnumberable
/// </summary>
/// <returns>An IEnumberable object containing the results of the query</returns>
public IEnumerable<T> GetAll()
{
return Fetch().AsEnumerable();
}
/// <summary>
/// Finds a record with the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A collection containing the results of the query</returns>
public IEnumerable<T> Find(Func<T, bool> predicate)
{
return _objectSet.Where<T>(predicate);
}
public List<int?> GetGroups()
{
List<object> objectGroups = new List<object>();
List<int?> intGroups = new List<int?>();
//var r = (from n in _objectSet
// select n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
var r = Fetch().Select(n => n.GetType().GetProperty("Group_ID").GetValue(n, null)).Distinct();
objectGroups = r.OrderBy(n => n.GetType().GetProperty("Value").GetValue(n, null)).ToList();
foreach (object value in r)
{
intGroups.Add((int?)value);
}
return intGroups;
}
/// <summary>
/// Gets a single record by the specified criteria (usually the unique identifier)
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record that matches the specified criteria</returns>
public T Single(Func<T, bool> predicate)
{
return _objectSet.Single<T>(predicate);
}
/// <summary>
/// The first record matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
/// <returns>A single record containing the first record matching the specified criteria</returns>
public T First(Func<T, bool> predicate)
{
return _objectSet.First<T>(predicate);
}
/// <summary>
/// Deletes the specified entitiy
/// </summary>
/// <param name="entity">Entity to delete</param>
/// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
public void Delete(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_objectSet.DeleteObject(entity);
}
/// <summary>
/// Deletes records matching the specified criteria
/// </summary>
/// <param name="predicate">Criteria to match on</param>
public void Delete(Func<T, bool> predicate)
{
IEnumerable<T> records = from x in _objectSet.Where<T>(predicate) select x;
foreach (T record in records)
{
_objectSet.DeleteObject(record);
}
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
Somehow, my reflection tricks seem to be executed in the LINQ statements, which obviously ends up throwing an exception when I assign a value for objectGroups.
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object, System.Object[])' method, and this method cannot be translated into a store expression.
Any ideas ? I really need to preserve the generic nature of the repository. All of the methods where I'm using Reflection are throwing exceptions...
Thanks much.
EDIT: Added most of the Repository's methods and the interface. Some methods might be missing from the class but done so to lighten reading :)
How about defining the Value and GroupID properties in an interface, and then making your entities implement this interface and adding this as a generic constraint?
Then your GetGroups method will be able to call these properties without reflection.
I get how to use dynamic in C# 4.0, however, I'm not sure how to take something and make it dynamic-able (my technical term).
For example, instead of ConfigurationManager.AppSettings["blah"], how can I make a wrapper of sorts that will let me just use it like a dynamic: settings.Blah ?
You still need an entry point. However, from there the possibilities are quite flexible. This is an example idea to demonstrate how powerful dynamic dispatch can be:
public abstract class MyBaseClass
{
public dynamic Settings
{
get { return _settings; }
}
private SettingsProxy _settings = new SettingsProxy();
private class SettingsProxy : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var setting = ConfigurationManager.AppSettings[binder.Name];
if(setting != null)
{
result = setting.ToString();
return true;
}
result = null;
return false;
}
}
}
New version with generics !!
public static class WebConfiguration<T>
{
public static dynamic Get {
get { return _settings; }
}
static readonly SettingsProxy _settings = new SettingsProxy ();
class SettingsProxy : DynamicObject
{
public override bool TryGetMember (GetMemberBinder binder, out object result)
{
var setting = ConfigurationManager.AppSettings [binder.Name];
if (setting != null) {
result = Convert.ChangeType (setting, typeof(T));
return true;
}
result = null;
return false;
}
}
}
Usage
WebConfiguration<int>.Get.UserNameLength
Here, let me show you more powers of dynamic dispatch:
/// <summary>
/// Dynamic wrapper for <see cref="AppSettingsSection"/>.
/// </summary>
public class DynamicAppSettings : DynamicObject
{
private string _exeConfigPath;
private System.Configuration.Configuration _config;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicAppSettings"/> class with configuration file
/// of the current application domain.
/// </summary>
public DynamicAppSettings()
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DynamicAppSettings"/> class with the specified configuration file.
/// </summary>
/// <param name="exeConfigPath">Path to configuration file.</param>
public DynamicAppSettings(string exeConfigPath)
{
if (string.IsNullOrEmpty(exeConfigPath))
throw new ArgumentNullException("exeConfigPath");
_exeConfigPath = exeConfigPath;
_config = ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = _exeConfigPath },
ConfigurationUserLevel.None);
}
/// <summary>
/// Gets the application settings entry from the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _config.AppSettings.Settings[binder.Name];
return true;
}
/// <summary>
/// Adds or updates application settings entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value != null && !(value is string))
{
return base.TrySetMember(binder, value);
}
if (_config.AppSettings.Settings[binder.Name] != null)
{
_config.AppSettings.Settings[binder.Name].Value = value.ToString();
}
else
{
_config.AppSettings.Settings.Add(binder.Name, value.ToString());
}
return true;
}
/// <summary>
/// Invokes <see cref="Configuration.Save"/> method to save changes or removes an entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name.Equals("Save", StringComparison.OrdinalIgnoreCase))
{
_config.Save();
result = null;
return true;
}
if (binder.Name.Equals("Remove", StringComparison.OrdinalIgnoreCase) &&
args != null && args.Length == 1 && (args[0] is string))
{
_config.AppSettings.Settings.Remove(args[0].ToString());
result = null;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
/// <summary>
/// Dynamic wrapper for <see cref="ConnectionStringsSection"/>.
/// </summary>
public class DynamicConnectionStrings : DynamicObject
{
private string _exeConfigPath;
private System.Configuration.Configuration _config;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicConnectionStrings"/> class with the configuration file
/// of the current application domain.
/// </summary>
public DynamicConnectionStrings()
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DynamicConnectionStrings"/> class with the specified
/// configuration file.
/// </summary>
/// <param name="exeConfigPath">Path to the configuration file.</param>
public DynamicConnectionStrings(string exeConfigPath)
{
if (string.IsNullOrEmpty(exeConfigPath))
throw new ArgumentNullException("exeConfigPath");
_exeConfigPath = exeConfigPath;
_config = ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = _exeConfigPath },
ConfigurationUserLevel.None);
}
/// <summary>
/// Gets the connection string value from the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _config.ConnectionStrings.ConnectionStrings[binder.Name].ConnectionString;
return true;
}
/// <summary>
/// Adds or updates a connection string entry in the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value != null && !(value is string))
{
return base.TrySetMember(binder, value);
}
if (_config.ConnectionStrings.ConnectionStrings[binder.Name] != null)
{
_config.ConnectionStrings.ConnectionStrings[binder.Name].ConnectionString = value.ToString();
}
else
{
_config.ConnectionStrings.ConnectionStrings.Add(
new ConnectionStringSettings(binder.Name, value.ToString()));
}
return true;
}
/// <summary>
/// Invokes <see cref="Configuration.Save"/> to save changes or removes an entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name.Equals("Save", StringComparison.OrdinalIgnoreCase))
{
_config.Save();
result = null;
return true;
}
if (binder.Name.Equals("Remove", StringComparison.OrdinalIgnoreCase) &&
args != null && args.Length == 1 && (args[0] is string))
{
_config.ConnectionStrings.ConnectionStrings.Remove(args[0].ToString());
result = null;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
For example:
dynamic apps = new DynamicAppSettings();
apps.UploadLocation = "~/Uploads/";
apps.WebServiceUrl = "http://www.site.com/service.svc";
apps.Remove("TempFolderPath");
apps.Save();
dynamic conns = new DynamicConnectionStrings();
conns.MasterDatabase = "Data Source=lpc:(local);Integrated Security=True;Initial Catalog=master";
conns.Save();
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.