Can the DisplayName attribute be accessed without using MVC? - c#

I have refactored out a generic CSV builder for List using lambda expressions to access the correct values
public static string ToCsv<TModel>(this List<TModel> list, string delimiter, string lineBreak, string valueWrap, params Expression<Func<TModel, object>>[] expressions)
{
var sb = new StringBuilder();
var headers = expressions.Select(m => String.Format("{0}{1}{0}", valueWrap, GetPropertyName(m))).ToArray();
sb.Append(String.Format("{0}{1}", String.Join(delimiter, headers), lineBreak));
foreach (var listItem in list)
{
var values = expressions.Select(m => String.Format("{0}{1}{0}", valueWrap, m.Compile()(listItem))).ToArray();
sb.Append(String.Format("{0}{1}", String.Join(delimiter, values), lineBreak));
}
return sb.ToString();
}
This works well, however because I am trying to move this into some common code for several projects across several servers to access. I cannot reference the System.Web.Mvc assembly
Is there a good way to access the DisplayName Attribute and if that doesnot exist, access the variable name?
My current attempt access the ((MemberExpression) expression.Body).Member.Name however it will not work if it has to convert a value to string. (ie passing an int and implicitly converting)
A second attempt of iterating over the attributes did not work for inner class (ie model => model.innerClass.property)

With a couple simple helper functions, you can eliminate all reference to the System.Web.Mvc assembly. You'll also notice in the example below that (model => model.member.property) works.
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.ComponentModel;
using System.Reflection;
namespace Test
{
public class Program
{
public static void Main(string[] args)
{
List<Class1> foobars = new List<Class1>();
foobars.Add(new Class1 { Foo = "Hello world!", Bar = -1, Skip = false, Ref = new Class2 { ToBe = true } });
string result = foobars.ToCsv(",", Environment.NewLine, "\"", m => m.Foo, m => m.Bar, m => m.Ref.ToBe);
}
public class Class1
{
[DisplayName("Foo Property")]
public string Foo { get; set; }
public int Bar { get; set; }
[DisplayName("Skipped Property")]
public bool Skip { get; set; }
[DisplayName("Reference")]
public Class2 Ref { get; set; }
}
public class Class2
{
[DisplayName("To Be or Not To Be")]
public bool ToBe { get; set; }
}
}
public static class Extensions
{
public static string ToCsv<TModel>(this List<TModel> list, string delimiter, string lineBreak, string valueWrap, params Expression<Func<TModel, object>>[] expressions)
{
var sb = new StringBuilder();
var headers = expressions.Select(m => String.Format("{0}{1}{0}", valueWrap, GetDisplayName(m))).ToArray();
sb.Append(String.Format("{0}{1}", String.Join(delimiter, headers), lineBreak));
foreach (var listItem in list)
{
var values = expressions.Select(m => String.Format("{0}{1}{0}", valueWrap, m.Compile()(listItem))).ToArray();
sb.Append(String.Format("{0}{1}", String.Join(delimiter, values), lineBreak));
}
return sb.ToString();
}
// Get DisplayName, otherwise fallback to Name
private static string GetDisplayName(LambdaExpression memberReference)
{
MemberInfo info = GetMemberInfo(memberReference);
DisplayNameAttribute displayNameAttr = Attribute.GetCustomAttribute(info, typeof(DisplayNameAttribute)) as DisplayNameAttribute;
return (displayNameAttr != null ? displayNameAttr.DisplayName : info.Name);
}
// Can be swapped for your favourite GetMemberInfo/GetPropertyInfo utility method (there are many out there)
// Source: http://blog.baltrinic.com/software-development/dotnet/extension-methods-for-converting-lambda-expression-to-strings
private static MemberInfo GetMemberInfo(LambdaExpression memberReference)
{
MemberExpression memberExpression;
var unary = memberReference.Body as UnaryExpression;
if (unary != null)
//In this case the return type of the property was not object,
//so .Net wrapped the expression inside of a unary Convert()
//expression that casts it to type object. In this case, the
//Operand of the Convert expression has the original expression.
memberExpression = unary.Operand as MemberExpression;
else
//when the property is of type object the body itself is the
//correct expression
memberExpression = memberReference.Body as MemberExpression;
if (memberExpression == null || !(memberExpression.Member is MemberInfo))
throw new ArgumentException("Expression was not of the form 'x => x.member'.");
return memberExpression.Member;
}
}
}

