HqlCase implementation to be used in a custom HqlGenerator - c#

I am trying to build a custom Hql generator which must build a Case construct. This construct is to be used in an order by clause. I am trying to do an alphabetically sort on an enumeration (a Gender enum in this case) in the language of the current user. As you can see, the sortorder is retrieved from the GenderResourceTextAttribute. The values in the order array must be used in the Case construct. This is what I have so far:
public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
GenderResourceTextAttribute attribute = null;
if (targetObject.Type.IsEnum)
{
attribute = targetObject.Type.GetCustomAttributes(typeof(GenderResourceTextAttribute), false).FirstOrDefault() as GenderResourceTextAttribute;
}
int[] order = attribute.GetSortOrderPosition();
return treeBuilder.Case(new HqlWhen(....));
}
[GenderResourceText]
public enum Gender
{
Unknown = 0,
Men,
Women
}
I eventually want it to generate something like the following sql:
case Gender when 0 then 1 when 1 then 2 else 0 end
How can I implement this?
Edited: added my solution, based on Gerben's advice:
Thanks Gerben!
With your provided example I was able to get it done:
public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
EnumResourceTextAttribute attribute = (EnumResourceTextAttribute)targetObject.Type.GetCustomAttributes(typeof(EnumResourceTextAttribute), false).FirstOrDefault();
IEnumerable<int> sortOrder = attribute.GetSortOrderPositions(arguments[0].ToString() == "Descending" ? System.Data.SqlClient.SortOrder.Descending : SortOrder.Ascending);
List<HqlExpression> parameters = new List<HqlExpression>();
List<HqlWhen> hqlWhenList = new List<HqlWhen>();
for(int index = 0; index < sortOrder.Count(); index++)
{
int position = sortOrder.ElementAt(index);
hqlWhenList.Add(
treeBuilder.When(
treeBuilder.Equality(visitor.Visit(targetObject).AsExpression(), treeBuilder.Constant(index)),
treeBuilder.Constant(position)
)
);
}
HqlCase hqlCase = treeBuilder.Case(hqlWhenList.ToArray());
return hqlCase;
}

My BuildHql method looks like this. It uses a CaseBuilder which is included below.
public override HqlTreeNode BuildHql(
MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor
)
{
// Get the CaseBuilder form the arguments.
var caseBuilder = (arguments[1] as ConstantExpression).Value as CaseBuilder;
var hqlWhenList = new List<HqlWhen>();
// Add a HqlWhen for each CaseBuilderOption.
foreach (var option in caseBuilder.Options)
{
// add the HqlWhen
hqlWhenList.Add(
// create HqlWhen with given treeBuilder.
treeBuilder.When(
// compare given property with the When of the CaseBuilderOption.
treeBuilder.Equality(visitor.Visit(arguments[0]).AsExpression(), treeBuilder.Constant(option.When)),
// add the Then value of the CaseBuilderOption
treeBuilder.Constant(option.Then)
)
);
}
//
return
// cast the returned value to returntype of CaseBuilder.
treeBuilder.Cast(
// create the HqlCase with the TreeBuilder.
treeBuilder.Case(
// add the created HqlWhen list.
hqlWhenList.ToArray(),
// add the final or else value from the CaseBuilder.
treeBuilder.Constant(caseBuilder.ElseValue)
),
// the return type for the cast.
caseBuilder.ReturnType
);
}
The CaseBuilder and the CaseBuilderOption classes.
public class CaseBuilder
{
/// <summary>
/// The options of this case.
/// </summary>
public List<CaseBuilderOption> Options { get; set; }
/// <summary>
/// Else return value.
/// </summary>
public object ElseValue { get; set; }
/// <summary>
/// Type of return value.
/// </summary>
public Type ReturnType { get; set; }
/// <summary>
///
/// </summary>
/// <param name="returnType"></param>
/// <param name="case1"></param>
/// <param name="value1"></param>
/// <param name="elseValue"></param>
public CaseBuilder(Type returnType, object when, object then, object elseValue)
{
ReturnType = returnType;
if (then.GetType() != returnType || elseValue.GetType() != returnType)
{
throw new Exception();
}
Options = new List<CaseBuilderOption>();
Options.Add(new CaseBuilderOption() { When = when, Then = then });
ElseValue = elseValue;
}
/// <summary>
/// Add a WhenThen option to the case builder.
/// </summary>
/// <param name="when"></param>
/// <param name="then"></param>
/// <returns></returns>
public CaseBuilder Append(object when, object then)
{
if (then.GetType() != ReturnType)
{
throw new Exception();
}
Options.Add(new CaseBuilderOption() { When = when, Then = then });
return this;
}
}
/// <summary>
/// A When Then option of a Case
/// </summary>
public class CaseBuilderOption
{
/// <summary>
/// When
/// </summary>
public object When { get; set; }
/// <summary>
/// returns this value if When and Case property are equal
/// </summary>
public object Then { get; set; }
}
I hope this will help you.

