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();
Related
Is it possible to dynamically build an IQueryable/Linq Expression with filtering criteria based on a NESTED/Child List Object's Property.
I have not included all code here - particularly the code around Pagination but I hope there is sufficient detail. Things to note is my use of EFCore5 and Automapper ProjectTo extension method.
For Example:
public class PersonModel
{
public int Id { get; set; }
public PersonName Name { get; set; }
public List<Pet> Pets { get; set; }
}
[Owned]
public class PersonName
{
public string Surname { get; set; }
public string GivenNames { get; set; }
}
public class Pet
{
public string Name { get; set; }
public string TypeOfAnimal { get; set; }
}
Here is my WebApi Controller.
[HttpGet(Name = nameof(GetAllPersons))]
public async Task<ActionResult<IEnumerable<PersonDTO>>> GetAllPersons(
[FromQuery] QueryStringParameters parameters)
{
IQueryable<Person> persons = _context.Persons;
parameters.FilterClauses.ForEach(filter =>
persons = persons.Where(filter.name, filter.op, filter.val));
// Note the use of 'Where' Extension Method.
var dTOs = persons
.ProjectTo<PersonDTO>(_mapper.ConfigurationProvider);;
var pagedPersons = PaginatedList<PersonDTO>
.CreateAsync(dTOs, parameters);
return Ok(await pagedPersons);
}
To query for all people with a Name.GivenNames property equal to "John" I would issue a GET call such as;
http://127.0.0.1/api/v1.0/?Filter=Name.GivenNames,==,John
This works perfectly fine.
However I would like to query for all people with a Pet with a Name property equal to "Scruffy" I would issue a GET call such as;
http://127.0.0.1/api/v1.0/?Filter=Pets.Name,==,Scruffy
Somewhat expectedly it throws the following exception on the line of code in BuildPredicate Function. This is because "Pets" is Type is a "List"... not a "Pet"
var left = propertyName.Split...
Instance property 'Pet:Name' is not defined for type
System.Collections.Generic.List`1[Person]' (Parameter 'propertyName')
Here are the Extension Methods.
public static class ExpressionExtensions
{
public static IQueryable<T> Where<T>(this IQueryable<T> source, string propertyName, string comparison, string value)
{
return source.Where(BuildPredicate<T>(propertyName, comparison, value));
}
}
public static Expression<Func<T, bool>> BuildPredicate<T>(string propertyName, string comparison, string value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var left = propertyName.Split('.').Aggregate((Expression)parameter, Expression.Property);
var body = MakeComparison(left, comparison, value);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
private static Expression MakeComparison(Expression left, string comparison, string value)
{
switch (comparison)
{
case "==":
return MakeBinary(ExpressionType.Equal, left, value);
case "!=":
return MakeBinary(ExpressionType.NotEqual, left, value);
case ">":
return MakeBinary(ExpressionType.GreaterThan, left, value);
case ">=":
return MakeBinary(ExpressionType.GreaterThanOrEqual, left, value);
case "<":
return MakeBinary(ExpressionType.LessThan, left, value);
case "<=":
return MakeBinary(ExpressionType.LessThanOrEqual, left, value);
case "Contains":
case "StartsWith":
case "EndsWith":
return Expression.Call(MakeString(left), comparison, Type.EmptyTypes, Expression.Constant(value, typeof(string)));
default:
throw new NotSupportedException($"Invalid comparison operator '{comparison}'.");
}
}
private static Expression MakeString(Expression source)
{
return source.Type == typeof(string) ? source : Expression.Call(source, "ToString", Type.EmptyTypes);
}
private static Expression MakeBinary(ExpressionType type, Expression left, string value)
{
object typedValue = value;
if (left.Type != typeof(string))
{
if (string.IsNullOrEmpty(value))
{
typedValue = null;
if (Nullable.GetUnderlyingType(left.Type) == null)
left = Expression.Convert(left, typeof(Nullable<>).MakeGenericType(left.Type));
}
else
{
var valueType = Nullable.GetUnderlyingType(left.Type) ?? left.Type;
typedValue = valueType.IsEnum ? Enum.Parse(valueType, value) :
valueType == typeof(Guid) ? Guid.Parse(value) :
valueType == typeof(DateTimeOffset) ? DateTimeOffset.ParseExact(value, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal) :
Convert.ChangeType(value, valueType);
}
}
var right = Expression.Constant(typedValue, left.Type);
return Expression.MakeBinary(type, left, right);
}
Is there anyway to adapt this code to detect that if one of the nested properties is a LIST that it builds an 'Inner Predicate' to do a query on the child collection? ie: Enumerable.Any() ?
Working with raw expression tree's, it sometimes helps to start with an example, let the C# compiler have a go at it, and work backwards. eg;
Expression<Func<Person,bool>> expr = p => p.Pets.Any(t => t.Foo == "blah");
Though the compiler does take a shortcut in IL to specify type members that can't be decompiled.
The trick here is to make your method recursive. Instead of assuming that you can get each property;
var left = propertyName.Split('.').Aggregate((Expression)parameter, Expression.Property);
If you find a collection property in the list, you need to call BuildPredicate<Pet> with the remaining property string. Then use the return value as the argument to call .Pets.Any(...).
Perhaps something like;
public static Expression<Func<T, bool>> BuildPredicate<T>(string propertyName, string comparison, string value)
=> (Expression<Func<T, bool>>)BuildPredicate(typeof(T), propertyName.Split('.'), comparison, value);
public static LambdaExpression BuildPredicate(Type t, Span<string> propertyNames, string comparison, string value)
{
var parameter= Expression.Parameter(t, "x");
var p = (Expression)parameter;
for(var i=0; i<propertyNames.Length; i++)
{
var method = p.Type.GetMethods().FirstOrDefault(m => m.Name == "GetEnumerator" && m.ReturnType.IsGenericType);
if (method != null)
{
BuildPredicate(method.ReturnType.GetGenericArguments()[0], propertyNames.Slice(i), comparison, value);
// TODO ...
}
else
p = Expression.Property(p, propertyNames[i]);
}
// TODO ...
return Expression.Lambda(body, parameter);
}
Let's say I have this expression:
e => e.Name.StartsWith(GetArgument())
Where GetArgument() is defined as follows:
public string GetArgument() { return "Lu"; }
I want this expression to be translated to the following string:
"begins_with(Name, Lu)"
I have developed an expression visitor that visits each sub expression in a recursive manner:
public class MyExpressionTranslator : ExpressionVisitor, IExpressionTranslator<string>
{
// ....
//Implementing IExpressionTranslator<string>
public string Translate(Expression expr)
{
//Begin visiting the expression and its sub expressions
base.Visit(expr);
// I need to return the string here
return null;
}
// Overrides method from base class ExpressionVisitor
protected override MethodCallExpression VisitMethodCall(MethodCallExpression expr)
{
//This method is called when a method call sub expression is visited
//For example, I can check if the method being called is "StartsWith"
if(expr.Method.Name == "StartsWith")
{
// I have no idea what to do here
}
//Proceeds to visit this expression's sub expressions
return base.VisitMethodCall(expr);
}
}
I would use this class as follows:
MyExpressionTranslator translator = // new MyExpressionTranslator(...)
Expression<Func<SomeClass, bool>> expr = e => e.Name.StartsWith(GetArgument());
string result = translator.Translate(expr);
// result should be "begins_with(Name, Lu)"
Providing my base class has a virtual visit method for each expression type (be it a constant, an argument, a method call, or any other), how can I build the expected string output?
What are you trying to accomplish? Something like the below might work. It won't be easy to fill out the other methods though.
class Program
{
static void Main(string[] args)
{
Expression<Func<TestObject, bool>> expr = e => e.FirstName.StartsWith(GetArgument());
var visitor = new MyExpressionTranslator();
var translation = visitor.Translate(expr); // = "begins_with(FirstName, Lu)"
}
static string GetArgument()
{
return "Lu";
}
}
class TestObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IExpressionTranslator<T>
{
T Translate(Expression expr);
}
public class MyExpressionTranslator : ExpressionVisitor, IExpressionTranslator<string>
{
private StringBuilder _sb = null;
public string Translate(Expression expr)
{
_sb = new StringBuilder();
base.Visit(expr);
// I need to return the string here
return _sb.ToString();
}
protected override Expression VisitMethodCall(MethodCallExpression expr)
{
if (expr.Method.Name == "StartsWith")
{
var mainArg = expr.Arguments[0];
var lambda = Expression.Lambda<Func<string>>(mainArg);
var arg = lambda.Compile()();
var member = expr.Object as MemberExpression;
if (member != null)
{
_sb.AppendFormat("begins_with({0}, {1})", member.Member.Name, arg);
}
else
{
//Don't know what you want here.
_sb.AppendFormat("begins_with({0}, {1}))", "(obj)", arg);
}
}
return base.VisitMethodCall(expr);
}
}
Is there any way to ensure that I can only pass in an expression that points to property on the a class?
public class Foo
{
public string Bar { get; set; }
public void DoSomething()
{
HelperClass.HelperFunc(() => Bar);
}
}
public static class HelperClass
{
public static void HelperFunc(Expression<Func<string>> expression)
{
// Ensure that the expression points to a property
// that is a member of the class Foo (or throw exception)
}
}
Also, if need be, I can change the signature to pass the actual class in as well...
Here is extension method, which converts lambda to property. If lambda does not points to property, exception is thrown
public static PropertyInfo ToPropertyInfo(this LambdaExpression expression)
{
MemberExpression body = expression.Body as MemberExpression;
if (body != null)
{
PropertyInfo member = body.Member as PropertyInfo;
if (member != null)
{
return member;
}
}
throw new ArgumentException("Property not found");
}
I was pretty sure that this was possible, but for some reason, I can't seem to figure this out... I am trying to make an extension method off of Type, that will take in a Func to a property from that type, and extract a DefaultValue from the DefaultValueAttribute.
I can get it working, but only if I specify the type arguments for the GetDefaultValue function call. Here is my code as I have it currently:
Person entity:
public class Person
{
public string FirstName { get; set; }
[DefaultValue("1234")]
public string DOB { get; set; }
}
Calls to the method:
//Messing around in LinqPad - .Dump() is LinqPad method
//Works
//typeof(Person).GetDefaultValue<Person, string>(x=>x.DOB).Dump();
//Trying to get to work
//Can't figure out how to get it to infer that TIn is of the Type type.....
typeof(Person).GetDefaultValue(x=> x.DOB).Dump();
This is where the method calls are going to... I am just trying to figure out the means right now before I incorporate into my actual program... Error checking will come into play once I've figured out either how to do it, or to give up b/c it can't be done...
public static class Extensions
{
// Works
// public static TProperty GetDefaultValue<TModel, TProperty>(this Type type, Expression<Func<TModel, TProperty>> exp)
// {
// var property = typeof(TModel).GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
// var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
// return (TProperty)defaultValue.Value;
// }
//trying to get to work
//I know that I can't do the following, but it is basically what I am trying to do... I think!
public static TProperty GetDefaultValue<TProperty>(this Type type, Expression<Func<typeof(type), TProperty>> exp)
{
var property = type.GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
return (TProperty)defaultValue.Value;
}
//GetFullPropertyName c/o: http://stackoverflow.com/users/105570/
//ref: http://stackoverflow.com/questions/2789504/
public static string GetFullPropertyName<TModel, TProperty>(Expression<Func<TModel, TProperty>> exp)
{
MemberExpression memberExp;
if (!TryFindMemberExpression(exp.Body, out memberExp))
return String.Empty;
var memberNames = new Stack<string>();
do
memberNames.Push(memberExp.Member.Name);
while (TryFindMemberExpression(memberExp.Expression, out memberExp));
return String.Join(".", memberNames.ToArray());
}
private static bool TryFindMemberExpression(Expression exp, out MemberExpression memberExp)
{
memberExp = exp as MemberExpression;
if (memberExp != null)
return true;
if (IsConversion(exp) && exp is UnaryExpression)
{
memberExp = ((UnaryExpression)exp).Operand as MemberExpression;
if (memberExp != null)
return true;
}
return false;
}
private static bool IsConversion(Expression exp)
{
return exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked;
}
}
Am I crazy, or is this actually possible? Thank you in advance for your help!
That's not how it works- typeof(Person) doesn't have a property called DOB, but a class whose type is Person does. What you want is to make your extension method generic:
public static TValue GetDefaultValue<TClass, TValue>(this TClass val, Expression<Func<TClass, TValue>> getter) {
var type = typeof(TClass);
var property = type.GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
return (TProperty)defaultValue.Value;
}
and call it something like:
Person somePerson = GetMeAPerson();
somePerson.GetDefaultValue(p=>p.DOB);
Note that I have not tested the above, but I've seen similar code in the past that worked. That said, I suspect that what you were originally trying to do isn't very appealing this way because you need to make a Person instance first.
Another, possibly more appealing approach is to not make it an extension method at all:
public static TValue GetDefaultValue<TClass, TValue>(Expression<Func<TClass, TValue>> getter) {
var type = typeof(TClass);
var property = type.GetProperties().ToList().Single(p => p.Name == GetFullPropertyName(exp));
var defaultValue = (DefaultValueAttribute)property.GetCustomAttributes(typeof(DefaultValueAttribute), false).FirstOrDefault();
return (TProperty)defaultValue.Value;
}
Then you can call it without an instance, but the inference isn't as nice):
var defaultValue = GetDefaultValue<Person, DateTime>(p => p.DOB);
I was wondering if there is something in c# to be able to pass a member of a class to another function that will use this member to get a value. So get a value of a field determined only which one at runtime. Something like in other languages (PHP at least I think) that you can do
a.b = "something"
but also
a["b"] = "something";
edit: actually not so good an example since a string is used, sorry
For clarity an example of what I'd like to be able to do:
class A
{
int x;
int y;
}
void somethingsomething<T>(T class, SomeMagicFieldClass f)
{
dosomethingwith(somemethodthatgivesmethevalueoffield(class, f));
}
Where then I can call the method like this:
A a = new A();
somethingsomething(a, A.x); //hypothetical notation
somethingsomething(a, A.y);
I now have something similar where I do:
somethingsomething(a, "x");
somethingsomething(a, "y");
I then go find the field using introspection API (also trying GetProperty)
MemberInfo memberInfo = item.GetType().GetField(fieldName);
This works but the disadvantage is that the fields passed as a string won't get updated when "refactoring" fieldnames in visual studio, so I was thinking maybe there exists something like this in c# that would get refactored automatically when changing field names?
Thanks a lot for reading this boring question
Your example looks a lot like a LINQ key selector, in that form it would look like:
A a = new A();
somethingsomething(a, p => p.x);
You can do some nice refactor-friendly things with LINQ Expressions. Here is a snippet of utilty code I used for such occasions. It allows you to get the Name, Type and Value of a property (it won't work with fields without modifications). There's also a setter for the value.
public static void Main(string[] args) {
var test = new { Test1 = 42, Test2 = "123", Test3 = 3.14195 };
somethingSomething(test, t => t.Test1);
somethingSomething(test, t => t.Test2);
somethingSomething(test, t => t.Test3);
}
static void somethingSomething<TObj,TProperty>(TObj obj, Expression<Func<TObj,TProperty>> expr) {
var accessor = GetMemberAccessor(expr, obj);
String name = accessor.Name;
TProperty value = accessor.Value;
String typeName = accessor.Type.Name;
Console.WriteLine("{0} = {1} ({2})", name, value, typeName);
}
The output of that would be:
Test1 = 42 (Int32)
Test2 = 123 (String)
Test3 = 3.14195 (Double)
To make this work, I used the following helper function and class:
public static MemberAccessor<TReturn> GetMemberAccessor<TObj,TReturn>(Expression<Func<TObj, TReturn>> expr, TObj tar) {
var body = expr.Body;
MemberExpression memberExpression = null;
if (body is UnaryExpression) {
var ue = (UnaryExpression)body;
memberExpression = (MemberExpression)ue.Operand;
} else if (body is MemberExpression)
memberExpression = (MemberExpression)body;
else
throw new NotImplementedException("can't get MemberExpression");
String name = memberExpression.Member.Name;
return new MemberAccessor<TReturn>(tar, name);
}
public class MemberAccessor<T> {
private readonly PropertyDescriptor propertyDesc;
private readonly Object target;
public MemberAccessor(Object target, String propertyName) {
this.target = target;
this.propertyDesc = TypeDescriptor.GetProperties(target)[propertyName];
}
public String Name {
get { return propertyDesc.Name; }
}
public Type Type {
get { return propertyDesc.PropertyType; }
}
public T Value {
get { return (T)Convert.ChangeType(propertyDesc.GetValue(target), typeof(T)); }
set { propertyDesc.SetValue(target, value); }
}
}
Mr. Plunkett is correct; a dynamic type will do the job. Luckily, the .NET 4 team included a handy object called the ExpandoObject that solves that for you.
You asked how to
pass a member of a class to another
function that will use this member to
get a value
You can usedelegates for this
class A
{
public string aField;
public string aProperty{get{return "someval";}}
public string aMemberFunction(){return "someval";}
}
void get_a_value(Func<string> func)
{
string theValue = func();
}
// use it:
A a = new A();
get_a_value( () => a.aField);
get_a_value( () => a.aProperty);
get_a_value( () => a.aMemberFunction());
What you don't get this way, of course, is a separation of parameters for the memberfunction and the object you are passing.