Related

C# Expression with collection item properties - access member names

I am building an SDK for building HTTP Queries to a certain system where I need to specify in the query string which properties of a model I want to include.
For example https://system/api/projects/1?fields=name,description
I want the SDK to be strongly typed, so I have query builder classes which allow specifying the query as
new ProjectBuilder(1, f => f.Name, f => f.Description)
That works very nice even for complex tree of nested objects, e.g. f => f.ProjectTemplate.Location.Owner.Email
The only problem is with collections, e.g.
public class Task
{
public string Name {get;set;}
//lots of other stuff
}
public string Project
{
public string Description {get;set;}
//lots of other stuff
public List<Task> Tasks {get;set;}
}
When I need to retrieve Project's Description and names of all Tasks in the Project, the query string would have to be as follows:
https://system/api/projects/1?fields=description,tasks.name
I cannot define an expression like that:
new ProjectBuilder(1, f => f.Tasks.Name), the syntax seem to require f.Tasks[0].Name.
Can I have the same nice expression-type syntax with collection members (and further nested objects)?
The code that I use for member access from expression is below (slighty simplified):
public static string Evaluate<T>(Expression<Func<T, object>> expression)
{
if (expression.Body is MemberExpression body)
{
return EvaluateExpressionTree(body);
}
else
{
throw new InvalidOperationException(
"Invalid expression. Expected Member expression, e.g. p=>p.Description");
}
}
private static string EvaluateExpressionTree(MemberExpression root)
{
if (root.Expression is MemberExpression nested)
{
var nestedProperty = EvaluateExpressionTree(nested);
var thisProperty = root.Member.Name;
return nestedProperty + "." + thisProperty;
}
else if (root.Expression is MethodCallExpression call)
{
//that's where I get when using the Tasks[0] syntax
}
else
{
return GetMemberName(root);
}
}
I was able to get to the point of finding the generic type from the collection when the expression was accessing a collection element, but from that point I cannot rebuild the further elements of the expression...
So I managed to solve this for my kind of problem. Not saying this is the way of handling expressions with collection properties, but question is specific, and so is the answer:)
Overview
The solution was to
stringify the expression
tokenize it
remove noise
evaluate parts by getting PropertyInfo by Reflection
keep track of the current type within the expression tree to get correct results
That allows me to have a nice fluent API for strong typed generation of query strings, as visible below.
Code
Usage
[TestMethod]
public void ProjectExpression_NestedType_CollectionAccess()
{
//arrange
ProjectBuilder builder = new ProjectBuilder("000",
f => f.Name,
f => f.Workflow.TaskConfigurations,
f => f.Workflow.TaskConfigurations[0].TaskTemplate.TaskType)
;
//act
string stringified = builder.BuildFullRequestUri();
//assert
Assert.AreEqual("projects/000?fields=name,workflow.taskConfigurations,workflow.taskConfigurations.taskTemplate.taskType", stringified);
}
Implementation:
The caller code that takes the function params:
protected ProjectBuilder(params Expression<Func<Project, object>>[] propertiesToInclude)
{
List<string> values = new List<string>();
foreach (Expression<Func<Project, object>> expression in propertiesToInclude)
{
values.Add(ExpressionEvaluator.Evaluate(expression));
}
this.AddProperties(values);
}
And the expression evaluator that takes each param and transforms it nicely.
internal static class ExpressionEvaluator
{
public static string Evaluate<T>(Expression<Func<T, object>> expression)
{
if (expression.Body is MemberExpression)
{
List<string> result = new List<string>();
List<string> parts = GetExpressionParts(expression);
List<Type> expressionTreeTypes = new List<Type>() { typeof(T) };
foreach (string part in parts)
{
PropertyInfo member = expressionTreeTypes.Last().GetProperty(part);
if (member != null)
{
AddValueFromJsonAttributeOrPropertyName<T>(member, result);
UpdateLastUsedType<T>(member, expressionTreeTypes);
}
else
{
throw new InvalidOperationException($"Expression [{expression.Body}] is not valid. Failed to resolve: [{part}]");
}
}
return string.Join(".", result);
}
else
{
throw new InvalidOperationException("Invalid expression. Expected Member expression, e.g. p=>p.Description");
}
}
private static List<string> GetExpressionParts<T>(Expression<Func<T, object>> expression)
{
var stringified = expression.Body.ToString();
//skip the first part of expression - e.g. (f=>f.) - skip 'f.
//also skip collection accessors
return stringified.Split('.').Where(x => !x.Contains("get_Item(")).Skip(1).ToList();
}
private static void UpdateLastUsedType<T>(PropertyInfo member, List<Type> expressionTreeTypes)
{
//make sure that in case of primitive types we don't change the type
if (member.PropertyType.Assembly == typeof(Project).Assembly && member.PropertyType != expressionTreeTypes.Last())
{
expressionTreeTypes.Add(member.PropertyType);
}
else if (member.PropertyType.IsGenericType) // in case of collection properties, extract the nested type
{
expressionTreeTypes.Add(member.PropertyType.GenericTypeArguments.First());
}
}
private static void AddValueFromJsonAttributeOrPropertyName<T>(MemberInfo member, List<string> result)
{
var attr = member.CustomAttributes
.FirstOrDefault(x => x.AttributeType == typeof(JsonPropertyAttribute))?.ConstructorArguments?
.FirstOrDefault();
if (!string.IsNullOrEmpty(attr?.Value?.ToString()))
{
result.Add(attr?.Value?.ToString());
}
else
{
result.Add(member.Name[0].ToString().ToLower() + member.Name.Substring(1));
}
}
}