Related

c# creating a dynamically anonymous types (var) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I need to create a anonymous type (HAS to be a var). Like this:
var sizes = new {
size = new { Medium = "1", Large = "-3", XL = "10%" }
};
It has to be dynamically so the next time this could also happen:
var sizes = new {
size = new { 3XL = "5", 4XL = "5%", 5XL = "-10%" }
};
How do I do this in C# winforms?
How do i fill in the var sizes? It has to be in this order!
You can create a dynamic type at runtime which contains methods and properties of any type using System.Reflection.Emit, you can assign default values into your properties within the dynamic type created. This is not a trivial exercise and it needs some work, but when you have the base code complete using it in your code is easy.
First you need to attach your dynamic type to your current AppDomain.
private AssemblyName _assemblyName;
private AssemblyBuilder _asssemblyBuilder;
private ModuleBuilder _moduleBuilder;
private Dictionary<SignatureBuilder, Type> _classes;
private ReaderWriterLock _rwLock;
private TypeBuilder _typeBuilder;
private string _typeName;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="moduleName">The name of the assembly module.</param>
public DynamicTypeBuilder(string moduleName)
{
// Make sure the page reference exists.
if (moduleName == null) throw new ArgumentNullException("moduleName");
// Create the nw assembly
_assemblyName = new AssemblyName(moduleName);
_asssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.Run);
// Create only one module, therefor the
// modile name is the assembly name.
_moduleBuilder = _asssemblyBuilder.DefineDynamicModule(_assemblyName.Name);
// Get the class unique signature.
_classes = new Dictionary<SignatureBuilder, Type>();
_rwLock = new ReaderWriterLock();
}
The dynamic property class can be
/// <summary>
/// Dynamic property builder, with value assigned.
/// </summary>
public class DynamicPropertyValue
{
object value;
string name;
Type type;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="type">The type of the property</param>
/// <param name="value">The value of the property.</param>
public DynamicPropertyValue(string name, Type type, object value)
{
if (name == null) throw new ArgumentNullException("name");
if (type == null) throw new ArgumentNullException("type");
if (value == null) throw new ArgumentNullException("value");
this.name = name;
this.type = type;
this.value = value;
}
/// <summary>
/// Gets, the property name.
/// </summary>
public string Name
{
get { return name; }
}
/// <summary>
/// Gets, the property type.
/// </summary>
public Type Type
{
get { return type; }
}
/// <summary>
/// Gets, the property value.
/// </summary>
public object Value
{
get { return value; }
}
}
The dynamic method class can be
/// <summary>
/// Dynamic method builder.
/// </summary>
public class DynamicMethod
{
string name;
IEnumerable<Type> parameters;
Type returnType;
Action<TypeBuilder> buildAction = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="name">The name of the method.</param>
/// <param name="parameters">The collection parameter types.</param>
/// <param name="returnType">The return type.</param>
public DynamicMethod(string name, IEnumerable<Type> parameters, Type returnType)
{
if (name == null) throw new ArgumentNullException("name");
this.name = name;
this.parameters = parameters;
this.returnType = returnType;
}
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="name">The name of the method.</param>
/// <param name="parameters">The collection parameter types.</param>
/// <param name="returnType">The return type.</param>
/// <param name="buildAction">The build action.</param>
public DynamicMethod(string name, IEnumerable<Type> parameters, Type returnType, Action<TypeBuilder> buildAction)
{
if (name == null) throw new ArgumentNullException("name");
this.name = name;
this.parameters = parameters;
this.returnType = returnType;
this.buildAction = buildAction;
}
/// <summary>
/// Gets, the method name.
/// </summary>
public string Name
{
get { return name; }
}
/// <summary>
/// Gets, the collection of parameters
/// </summary>
public IEnumerable<Type> Parameters
{
get { return parameters; }
}
/// <summary>
/// Gets, the return type.
/// </summary>
public Type ReturnType
{
get { return returnType; }
}
/// <summary>
/// Gets, build action.
/// </summary>
public Action<TypeBuilder> BuildAction
{
get { return buildAction; }
}
}
Start the create process.
/// <summary>
/// Create a new instance of the dynamic type.
/// </summary>
/// <param name="typeName">The name of the type.</param>
/// <param name="properties">The collection of properties to create in the type.</param>
/// <param name="methods">The collection of methods to create in the type.</param>
/// <returns>The new instance of the type.</returns>
public object Create(string typeName, IEnumerable<DynamicPropertyValue> properties, IEnumerable<DynamicMethod> methods)
{
// Make sure the page reference exists.
if (typeName == null) throw new ArgumentNullException("typeName");
if (properties == null) throw new ArgumentNullException("properties");
if (methods == null) throw new ArgumentNullException("methods");
_typeName = typeName;
// Create the dynamic type collection
List<DynamicProperty> prop = new List<DynamicProperty>();
foreach (DynamicPropertyValue item in properties)
prop.Add(new DynamicProperty(item.Name, item.Type));
// Return the create type.
object instance = CreateEx(typeName, prop.ToArray(), methods);
PropertyInfo[] infos = instance.GetType().GetProperties();
// Assign each type value
foreach (PropertyInfo info in infos)
info.SetValue(instance, properties.First(u => u.Name == info.Name).Value, null);
// Return the instance with values assigned.
return instance;
}
If this is something you can use the complete source code for the dynamic type builder can be found at https://github.com/nequeo/misc/blob/master/csharp/DynamicTypeBuilder.cs

