It is possible to do this:
public static void SomeMethod<TFunc>(Expression<TFunc> expr)
{
//LambdaExpression happily excepts any Expession<TFunc>
LambdaExpression lamb = expr;
}
and call it elsewhere passing a lambda for the parameter:
SomeMethod<Func<IQueryable<Person>,Person>>( p=>p.FirstOrDefault());
I would instead like to pass an expression as a parameter to an attribute constructor.
Is it possible to do the below?
class ExpandableQueryAttribute: Attribute {
private LambdaExpression someLambda;
//ctor
public ExpandableQueryMethodAttribute(LambdaExpression expression)
{
someLambda = expression
}
}
//usage:
static LambdaExpression exp =
(Expression<Func<IQueryable<Person>, Person>>)
(p => p.FirstOrDefault());
[ExpandableQueryAttribute(exp)] //error here
// "An attribute argument must be a constant expression, typeof expression
// or array creation expression of an attribute parameter type"
My goal is to specify a method or lambda in the constructor of the attribute(even if I have to declare a full named method and pass the name of the method somehow, that'd be fine to).
Parameter types can change, but it is important that the attribute constructor can take that parameter and in some way be able to assign it to a field of type LambdaExpression
I want the declaration of the lambda/method to be just above the call to the attribute constructor, or inline, so that you don't have to go far to see what is being passed.
So these alternatives would be fine, but no luck getting them to work:
public static ... FuncName(...){...}
[ExpandableQueryAttribute(FuncName)]
// ...
or
//lambdas aren't allowed inline for an attribute, as far as I know
[ExpandableQueryAttribute(q => q.FirstOrDefault())]
// ...
The existing work around is to pass a number ID to the constructor(satisfying the "argument must be a constant" requirement), which is used by the constructor to do a lookup in a dictionary where expressions have been added previously. Was hoping to improve/simplify this, but I have a feeling it doesn't get any better due to limitations on attribute constructors.
how about this:
class ExpandableQueryAttribute : Attribute
{
private LambdaExpression someLambda;
//ctor
public ExpandableQueryAttribute(Type hostingType, string filterMethod)
{
someLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
// could also use a static method
}
}
this should let you assign your lambda to a field and then suck it in at runtime, although in general I would prefer to use something like PostSharp to do this at compile time.
simple usage example
public class LambdaExpressionAttribute : Attribute
{
public LambdaExpression MyLambda { get; private set; }
//ctor
public LambdaExpressionAttribute(Type hostingType, string filterMethod)
{
MyLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
}
}
public class User
{
public bool IsAdministrator { get; set; }
}
public static class securityExpresions
{
public static readonly LambdaExpression IsAdministrator = (Expression<Predicate<User>>)(x => x.IsAdministrator);
public static readonly LambdaExpression IsValid = (Expression<Predicate<User>>)(x => x != null);
public static void CheckAccess(User user)
{
// only for this POC... never do this in shipping code
System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
var method = stackTrace.GetFrame(1).GetMethod();
var filters = method.GetCustomAttributes(typeof(LambdaExpressionAttribute), true).OfType<LambdaExpressionAttribute>();
foreach (var filter in filters)
{
if ((bool)filter.MyLambda.Compile().DynamicInvoke(user) == false)
{
throw new UnauthorizedAccessException("user does not have access to: " + method.Name);
}
}
}
}
public static class TheClass
{
[LambdaExpression(typeof(securityExpresions), "IsValid")]
public static void ReadSomething(User user, object theThing)
{
securityExpresions.CheckAccess(user);
Console.WriteLine("read something");
}
[LambdaExpression(typeof(securityExpresions), "IsAdministrator")]
public static void WriteSomething(User user, object theThing)
{
securityExpresions.CheckAccess(user);
Console.WriteLine("wrote something");
}
}
static void Main(string[] args)
{
User u = new User();
try
{
TheClass.ReadSomething(u, new object());
TheClass.WriteSomething(u, new object());
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
This is not possible because what you can pass into an attribute needs to fit into the CLR's binary DLL format and there is no way to encode arbitrary object initialization. For the same you you cannot pass a nullable value for example. The restrictions are very strict.
Although you cannot have complex constructor for attributes, in some situation a work-aound is to have a public property for that attribute and update it at run-time.
self point to an object of the class that contains some attributes on its properties. LocalDisplayNameAttribute is a custom attribute.
The following code will set ResourceKey property of my custom attribute class at run-time. Then at that point you can override DisplayName to outpiut whatever text you want.
static public void UpdateAttributes(object self)
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(self))
{
LocalDisplayNameAttribute attr =
prop.Attributes[typeof(LocalDisplayNameAttribute)]
as LocalDisplayNameAttribute;
if (attr == null)
{
continue;
}
attr.ResourceKey = prop.Name;
}
}
Use dymanic linq: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
You can make the attribute's constructor take a string which evaluates to an expression.
Related
I am currently creating a custom way of deep-copying my objects. I use a static class for this functionality.
public static class CopyServer
{
public static int CopyDeep(int original)
{
return original;
}
//not shown: same for all other value types I use (long, float,...)
public static T CopyDeep<T>(T original) where T: ICopyAble
{
if (original == null)
return default;
if (original is ICopyAutofields)
return CopyAutofields(original);
return (T)original.CopyDeep();
}
private static T CopyAutofields<T>(T original)
{
Delegate del;
if (!_copyFunctions.TryGetValue(typeof(T), out del))
{
//not shown: Building expression for parameter etc.
foreach (var fieldInfo in typeof(T).GetFields())
{
//not shown: checking options set by custom attributes
MethodInfo methodInfo = typeof(CopyServer).GetMethod("CopyDeep", new[] { fieldInfo.FieldType });
//I can't remove the second param without getting an AmbiguousMatchException
if (methodInfo == null)
{
throw new Exception($"CopyDeep not defined for type {fieldInfo.FieldType}");
}
if (methodInfo.IsGenericMethod)
methodInfo = methodInfo.MakeGenericMethod(fieldInfo.FieldType);
Expression call = Expression.Call(methodInfo, readValue);
//not shown: Assign Expression
}
//not shown: return Expression and compiling
}
return ((Func<T, T>)del)(original);
}
}
I use T CopyAutofields<T> to build functions (by building and compiling Expression Trees) so I don't have to create copy-functions for each and every class I want to copy by hand. I control the copy-behaviour with Custom Attributes (I left this part in the code above since it is not relevant for my problem).
The code works fine as long as long as only fields with types for which a non-generic function exists are used. But it is not able to retrieve my generic function T CopyDeep<T>.
Example:
//This works:
public class Manager : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
}
//This doesn't
//Meaning: typeof(CopyServer).GetMethod("copyDeep", new[] { fieldInfo.FieldType });
//in T copyAutofields<T> returns null for the Manager-field and my exception gets thrown
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
}
//This is what I was using before I started using the ICopyAutofields.
//This approach works, but its' too much too write since my classes usually
//have way more than three fields and I occasionally forget to update
//copyDeep()-function if I add new ones.
public class Employee : ICopyAble,ICopyAutofields
{
public string FirstName;
public string LastName;
public Manager Manager;
public IModable CopyDeep()
{
var result = new Employee();
result.FirstName = CopyServer.copyDeep(FirstName);
result.LastName= CopyServer.copyDeep(LastName);
result.Manager= CopyServer.copyDeep(Manager);
return result;
}
}
Long story short: I need a way of getting a matching function for a type T if both generic and non-generic functions with the right name exist.
In .NET 4.7.1 you need to use method GetMethods and filter the results:
class MyClass
{
public T M<T>(T t) { return default(T); }
public int M(int t) { return 0; }
}
var m = typeof(MyClass).GetMethod("M", new[] { typeof(string) }); // null
var m1 = typeof(MyClass).GetMethods()
.Where(mi => mi.Name == "M" && mi.GetGenericArguments().Any())
.First(); // returns generic method
In .NET Standard 2.1 (and .NET Core since 2.1) there is another way to resolve generic type arguments - Type.MakeGenericMethodParameter, like you can see it in this answer.
Also as workaround you can move your copyAutofields<T> method to generic class like CopyAutoFieldServer<T>:
public static class CopyAutoFieldServer<T>
{
public static T copyAutofields(T original) { ... }
}
I have found this solution:
public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute
{
var attrType = typeof(T);
var property = instance.GetType().GetProperty(propertyName);
return (T)property .GetCustomAttributes(attrType, false).First();
}
Code by jgauffin from How to retrieve Data Annotations from code
I always use the extension this way:
foo.GetAttributeFrom<StringLengthAttribute>(nameof(Foo.Bar)).MaximumLength
Is there a way to pass the propertyName by using a lambda like:
foo.GetAttributeFrom<StringLengthAttribute>(f => f.Bar).MaximumLength
Thank you in advance!
You can split the work into two functions in order to bypass specifying all generic parameter type for a generic method restriction
public static object[] GetPropertyAttributes<TObject, TProperty>(
this TObject instance,
Expression<Func<TObject, TProperty>> propertySelector)
{
//consider handling exceptions and corner cases
var propertyName = ((PropertyInfo)((MemberExpression)propertySelector.Body).Member).Name;
var property = instance.GetType().GetProperty(propertyName);
return property.GetCustomAttributes(false);
}
public static T GetFirst<T>(this object[] input) where T : Attribute
{
//consider handling exceptions and corner cases
return input.OfType<T>().First();
}
then use it like
foo.GetPropertyAttributes(f => f.Bar)
.GetFirst<StringLengthAttribute>()
.MaximumLength;
The method can be like this:
public static TAtt GetAttribute<TAtt,TObj,TProperty>(this Rootobject inst,
Expression<Func<TObj,TProperty>> propertyExpression)
where TAtt : Attribute
{
var body = propertyExpression.Body as MemberExpression;
var expression = body.Member as PropertyInfo;
var ret = (TAtt)expression.GetCustomAttributes(typeof(TAtt), false).First();
return ret;
}
If you have a class like this with the attribute:
public class Rootobject
{
[StringLengthAttribute(10)]
public string Name { get; set; }
}
Then you will use it like this:
var obj = new Rootobject();
var max = obj.GetAttribute<StringLengthAttribute, Rootobject, string>((x) => x.Name)
.MaximumLength;
Improvements
Add error checking in case the attribute is not found or the lambda is not for a property etc.
I've ended up with a situation where an function expression tree Expression<Func<TClass, TProperty>> is assigned to a global variable of type object and then later in the code I need to call a different method with the expression. I can't change the global objects type; it has to be object.
The code won't compile when trying to call the second method with the global object unless I cast the object as Expression<Func<TClass, TProperty>>. The problem is I don't know what TProperty is at the point that the second method is called.
I've created a quick demo app to illustrate the point (c# console app written in VS2010) - the real application looks nothing like this.
using System;
using System.Linq.Expressions;
namespace FuncExpressionTree
{
public class ViewModel
{
public string StringProperty { get; set; }
public int IntProperty { get; set; }
}
public class Helper<T>
where T : ViewModel
{
private object _global;
public void Execute()
{
AssignToGlobal((T vm) => vm.StringProperty);
ProcessGlobal();
AssignToGlobal((T vm) => vm.IntProperty);
ProcessGlobal();
}
public void AssignToGlobal<TClass, TProperty>(Expression<Func<TClass, TProperty>> expression)
{
_global = expression;
}
public void ProcessGlobal()
{
// invalid cast exception thrown when IntProperty is assigned to _global
AssignToGlobal((Expression<Func<T, string>>)_global);
}
}
class Program
{
static void Main(string[] args)
{
(new Helper<ViewModel>()).Execute();
}
}
}
If we focus on the Execute() method.
First global is assigned expression for the string property.
ProcessGlobal executes and works because I'm casting to Expression<Func<T, string>>.
Next global is assigned expression for int property.
ProcessGlobal again executes but its at this point that an invalid cast exception is thrown. It would work if I changed it to cast Expression<Func<T, int>> instead but then the string property wouldn't work. Also Expression<Func<T, object>> throws an invalid cast exception.
I feel like Im missing something and that it should be possible to do something with the System.Linq.Expressions namespace to dynamically invoke the the second method (eg AssignToGlobal within ProcessGlobal in the above example).
So how can I get this to work in a generic way?
public void ProcessGlobal()
{
var globalType = _global.GetType(); // globalType = Expression<Func<TClass, TProperty>>
var functionType = globalType.GetGenericArguments()[0]; // functionType = Func<TClass, TProperty>
var functionGenericArguments = functionType.GetGenericArguments(); // v = [TClass, TProperty]
var method = this.GetType().GetMethod("AssignToGlobal").MakeGenericMethod(functionGenericArguments); //functionGenericArguments = AssignToGlobal<TClass, TProperty>
method.Invoke(this, new[] { this._global }); // Call AssignToGlobal<TClass, TProperty>)(this._global);
}
I've started using C# Expression constructs, and I've got a question about how generics are applied in the following situation:
Consider I have a type MyObject which is a base class for many different types. Inside this class I have the following code:
// This is a String Indexer Expression, used to define the string indexer when the object is in a collection of MyObjects
public Expression<Func<MyObject, string, bool>> StringIndexExpression { get; private set;}
// I use this method in Set StringIndexExpression and T is a subtype of MyObject
protected void DefineStringIndexer<T>(Expression<T, string, bool>> expresson) where T : MyObject
{
StringIndexExpression = expression;
}
This is how I use DefineStringIndexer:
public class MyBusinessObject : MyObject
{
public string Name { get; set; }
public MyBusinessObject()
{
Name = "Test";
DefineStringIndexer<MyBusinessObject>((item, value) => item.Name == value);
}
}
However in the assignment inside DefineStringIndexer I get the compile error:
Cannot implicitly convert type
System.Linq.Expression.Expression< MyObject, string, bool > to
System.Linq.Expression.Expression < MyBusinessObject, string, bool >>
Can I use Generics with C# Expressions in this situation? I want to use T in DefineStringIndexer so I can avoid casting MyObject inside the lambda.
The assignment will not work, because the Func<MyBusinessObject,string,bool> type is not assignment-compatible with Func<MyObject,string,bool>. However, the parameters of the two functors are compatible, so you can add a wrapper to make it work:
protected void DefineStringIndexer<T>(Func<T,string,bool> expresson) where T : MyObject {
StringIndexExpression = (t,s) => expression(t, s);
}
Would this work better for you?
Edit: Added <T> to constraint - think you will need that :)
class MyObject<T>
{
// This is a String Indexer Expression, used to define the string indexer when the object is in a collection of MyObjects
public Expression<Func<T, string, bool>> StringIndexExpression { get; private set;}
// I use this method in Set StringIndexExpression and T is a subtype of MyObject
protected void DefineStringIndexer<T>(Expression<T, string, bool>> expresson)
where T : MyObject<T> // Think you need this constraint to also have the generic param
{
StringIndexExpression = expression;
}
}
then:
public class MyBusinessObject : MyObject<MyBusinessObject>
{
public string Name { get; set; }
public MyBusinessObject()
{
Name = "Test";
DefineStringIndexer<MyBusinessObject>((item, value) => item.Name == value);
}
}
I have several classes with attributes assigned to them. The one I'm mostly interested in is the FieldLength.MaxLength value.
/// <summary>
/// Users
/// </summary>
[Table(Schema = "dbo", Name = "users"), Serializable]
public partial class Users
{
/// <summary>
/// Last name
/// </summary>
[Column(Name = "last_name", SqlDbType = SqlDbType.VarChar)]
private string _LastName;
[FieldLength(MaxLength=25), FieldNullable(IsNullable=false)]
public string LastName
{
set { _LastName = value; }
get { return _LastName; }
}
}
I need to know if it's possible to write some kind of extension method for the properties in my class to return the MaxLength value of the FieldLength attribute?
For instance. I'd like to be able to write something like the following…
Users user = new Users();
int lastNameMaxLength = user.LastName.MaxLength();
No, this is not possible. You could add an extension method on Users though:
public static int LastNameMaxLength(this Users user) {
// get by reflection, return
}
To save typing, you could further refine Jason's Extension to something like this.
public static void MaxLength<T>(this T obj, Expression<Func<T, object>> property)
This way it will appear on all object (unless you specify a where T restriction) and you have a compile-time safe implementation of the Property Accessor as you would use the code as:
user.MaxLength(u => u.LastName);
No. Because the syntax you propose returns the value of the LastName property and not the property itself.
In order to retrieve and make use of the attributes, you'll need to use reflection which means you need to know the property itself.
As an idea, you could achieve this neatly by using LINQ's Expression library though to resolve the property for the object.
Example syntax you might look for:
var lastNameMaxLength = AttributeResolver.MaxLength<Users>(u => u.LastName);
Where:
public class AttributeResolver
{
public int MaxLength<T>(Expression<Func<T, object>> propertyExpression)
{
// Do the good stuff to get the PropertyInfo from the Expression...
// Then get the attribute from the PropertyInfo
// Then read the value from the attribute
}
}
I've found this class helpful in resolving properties from Expressions:
public class TypeHelper
{
private static PropertyInfo GetPropertyInternal(LambdaExpression p)
{
MemberExpression memberExpression;
if (p.Body is UnaryExpression)
{
UnaryExpression ue = (UnaryExpression)p.Body;
memberExpression = (MemberExpression)ue.Operand;
}
else
{
memberExpression = (MemberExpression)p.Body;
}
return (PropertyInfo)(memberExpression).Member;
}
public static PropertyInfo GetProperty<TObject>(Expression<Func<TObject, object>> p)
{
return GetPropertyInternal(p);
}
}
That's not possible in that form. The best you can manage is a method that takes a lambda expression, gets the property associated with it and then use reflection to obtain the attribute.
int GetMaxLength<T>(Expression<Func<T,string>> property);
And call it like:
GetMaxLength<Users>((u)=>LastName)
You could write an extension method, but it would have to accept a first parameter of PropertyInfo rather than string (since the string itself has no attributes.) It would look something like this:
public static int GetMaxLength(this PropertyInfo prop)
{
// TODO: null check on prop
var attributes = prop.GetCustomeAttributes(typeof(FieldLengthAttribute), false);
if (attributes != null && attributes.Length > 0)
{
MaxLengthAttribute mla = (MaxLengthAttribute)attributes[0];
return mla.MaxLength;
}
// Either throw or return an indicator that something is wrong
}
You then get the property via reflection:
int maxLength = typeof(Users).GetProperty("LastName").GetMaxLength();