Application and User Settings C# [duplicate]

I have a class, lets call it Book with a property called Name. With that property, I have an attribute associated with it.
public class Book
{
[Author("AuthorName")]
public string Name
{
get; private set;
}
}
In my main method, I'm using reflection and wish to get key value pair of each attribute for each property. So in this example, I'd expect to see "Author" for attribute name and "AuthorName" for the attribute value.
Question: How do I get the attribute name and value on my properties using Reflection?
Use typeof(Book).GetProperties() to get an array of PropertyInfo instances. Then use GetCustomAttributes() on each PropertyInfo to see if any of them have the Author Attribute type. If they do, you can get the name of the property from the property info and the attribute values from the attribute.
Something along these lines to scan a type for properties that have a specific attribute type and to return data in a dictionary (note that this can be made more dynamic by passing types into the routine):
public static Dictionary<string, string> GetAuthors()
{
Dictionary<string, string> _dict = new Dictionary<string, string>();
PropertyInfo[] props = typeof(Book).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
AuthorAttribute authAttr = attr as AuthorAttribute;
if (authAttr != null)
{
string propName = prop.Name;
string auth = authAttr.Name;
_dict.Add(propName, auth);
}
}
}
return _dict;
}
To get all attributes of a property in a dictionary use this:
typeof(Book)
.GetProperty("Name")
.GetCustomAttributes(false)
.ToDictionary(a => a.GetType().Name, a => a);
remember to change from false to true if you want to include inheritted attributes as well.
If you just want one specific Attribute value For instance Display Attribute you can use the following code:
var pInfo = typeof(Book).GetProperty("Name")
.GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
I have solved similar problems by writing a Generic Extension Property Attribute Helper:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class AttributeHelper
{
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(
Expression<Func<T, TOut>> propertyExpression,
Func<TAttribute, TValue> valueSelector)
where TAttribute : Attribute
{
var expression = (MemberExpression) propertyExpression.Body;
var propertyInfo = (PropertyInfo) expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() as TAttribute;
return attr != null ? valueSelector(attr) : default(TValue);
}
}
Usage:
var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"
You can use GetCustomAttributesData() and GetCustomAttributes():
var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
If you mean "for attributes that take one parameter, list the attribute-names and the parameter-value", then this is easier in .NET 4.5 via the CustomAttributeData API:
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
public static class Program
{
static void Main()
{
PropertyInfo prop = typeof(Foo).GetProperty("Bar");
var vals = GetPropertyAttributes(prop);
// has: DisplayName = "abc", Browsable = false
}
public static Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
{
Dictionary<string, object> attribs = new Dictionary<string, object>();
// look for attributes that takes one constructor argument
foreach (CustomAttributeData attribData in property.GetCustomAttributesData())
{
if(attribData.ConstructorArguments.Count == 1)
{
string typeName = attribData.Constructor.DeclaringType.Name;
if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
attribs[typeName] = attribData.ConstructorArguments[0].Value;
}
}
return attribs;
}
}
class Foo
{
[DisplayName("abc")]
[Browsable(false)]
public string Bar { get; set; }
}
private static Dictionary<string, string> GetAuthors()
{
return typeof(Book).GetProperties()
.SelectMany(prop => prop.GetCustomAttributes())
.OfType<AuthorAttribute>()
.ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), a => a.Name);
}
Example using generics (target framework 4.5)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
private static Dictionary<string, string> GetAttribute<TAttribute, TType>(
Func<TAttribute, string> valueFunc)
where TAttribute : Attribute
{
return typeof(TType).GetProperties()
.SelectMany(p => p.GetCustomAttributes())
.OfType<TAttribute>()
.ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), valueFunc);
}
Usage
var dictionary = GetAttribute<AuthorAttribute, Book>(a => a.Name);
public static class PropertyInfoExtensions
{
public static TValue GetAttributValue<TAttribute, TValue>(this PropertyInfo prop, Func<TAttribute, TValue> value) where TAttribute : Attribute
{
var att = prop.GetCustomAttributes(
typeof(TAttribute), true
).FirstOrDefault() as TAttribute;
if (att != null)
{
return value(att);
}
return default(TValue);
}
}
Usage:
//get class properties with attribute [AuthorAttribute]
var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
foreach (var prop in props)
{
string value = prop.GetAttributValue((AuthorAttribute a) => a.Name);
}
or:
//get class properties with attribute [AuthorAttribute]
var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
IList<string> values = props.Select(prop => prop.GetAttributValue((AuthorAttribute a) => a.Name)).Where(attr => attr != null).ToList();
While the above most upvoted answers definitely work, I'd suggest using a slightly different approach in some cases.
If your class has multiple properties with always the same attribute and you want to get those attributes sorted into a dictionary, here is how:
var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());
This still uses cast but ensures that the cast will always work as you will only get the custom attributes of the type "AuthorName".
If you had multiple Attributes above answers would get a cast exception.
Here are some static methods you can use to get the MaxLength, or any other attribute.
using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
public static class AttributeHelpers {
public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}
//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
return GetMaxLength<T>(propertyExpression);
}
//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
var expression = (MemberExpression)propertyExpression.Body;
var propertyInfo = (PropertyInfo)expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;
if (attr==null) {
throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
}
return valueSelector(attr);
}
}
Using the static method...
var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);
Or using the optional extension method on an instance...
var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);
Or using the full static method for any other attribute (StringLength for example)...
var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);
Inspired by the Mikael Engver's answer.
I wrote this into a dynamic method since I use lots of attributes throughout my application. Method:
public static dynamic GetAttribute(Type objectType, string propertyName, Type attrType)
{
//get the property
var property = objectType.GetProperty(propertyName);
//check for object relation
return property.GetCustomAttributes().FirstOrDefault(x => x.GetType() == attrType);
}
Usage:
var objectRelAttr = GetAttribute(typeof(Person), "Country", typeof(ObjectRelationAttribute));
var displayNameAttr = GetAttribute(typeof(Product), "Category", typeof(DisplayNameAttribute));
Hope this helps anyone
Necromancing.
For those that still have to maintain .NET 2.0, or those that want to do it without LINQ:
public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
object[] objs = mi.GetCustomAttributes(t, true);
if (objs == null || objs.Length < 1)
return null;
return objs[0];
}
public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
return (T)GetAttribute(mi, typeof(T));
}
public delegate TResult GetValue_t<in T, out TResult>(T arg1);
public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
// TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));
if (att != null)
{
return value(att);
}
return default(TValue);
}
Example usage:
System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});
or simply
string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );
Just looking for the right place to put this piece of code.
let's say you have the following property:
[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }
And you want to get the ShortName value. You can do:
((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
Or to make it general:
internal static string GetPropertyAttributeShortName(string propertyName)
{
return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
foreach (var p in model.GetType().GetProperties())
{
var valueOfDisplay =
p.GetCustomAttributesData()
.Any(a => a.AttributeType.Name == "DisplayNameAttribute") ?
p.GetCustomAttribute<DisplayNameAttribute>().DisplayName :
p.Name;
}
In this example I used DisplayName instead of Author because it has a field named 'DisplayName' to be shown with a value.
to get attribute from enum, i'm using :
public enum ExceptionCodes
{
[ExceptionCode(1000)]
InternalError,
}
public static (int code, string message) Translate(ExceptionCodes code)
{
return code.GetType()
.GetField(Enum.GetName(typeof(ExceptionCodes), code))
.GetCustomAttributes(false).Where((attr) =>
{
return (attr is ExceptionCodeAttribute);
}).Select(customAttr =>
{
var attr = (customAttr as ExceptionCodeAttribute);
return (attr.Code, attr.FriendlyMessage);
}).FirstOrDefault();
}
// Using
var _message = Translate(code);
If you want get property having the custom Attribute then please try the following:
IEnumerable propertyInfos = properties.GetType().GetProperties();
PropertyInfo p = propertyInfos.Where(x => x.GetCustomAttribute() != null);

Getting the callee's name of an extension method

I've built up a simple ArgumentValidator class in order to simplify argument preconditions in any given method. Most of them are null or bounds checks and it gets pretty tedious after a couple of
if (arg == null ) throw new ArgumentNullException(nameof(arg));
So I've come up with the following set up:
public static class ArgumentValidator
{
public interface IArgument<T>
{
string ParamName { get; }
T Value { get; }
}
private class Argument<T>: IArgument<T>
{
public Argument(T argument, string paramName)
{
ParamName = paramName;
Value = argument;
}
public string ParamName { get; }
public T Value { get; }
}
public static IArgument<T> Validate<T>(this T argument, string paramName = null)
{
return new Argument<T>(argument, paramName ?? string.Empty);
}
public static IArgument<T> IsNotNull<T>(this IArgument<T> o)
{
if (ReferenceEquals(o.Value, null))
throw new ArgumentNullException(o.ParamName);
return o;
}
public static IArgument<T> IsSmallerThan<T, Q>(this IArgument<T> o, Q upperBound) where T : IComparable<Q> { ... }
//etc.
}
And I can use it in the following way:
public Bar Foo(object flob)
{
flob.Validate(nameof(flob)).IsNotNull().IsSmallerThan(flob.MaxValue);
}
Ideally I'd love to get rid of nameof(flob) in the Validate call and ultimately get rid of Validate alltogether; the only purpose of Validate is to avoid having to pass nameof(...) on every check down the chain.
Is there a way to get the name flob inside the Validate() method?
Doing that with an extension method is not that easy. It is easier with a static method that takes an LINQ expression (derived from devdigital's answer here):
public static T Validate<T>(this Expression<Func<T>> argument)
{
var lambda = (LambdaExpression)argument;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
string name = memberExpression.Member.Name;
Delegate d = lambda.Compile();
return (T)d.DynamicInvoke();
}
The name inside is the name of the property you put in the method:
MyMethods.Validate(() => o);
Since the Validate returns T, you can use that further on. This might not be as performing as you want it to be, but this is the only viable option.
It is possible to make this an extension method too, you have to create the expression yourself by hand:
Expression<Func<object>> f = () => o; // replace 'object' with your own type
f.Validate();

Dynamic linq Building Expression

I need to create a dynamic linq expression for a dynamic search.The basic search is working but it fails to work with collection.
I am able to get the book's title and author but fails to get the required page heading.
I get the exception in line "left11 = Expression.Property(page1, "Heading");".
I think the expression that i built is unable to recognise the List. How could this be possible?
Please see the below code and stacktrace exception.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace XMLStorageAndFilter
{
public class Books
{
public Books()
{
Page = new List<Page>();
}
public string Title { get; set; }
public Author Author { get; set; }
public List<Page> Page { get; set; }
}
public class Author
{
public string FirstName { get; set; }
}
public class Page
{
public string Heading { get; set; }
}
public class Program2
{
static void Main()
{
Page page = new Page();
page.Heading = "Heading";
Books bok = new Books();
bok.Title = "Title";
bok.Author = new Author() { FirstName = "FirstName" };
bok.Page.Add(page);
List<Books> testList = new List<Books>();
testList.Add(bok);
IQueryable<Books> queryableTestData = testList.AsQueryable<Books>();
ParameterExpression pe11 = Expression.Parameter(typeof(Books), "p");
Expression left11 = Expression.Property(pe11, "Title");
Expression right11 = Expression.Constant("Title");
Expression e11 = Expression.Equal(left11, right11);
var author = Expression.Property(pe11, "Author");
left11 = Expression.Property(author, "FirstName");
right11 = Expression.Constant("FirstName");
Expression e21 = Expression.Equal(left11, right11);
Expression predicateBody11 = Expression.And(e11, e21);
Expression<Func<Books, bool>> condition = Expression.Lambda
<Func<Books, bool>>(predicateBody11, new ParameterExpression[] { pe11 });
var q = queryableTestData.Where(condition);
var page1 = Expression.Property(pe11, "Page");
left11 = Expression.Property(page1, "Heading");
right11 = Expression.Constant("Heading");
Expression e22 = Expression.Equal(left11, right11);
Expression predicateBody12 = Expression.And(e11, e22);
Expression<Func<Books, bool>> condition2 = Expression.Lambda
<Func<Books, bool>>(predicateBody12, new ParameterExpression[] { pe11 });
var qq1 = queryableTestData.Where(condition2);
}
}
}
Exception Message:-
{"Instance property 'Heading' is not defined for type >'System.Collections.Generic.List`1[XMLStorageAndFilter.Page]'"}
StackTrace:-
at System.Linq.Expressions.Expression.Property(Expression expression, String propertyName)
at XMLStorageAndFilter.Program2.Main() in c:\Users\Administrator\Documents\Visual Studio 2013\Projects\XMLStorageAndFilter\NavProperty.cs:line 61
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
You can use the method described here.
You would need to cast the result of the method to Expression<Func<T,bool>>. T being your type.
I will provide an complete example when i get home.
Edit:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Collections;
using System.Reflection;
namespace ExpressionPredicateBuilder
{
public enum OperatorComparer
{
Contains,
StartsWith,
EndsWith,
Equals = ExpressionType.Equal,
GreaterThan = ExpressionType.GreaterThan,
GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual,
LessThan = ExpressionType.LessThan,
LessThanOrEqual = ExpressionType.LessThan,
NotEqual = ExpressionType.NotEqual
}
public class ExpressionBuilder
{
public static Expression<Func<T,bool>> BuildPredicate<T>(object value, OperatorComparer comparer, params string[] properties)
{
var parameterExpression = Expression.Parameter(typeof(T), typeof(T).Name);
return (Expression<Func<T, bool>>)BuildNavigationExpression(parameterExpression, comparer, value, properties);
}
private static Expression BuildNavigationExpression(Expression parameter, OperatorComparer comparer, object value, params string[] properties)
{
Expression resultExpression = null;
Expression childParameter, predicate;
Type childType = null;
if (properties.Count() > 1)
{
//build path
parameter = Expression.Property(parameter, properties[0]);
var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
//if it´s a collection we later need to use the predicate in the methodexpressioncall
if (isCollection)
{
childType = parameter.Type.GetGenericArguments()[0];
childParameter = Expression.Parameter(childType, childType.Name);
}
else
{
childParameter = parameter;
}
//skip current property and get navigation property expression recursivly
var innerProperties = properties.Skip(1).ToArray();
predicate = BuildNavigationExpression(childParameter, comparer, value, innerProperties);
if (isCollection)
{
//build subquery
resultExpression = BuildSubQuery(parameter, childType, predicate);
}
else
{
resultExpression = predicate;
}
}
else
{
//build final predicate
resultExpression = BuildCondition(parameter, properties[0], comparer, value);
}
return resultExpression;
}
private static Expression BuildSubQuery(Expression parameter, Type childType, Expression predicate)
{
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
predicate = Expression.Call(anyMethod, parameter, predicate);
return MakeLambda(parameter, predicate);
}
private static Expression BuildCondition(Expression parameter, string property, OperatorComparer comparer, object value)
{
var childProperty = parameter.Type.GetProperty(property);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(value);
var predicate = BuildComparsion(left, comparer, right);
return MakeLambda(parameter, predicate);
}
private static Expression BuildComparsion(Expression left, OperatorComparer comparer, Expression right)
{
var mask = new List<OperatorComparer>{
OperatorComparer.Contains,
OperatorComparer.StartsWith,
OperatorComparer.EndsWith
};
if(mask.Contains(comparer) && left.Type != typeof(string))
{
comparer = OperatorComparer.Equals;
}
if(!mask.Contains(comparer))
{
return Expression.MakeBinary((ExpressionType)comparer, left, Expression.Convert(right,left.Type));
}
return BuildStringCondition(left, comparer, right);
}
private static Expression BuildStringCondition(Expression left, OperatorComparer comparer, Expression right)
{
var compareMethod = typeof(string).GetMethods().Single(m => m.Name.Equals(Enum.GetName(typeof(OperatorComparer), comparer)) && m.GetParameters().Count() == 1);
//we assume ignoreCase, so call ToLower on paramter and memberexpression
var toLowerMethod = typeof(string).GetMethods().Single(m => m.Name.Equals("ToLower") && m.GetParameters().Count() == 0);
left = Expression.Call(left, toLowerMethod);
right = Expression.Call(right, toLowerMethod);
return Expression.Call(left, compareMethod, right);
}
private static Expression MakeLambda(Expression parameter, Expression predicate)
{
var resultParameterVisitor = new ParameterVisitor();
resultParameterVisitor.Visit(parameter);
var resultParameter = resultParameterVisitor.Parameter;
return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}
private class ParameterVisitor : ExpressionVisitor
{
public Expression Parameter
{
get;
private set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Parameter = node;
return node;
}
}
}
}
This can be used like
var predicate = ExpressionBuilder.BuildPredicate<Books>("Heading",OperatorComparer.Equals,"Page","Heading");
query = query.Where(predicate);
Based on your description I'm not sure that you need Expression. Creating an Expression with a complex object model is quite difficult. Do you really need to create dynamic expression or you simply need to create a dynamic query? If the object model is fixed then you don't need Expression.
I suggest first of all to clean your object model:
Rename the Books class to Book (this class represents a Book not a list of books)
Rename property Page to Pages (this property returns a list of pages)
Now you can write a dynamic where using just LINQ and one or more helper functions one for each property that you need to search. For example to search for Heading you can write:
private static bool SearchByHeading(Book b, string heading)
{
if (string.IsNullOrEmpty(heading))
return true;
else
return b.Pages.Any(p => p.Heading == heading);
}
Here you can also see why your previous code didn't work. The expression to search for a given Heading is book.Pages.Any(p => p.Heading == x) and not book.Pages.Heading == x.
In any case given one or more functions like this you can rewrite your code with something like:
using System.Collections.Generic;
using System.Linq;
namespace XMLStorageAndFilter
{
public class Book
{
public Book()
{
Pages = new List<Page>();
}
public string Title { get; set; }
public Author Author { get; set; }
public List<Page> Pages { get; set; }
}
public class Author
{
public string FirstName { get; set; }
}
public class Page
{
public string Heading { get; set; }
}
public class Program2
{
static void Main()
{
Page page = new Page();
page.Heading = "Heading1";
Book bok = new Book();
bok.Title = "Title1";
bok.Author = new Author() { FirstName = "FirstName1" };
bok.Pages.Add(page);
List<Book> testList = new List<Book>();
testList.Add(bok);
var searchResult = Search(testList,
title: "Title1",
author: "FirstName1",
heading: "Heading1");
}
private static IEnumerable<Book> Search(IEnumerable<Book> books, string author = null, string title = null, string heading = null)
{
return books
.Where((b) => SearchByAuthor(b, author))
.Where((b) => SearchByHeading(b, heading))
.Where((b) => SearchByTitle(b, title))
.ToList();
}
private static bool SearchByAuthor(Book b, string author)
{
if (string.IsNullOrEmpty(author))
return true;
else
return b.Author.FirstName == author;
}
private static bool SearchByTitle(Book b, string title)
{
if (string.IsNullOrEmpty(title))
return true;
else
return b.Title == title;
}
private static bool SearchByHeading(Book b, string heading)
{
if (string.IsNullOrEmpty(heading))
return true;
else
return b.Pages.Any(p => p.Heading == heading);
}
}
}
I have skipped search values when null or empty, just an example.
This code has also the advantage that is verified at compile time.
Update
The way to query a collection has been answered in the following:
Building a dynamic expression tree to filter on a collection property
Original Response
I believe Davide Lcardi is correct with his statement:
Heading is book.Pages.Any(p => p.Heading == x) and not book.Pages.Heading == x.
if you want to query the list you need to use Any() method to do so. I could not get it exactly right but it should look something like the following using Expression.Call:
ParameterExpression pe41 = Expression.Parameter(typeof (Page), "pg");
Expression left41 = Expression.Property(pe41, "Heading");
Expression right41 = Expression.Constant("Heading");
Expression e41 = Expression.Equal(left41, right41);
var methodCall = Expression.Call( Expression.Property(pe11, "Pages"), "Any", new Type[] {typeof(Page), typeof(Boolean)}, e41 );
I am getting this error: No method 'Any' exists on type 'System.Collections.Generic.List`1[SO.Page]'. SO is my NameSpace where class Page exists.
I think I am not sending the correct Type or the whole call may be incorrect. I think this is the correct direction thought to help you find your solution.
Here are some examples I was looking at:
http://msdn.microsoft.com/en-us/library/bb349020(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/dd402755(v=vs.110).aspx
http://community.bartdesmet.net/blogs/bart/archive/2009/08/10/expression-trees-take-two-introducing-system-linq-expressions-v4-0.aspx
http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx
On the whole, it is not bad to consider DynamicLinq when you are dealing with dynamic matters
You should use Contains - you are looking inside a list - not Equals.

Get the type of a member in a Expression

I'm trying to learn expressions, and I cannot find how to get the member type of a property in a expression. For example, consider this method:
static IEnumerable<Y> Filter<X,Y>(this IEnumerable<Person> p, Expression<Func<Person,X>> select, Expression<Func<X, Y>> format)
{
foreach (var item in p)
{
// member name
var m = ((MemberExpression)select.Body).Member;
// member attributes
var attributes = m.GetCustomAttributes(false);
// member type?
var a = select.Compile().Invoke(item);
var b = format.Compile().Invoke(a);
m.ToString();
yield return b;
}
}
It doesn't do anything interesting, it is just for trying. If I do:
String y = _persons.Filter(p => p.DateOfBirth, d => d.ToString("yyyy")).ToArray().Single();
I can get in Y the year of the person in the collection. I want to know the type of "p.DateOfBirth" in the "Filter" method. How could I do that without using reflection?
Regards.
Assuming I've understood you correctly, you just need the Expression.Type property:
using System;
using System.Linq.Expressions;
class Person
{
public DateTime DateOfBirth { get; set; }
}
public class Test
{
static void Main()
{
Expression<Func<Person, DateTime>> expression = p => p.DateOfBirth;
MemberExpression memberExpression = (MemberExpression) expression.Body;
Console.WriteLine(memberExpression.Type); // Prints System.DateTime
}
}

Categories