Name of class property to string

I have searched endlessly for a thread that specifies exactly my question but I cannot find it.
I want to take a class property and just take the property out of it as a string!
public class Foo
{
public Foo ()
{
}
public int MyProperty { get; set; }
}
Then I want to have the string "MyProperty" in the end, something like this:
Foo f = new Foo();
string s = Helperfunction(f.MyProperty);
string Helperfunction( X )
{
string MyString;
//do something with X
return MyString;
}
How difficult would this be to realize just with maybe a helperfunction?
So the output should be "MyProperty"
Because I dont want to hard code it, and as soon as I would ever refactor the property name, it would still work without editing the hardcoded string!
You can use reflection to get the name, here is a helper class I use:
public static class MemberName
{
/// <summary>
/// *** WARNING - Uses reflection - use responsibly ***
/// </summary>
/// <param name="instance"></param>
/// <param name="expression"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static string GetMemberName<T>(this T instance, Expression<Func<T, object>> expression)
{
return GetMemberName(expression);
}
/// <summary>
/// *** WARNING - Uses reflection - use responsibly ***
/// </summary>
/// <param name="expression"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static string GetMemberName<T>(Expression<Func<T, object>> expression)
{
if (expression == null)
{
throw new ArgumentException("The expression cannot be null.");
}
return GetMemberName(expression.Body);
}
/// <summary>
/// *** WARNING - Uses reflection - use responsibly ***
/// </summary>
/// <param name="instance"></param>
/// <param name="expression"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static string GetMemberName<T>(this T instance, Expression<Action<T>> expression)
{
return GetMemberName(expression);
}
/// <summary>
/// *** WARNING - Uses reflection - use responsibly ***
/// </summary>
/// <param name="expression"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static string GetMemberName<T>(Expression<Action<T>> expression)
{
if (expression == null)
{
throw new ArgumentException("The expression cannot be null.");
}
return GetMemberName(expression.Body);
}
private static string GetMemberName(Expression expression)
{
if (expression == null)
{
throw new ArgumentException("The expression cannot be null.");
}
if (expression is MemberExpression)
{
// Reference type property or field
var memberExpression = (MemberExpression)expression;
return memberExpression.Member.Name;
}
if (expression is MethodCallExpression)
{
// Reference type method
var methodCallExpression = (MethodCallExpression)expression;
return methodCallExpression.Method.Name;
}
if (expression is UnaryExpression)
{
// Property, field of method returning value type
var unaryExpression = (UnaryExpression)expression;
return GetMemberName(unaryExpression);
}
throw new ArgumentException("Invalid expression");
}
private static string GetMemberName(UnaryExpression unaryExpression)
{
if (unaryExpression.Operand is MethodCallExpression)
{
var methodExpression = (MethodCallExpression)unaryExpression.Operand;
return methodExpression.Method.Name;
}
return ((MemberExpression)unaryExpression.Operand).Member.Name;
}
}
And an example usage:
string propName = MemberName.GetMemberName<Foo>(x => x.MyProperty);
I don't quite understand the question, but if you want to do something with the propertyname, you could have a look at the CallerMemberName attribute.
string Helperfunction([CallerMemberName]string X = null )
{
return "PropertyName: " + X;
}
If you call this method within a property getter, then it will output the name of the property:
public class Foo
{
public string Foo { get { Trace.WriteLine (SomeFunction()); } }
}
Will output "MyProperty"

Using System.Reflection to retrieve a list of const string fields

I've created a class of classes (showing one of them) with const string that I want to itarate on.
public static class HTDB_Cols
{
public sealed class Assistant
{
public const string EntryID = "entryID",
CustName = "custName",
SerialNum = "serialNum",
UserName = "userName",
Password = "password",
EndDate = "end_date",
CustID = "custID",
TmpCheck = "tmpCheck",
Isfamily = "isfamily",
Isserver = "isserver";
}
}
public static class DB
{
public static void insert(string TableName)
{
ColumnsCollection = typeof(HTDB_Cols).GetNestedTypes().Where(f => f.DeclaringType.Name.ToLower().Equals(TableName.ToLower()));
}
}
The code above shows my attempt, but even after lots of trial and error I still couldn't get it right.
I want to have a list of all columns as const collection array or list.
var dict = typeof(HTDB_Cols).GetNestedTypes()
.First(t=>String.Compare(t.Name,TableName,true)==0)
.GetFields()
.ToDictionary(f => f.Name, f => f.GetValue(null));
To get a list
var list = typeof(HTDB_Cols).GetNestedTypes()
.First(t => String.Compare(t.Name, TableName, true) == 0)
.GetFields()
.Select(f => f.GetValue(null) as string)
.ToList();
It looks like what you need is an enum:
enum Assistant
{
EntryID,
CustName,
SerialNum,
UserName,
Password,
EndDate,
CustID,
TmpCheck,
Isfamily,
Isserver
};
You can then get all of those names as strings by doing:
string[] allNames = Enum.GetNames(typeof(Assistant));
As long as it's acceptable for you to have the names of the variables be the actual values that you care about, that's a valid option. I note that they're not quite the same in your example, but it's mostly just casing. If you can deal with using the variable names as the values, or changing the variable names to be the values you need, then that's likely to be your best option.
Now, if it really is important for the variable names to be different than the values they represent, or if you need to represent values that are illegal identifiers (for example, one of your values has a space, that's no good, and they couldn't ever start with a digit, or they might just be too long to be a convenient name). If that's the case, then what you really want is an enum that's backed by a string, rather than an integer or other numeric type. That's not strictly possible in C#, but since this has come up before I actually wrote the following class which is my best attempt at creating my own string backed enum. If you really need variable names that differ from the string values they represent, this should work for you.
All of the important stuff is right at the top, most everything after Equals is just syntactic sugar.
public struct StringEnum
{
#region Code that is to be configured
//For each value to be publicly exposed add a new field.
public static readonly StringEnum Alpha = new StringEnum("Alpha Value");
public static readonly StringEnum Beta = new StringEnum("Beta Value");
public static readonly StringEnum Invalid = new StringEnum("Invalid");
public static IEnumerable<StringEnum> AllValues
{
get
{
yield return Alpha;
yield return Beta;
yield return Invalid;
//...
//add a yield return for all instances here.
//TODO refactor to use reflection so it doesn't need to be manually updated.
}
}
#endregion
private string value;
/// <summary>
/// default constructor
/// </summary>
//private Group()
//{
// //You can make this default value whatever you want. null is another option I considered
// //(if this is a class an not a struct), but you
// //shouldn't have this be anything that doesn't exist as one of the options defined at the top of
// //the page.
// value = "Invalid";
//}
/// <summary>
/// primary constructor
/// </summary>
/// <param name="value">The string value that this is a wrapper for</param>
private StringEnum(string value)
{
this.value = value;
}
/// <summary>
/// Compares the StringEnum to another StringEnum, or to a string value.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj is StringEnum)
{
return this.Equals((StringEnum)obj);
}
string otherString = obj as string;
if (otherString != null)
{
return this.Equals(otherString);
}
throw new ArgumentException("obj is neither a StringEnum nor a String");
}
/// <summary>
/// Strongly typed equals method.
/// </summary>
/// <param name="other">Another StringEnum to compare this object to.</param>
/// <returns>True if the objects are equal.</returns>
public bool Equals(StringEnum other)
{
return value == other.value;
}
/// <summary>
/// Equals method typed to a string.
/// </summary>
/// <param name="other">A string to compare this object to.
/// There must be a Group associated with that string.</param>
/// <returns>True if 'other' represents the same Group as 'this'.</returns>
public bool Equals(string other)
{
return value == other;
}
/// <summary>
/// Overridden equals operator, for convenience.
/// </summary>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns>True if the objects are equal.</returns>
public static bool operator ==(StringEnum first, StringEnum second)
{
return object.Equals(first, second);
}
public static bool operator !=(StringEnum first, StringEnum second)
{
return !object.Equals(first, second);
}
/// <summary>
/// Properly overrides GetHashCode so that it returns the hash of the wrapped string.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return value.GetHashCode();
}
/// <summary>
/// returns the internal string that this is a wrapper for.
/// </summary>
/// <param name="stringEnum"></param>
/// <returns></returns>
public static implicit operator string(StringEnum stringEnum)
{
return stringEnum.value;
}
/// <summary>
/// Parses a string and returns an instance that corresponds to it.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static StringEnum Parse(string input)
{
return AllValues.Where(item => item.value == input).FirstOrDefault();
}
/// <summary>
/// Syntatic sugar for the Parse method.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public static explicit operator StringEnum(string other)
{
return Parse(other);
}
/// <summary>
/// A string representation of this object.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return value;
}
}

How to map two expressions of differing types?

I'll start with some classes...
The Domain Entity:
public class Account
{
public int Id { get; set; }
public double Balance { get; set; }
public string CustomerName { get; set; }
}
The View Model:
public class AccountModel
{
public int Id { get; set; }
public double Bal { get; set; }
public string Name { get; set; }
}
The Repository:
My repository has a method on it that takes an expression and returns a list, like this:
public interface IAccountRepository
{
IEnumerable<Account> Query(Expression<Func<Account, bool>> expression);
}
The Problem
My application generates an Expression<Func<AccountModel, bool>> in the UI. I need to somehow convert or map the EXPRESSION from AccountModel to Account so that I can use it in my Query method. I say "map" because, if you notice, my model and domain objects are similar, but don't necessarily have the same property names.
How can this be done?
This sounds like a job for AutoMapper. Automapper allows you to map one class to another at one point in time and use this mapping configuration later on.
See the Projection page on the wiki for the kind of thing you are after.
Update As you are using Entity Framework, here is an update for remapping your expression from using AccountModel to Account.
In the CompositionRoot of your application, set up AutoMapper like so (ignore Code Contract statements if you do not use Code Contracts):
var accountModelMap = Mapper.CreateMap<AccountModel, Account>();
Contract.Assume(accountModelMap != null);
accountModelMap.ForMember(account => account.Id, expression => expression.MapFrom(model => model.Id));
accountModelMap.ForMember(account => account.Balance, expression => expression.MapFrom(model => model.Bal));
accountModelMap.ForMember(account => account.CustomerName, expression => expression.MapFrom(model => model.Name));
This configures how the two data types relate to eachother.
Implement an ExpressionVisitor to use AutoMapper to rebind member access from one type to another.
/// <summary>
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly ParameterExpression _newParameter;
private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
/// <summary>
/// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class.
/// </summary>
/// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param>
public AutoMapVisitor(ParameterExpression newParameter)
{
Contract.Requires(newParameter != null);
_newParameter = newParameter;
Contract.Assume(_typeMap != null);
}
[ContractInvariantMethod]
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
private void ObjectInvariant()
{
Contract.Invariant(_typeMap != null);
Contract.Invariant(_newParameter != null);
}
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
/// </summary>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <param name="node">The expression to visit.</param>
protected override Expression VisitMember(MemberExpression node)
{
var propertyMaps = _typeMap.GetPropertyMaps();
Contract.Assume(propertyMaps != null);
// Find any mapping for this member
var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member);
if (propertyMap == null)
return base.VisitMember(node);
var destinationProperty = propertyMap.DestinationProperty;
Contract.Assume(destinationProperty != null);
var destinationMember = destinationProperty.MemberInfo;
Contract.Assume(destinationMember != null);
// Check the new member is a property too
var property = destinationMember as PropertyInfo;
if (property == null)
return base.VisitMember(node);
// Access the new property
var newPropertyAccess = Expression.Property(_newParameter, property);
return base.VisitMember(newPropertyAccess);
}
}
Then implement an extension method to make this easier to use:
/// <summary>
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source element.</typeparam>
/// <typeparam name="TDestination">The type of the destination element.</typeparam>
/// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam>
/// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns>
public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(this Expression<Func<TSource, TResult>> expression)
{
Contract.Requires(expression != null);
Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null);
var newParameter = Expression.Parameter(typeof (TDestination));
Contract.Assume(newParameter != null);
var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter);
var remappedBody = visitor.Visit(expression.Body);
if (remappedBody == null)
throw new InvalidOperationException("Unable to remap expression");
return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter);
}
}
This can subsequently be used like so (in an NUnit test):
[TestFixture]
public class RemappingTests
{
#region Setup/Teardown
/// <summary>
/// Sets up the variables before each test.
/// </summary>
[SetUp]
public void Setup()
{
var accountModelMap = Mapper.CreateMap<AccountModel, Account>();
Contract.Assume(accountModelMap != null);
accountModelMap.ForMember(account => account.Id, expression => expression.MapFrom(model => model.Id));
accountModelMap.ForMember(account => account.Balance, expression => expression.MapFrom(model => model.Bal));
accountModelMap.ForMember(account => account.CustomerName, expression => expression.MapFrom(model => model.Name));
}
[TearDown]
public void Teardown()
{
Mapper.Reset();
}
#endregion
/// <summary>
/// Checks that <see cref="ExpressionExtensions.RemapForType{TSource, TDestination, TResult}(Expression{Func{TSource, TResult}})"/> correctly remaps all property access for the new type.
/// </summary>
/// <param name="balance">The balance to use as the value for <see cref="Account.Balance"/>.</param>
/// <returns>Whether the <see cref="Account.Balance"/> was greater than 50.</returns>
[TestCase(0, Result = false)]
[TestCase(80, Result = true)]
public bool RemapperUsesPropertiesOfNewDataType(double balance)
{
Expression<Func<AccountModel, bool>> modelExpr = model => model.Bal > 50;
var accountExpr = modelExpr.RemapForType<AccountModel, Account, bool>();
var compiled = accountExpr.Compile();
Contract.Assume(compiled != null);
var hasBalance = compiled(new Account {Balance = balance});
return hasBalance;
}
}
In case that is too much code to find the exact call, here it is:
Expression<Func<AccountModel, bool>> modelExpr = model => model.Bal > 50;
var accountExpr = modelExpr.RemapForType<AccountModel, Account, bool>();
You can use an ExpressionVisitor to rewrite the Expression:
public class AccountModelRewriter : ExpressionVisitor
{
private Stack<ParameterExpression[]> _LambdaStack = new Stack<ParameterExpression[]>();
protected override Expression VisitLambda<T>(Expression<T> node)
{
var lambda = (LambdaExpression)node;
_LambdaStack.Push(
lambda.Parameters.Select(parameter => typeof(AccountModel) == parameter.Type ? Expression.Parameter(typeof(Account)) : parameter)
.ToArray()
);
lambda = Expression.Lambda(
this.Visit(lambda.Body),
_LambdaStack.Pop()
);
return lambda;
}
protected override Expression VisitMember(MemberExpression node)
{
var memberExpression = (MemberExpression)node;
var declaringType = memberExpression.Member.DeclaringType;
var propertyName = memberExpression.Member.Name;
if (typeof(AccountModel) == declaringType)
{
switch (propertyName)
{
case "Bal" :
propertyName = "Balance";
break;
case "Name" :
propertyName = "CustomerName";
break;
}
memberExpression = Expression.Property(
this.Visit(memberExpression.Expression),
typeof(Account).GetProperty(propertyName)
);
}
return memberExpression;
}
protected override Expression VisitParameter(ParameterExpression node)
{
node = (ParameterExpression)base.VisitParameter(node);
if (typeof(AccountModel) == node.Type)
{
node = this._LambdaStack.Peek().Single(parameter => parameter.Type == typeof(Account));
}
return node;
}
}
This visitor switches the input parameters from type AccountModel to Account (that's the VisitLambda and VisitParameter methods), and also changes all property accessors to use this new parameter as well as switching the property names if appropriate (that's the VisitMember part).
Usage is as follows:
Expression<Func<AccountModel, bool>> accountModelQuery = a => a.Bal == 0 && a.Name != null && a.Id != 7;
var accountQuery = (Expression<Func<Account, bool>>)new AccountModelRewriter().Visit(accountModelQuery);

Get the Enum<T> value Description

I have my enumHelper class that contains these:
public static IList<T> GetValues()
{
IList<T> list = new List<T>();
foreach (object value in Enum.GetValues(typeof(T)))
{
list.Add((T)value);
}
return list;
}
and
public static string Description(Enum value)
{
Attribute DescAttribute = LMIGHelper.GetAttribute(value, typeof(DescriptionAttribute));
if (DescAttribute == null)
return value.ToString();
else
return ((DescriptionAttribute)DescAttribute).Description;
}
my enum is something like:
public enum OutputType
{
File,
[Description("Data Table")]
DataTable
}
So far so good. All the previous work fine.
Now I want to add a new helper to return BindingList>, so I can link any enum to any combo using
BindingList<KeyValuePair<OutputType, string>> list = Enum<OutputType>.GetBindableList();
cbo.datasource=list;
cbo.DisplayMember="Value";
cbo.ValueMember="Key";
For that I added:
public static BindingList<KeyValuePair<T, string>> GetBindingList()
{
BindingList<KeyValuePair<T, string>> list = new BindingList<KeyValuePair<T, string>>();
foreach (T value in Enum<T>.GetValues())
{
string Desc = Enum<T>.Description(value);
list.Add(new KeyValuePair<T, string>(value, Desc));
}
return list;
}
But "Enum.Description(value)" is not even compiling:
Argument '1': cannot convert from 'T' to 'System.Enum'
How can I do that? Is that even possible?
Thank you.
Take a look at this article. You can do this using the System.ComponentModel.DescriptionAttribute or creating your own attribute:
/// <summary>
/// Provides a description for an enumerated type.
/// </summary>
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field,
AllowMultiple = false)]
public sealed class EnumDescriptionAttribute : Attribute
{
private string description;
/// <summary>
/// Gets the description stored in this attribute.
/// </summary>
/// <value>The description stored in the attribute.</value>
public string Description
{
get
{
return this.description;
}
}
/// <summary>
/// Initializes a new instance of the
/// <see cref="EnumDescriptionAttribute"/> class.
/// </summary>
/// <param name="description">The description to store in this attribute.
/// </param>
public EnumDescriptionAttribute(string description)
: base()
{
this.description = description;
}
}
You then need to decorate the enum values with this new attribute:
public enum SimpleEnum
{
[EnumDescription("Today")]
Today,
[EnumDescription("Last 7 days")]
Last7,
[EnumDescription("Last 14 days")]
Last14,
[EnumDescription("Last 30 days")]
Last30,
[EnumDescription("All")]
All
}
All of the "magic" takes place in the following extension methods:
/// <summary>
/// Provides a static utility object of methods and properties to interact
/// with enumerated types.
/// </summary>
public static class EnumHelper
{
/// <summary>
/// Gets the <see cref="DescriptionAttribute" /> of an <see cref="Enum" />
/// type value.
/// </summary>
/// <param name="value">The <see cref="Enum" /> type value.</param>
/// <returns>A string containing the text of the
/// <see cref="DescriptionAttribute"/>.</returns>
public static string GetDescription(this Enum value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
EnumDescriptionAttribute[] attributes =
(EnumDescriptionAttribute[])
fieldInfo.GetCustomAttributes(typeof(EnumDescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
description = attributes[0].Description;
}
return description;
}
/// <summary>
/// Converts the <see cref="Enum" /> type to an <see cref="IList" />
/// compatible object.
/// </summary>
/// <param name="type">The <see cref="Enum"/> type.</param>
/// <returns>An <see cref="IList"/> containing the enumerated
/// type value and description.</returns>
public static IList ToList(this Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
ArrayList list = new ArrayList();
Array enumValues = Enum.GetValues(type);
foreach (Enum value in enumValues)
{
list.Add(new KeyValuePair<Enum, string>(value, GetDescription(value)));
}
return list;
}
}
Finally, you can then simply bind the combobox:
combo.DataSource = typeof(SimpleEnum).ToList();
You should change:
public static string Description(Enum value)
{
...
}
to
public static string Description(T value)
{
...
}
so it accepts a value of the enumeration. Now here is where it gets tricky: you have a value, but attributes decorate the field which holds the value.
You actually need to reflect over the enumeration's fields and check the value of each against the value you've been given (results should be cached for performance):
foreach(var field in typeof(T).GetFields())
{
T fieldValue;
try
{
fieldValue = (T) field.GetRawConstantValue();
}
catch(InvalidOperationException)
{
// For some reason, one of the fields returned is {Int32 value__},
// which throws an InvalidOperationException if you try and retrieve
// its constant value.
//
// I am unsure how to check for this state before
// attempting GetRawConstantValue().
continue;
}
if(fieldValue == value)
{
var attribute = LMIGHelper.GetAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
}
Edit addressing the follow-up question
The FillComboFromEnum method is missing the type parameter for the enum. Try this:
public static void FillComboFromEnum<T>(ComboBox Cbo, BindingList<KeyValuePair<T, string>> List) where T : struct
Notice I constrained the type to be a struct. It's not a full enumeration constraint, but it's closer than nothing.
Enum doesn't have a Description() method. The best you could do is have your enum implement an interface that has the Description() method. If you do that, then you can have
public static BindingList<KeyValuePair<T extends _interface_, String>> getBindingList()
and then inside of that you can refer to
T foo = ...?
foo.Description(...);

Categories