I have a class that builds a url with query string parameters and so on. The class has a method: Url() which returns the complete url composed from the class properties and another method: UrlNew() which allows passing a predicate as parameter for the replacement of the value of one of the properties and THEN Returns the modified URL. Now, I need to modify this function to use TWO parameters, both predicates. How do I do that? I tried modifying the method's parameters as a List of predicates but I probably am not doing something right:
My OLD UrlNew() method looked like this:
public static string Url() (Action<LGUrlBuilder> predicate)
{
var instance = new LGUrlBuilder();
if (predicate != null) predicate(instance);
return instance.BuildUrl();
}
My NEW UrlNew() method looks like this:
public static string UrlNew(List<Action<LGUrlBuilder>> predicateList)
{
var instance = new LGUrlBuilder();
if (predicateList != null && predicateList.Count > 0)
{
foreach (Action<LGUrlBuilder> predicate in predicateList)
{
if (predicate != null) predicate(instance);
}
}
return instance.BuildUrl();
}
This compiles just fine but when I run it, using it in ASPX gives me this error:
CS1660: Cannot convert lambda expression to type 'System.Collections.Generic.List<System.Action<public_site.Library.LG.LGUrlBuilder>>' because it is not a delegate type
I am a C# beginner and I am sure I am doing something completely wrong. Any advice would help. Thanks!
Don't modify the function itself. Modify the method call like this:
UrlNew(x => { func1(x); func2(x); });
But if you really want to take arbitrary number of delegate instances as arguments, try modifying it like:
public static void UrlNew(params Action<LGUrlBuilder>[] list) {
// ... do what you're already doing in the second snippet ...
}
You can call it like:
UrlNew(x => firstthing(), x => secondthing(x), thirdthing);
Side note: An Action<T> is not called a predicate. A predicate returns a boolean value.
How about using the overloaded Action<T1,T2> delegate which accepts 2 parameters.
If you are looking to use a predicate instead which expects a boolean return value then use Func<T1,T2,bool> instead.
Related
UPDATED:
I am trying to write a method to do somes work and before it actually does those works it needs to go through some validations. Those validation varies based on what work it's going to do.
After some thoughts, I still want to use the same patterns with some minor changes.
Now, I want to make the following code works:
SomeClass:
public SomeResponse DoSomething<T>(params Func<T, bool>[] validations)
{
if(validations.All(v=>v(T))
{
some logic..
}
return SomeResponse;
}
Usage:
private Func<SomeRequest, bool> ValidateName = r =>
{return !string.IsNullOrWhiteSpace(r.Name);};
private Func<SomeRequest, bool> ValidatePhone = r =>
{return !string.IsNullOrWhiteSpace(r.Phone);};
var someResponse = SomeClass.DoSomething<SomeRequest>(ValidateName,ValidatePhone);
Again, the code currently doesn't work because it's giving me error on
if(validations.All(v=>v(T))
basically Type parameter is not valid here, and I couldn't find a way to pass in an actual SomeRequest object to the Func.
How should I write the code to loop through all the results return by the list of functions and make sure they are returning true, as well as keeping the flexibility of the Type parameter?
Answer:
Found a way to do that, hope this can be helpful:
Simply modify the method definition to :
SomeClass:
public SomeResponse DoSomething<T>(T request, params Func<T, bool>[] validations)
{
if(validations.All(v=>v(request))
{
some logic..
}
return SomeResponse;
}
Then use it like:
var someResponse = SomeClass.DoSomething<SomeRequest>(someRequest, ValidateName,ValidatePhone);
Please let me know if there is any other solution.
You're almost there!
First note that Validations is an array of Func<Request, bool> - that is, each item in the array is a function which takes a Request as a parameter and returns a bool.
So in order to get the bool out of the Func, you need to apply it to a Request object. In C#, applying a Func to an argument looks just like calling a method. So your LINQ expression will look like this:
validations.All(f => f(request))
This will return true if and only if all of the functions in validations return true (when applied to request).
So your full if block will look like this:
// I'm just guessing what the code to make a Request object will look like
Request request = resource.GetRequest();
if (!validations.All(f => f(request)))
{
throw new Exception("Not all of the validations checked out");
}
You haven't provided enough context for me to tell you where to get a Request object from but I'm sure you can figure it out.
Assuming I'm understanding these types correctly, I would think something like this should work:
if (Validations.All(v => v(resource)))
You don't need to use params here, a simple array will do. You can pass an arbitrary number of Func objects with an array.
public void DoSomething(Resource resource, Func<Request, bool>[] Validations)
Anyway, you can easily use LINQ's All method to check if all elements of an array satisfy a predicate.
if (Validations.All(v => v(resource)))
Working with EF6, in a generic method, I am trying to set some property values like so:
protected void MyFunc<T>(T entity, params Expression<Func<T, dynamic>>[] properiesToCopy) where T: class
{
foreach (var propertyEntry in properiesToCopy)
{
context.Entry(_entityCopy).Property(propertyEntry ).CurrentValue = context.Entry(entity).Property(propertyEntry).CurrentValue;
}
}
Now, I would like to use the same function for reference types of the entity, but then I would have to use DbEntityEntry.Reference function.
How can I know whether the lambda expression (named propertyEntry above) referes to a DbPropertyEntry or to a DbReferenceEntry? Or can my function perform the same action in a better way?
I found the answer, I just had to use DbEntityEntry.Member function, which will work for scalar types, reference types and even collections. However, this only works with a string parameter: so I took the method TryParsePath from the entity framework source code that converts a Lambda path to a string path.
Then I could do:
string path;
foreach (var memberEntry in membersToCopy)
{
if (!Utils.TryParsePath(memberEntry .Body, out path))
{
throw new ArgumentException("Include path not valid", "path");
}
context.Entry(_entityCopy).Member(path).CurrentValue = context.Entry(entity).Member(path).CurrentValue;
}
I have a query which filters results:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
return _context.Context.Quotes.Select(q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => q.User.Id == qpi.ItemOrder))
});
}
In the where clause I'm using the parameter q to match a property against a property from the parameter qpi.
Because the filter will be used in several places I'm trying to rewrite the where clause to an expression tree which would look like something like this:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
return _context.Context.Quotes.Select(q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.AsQueryable().Where(ExpressionHelper.FilterQuoteProductImagesByQuote(q)))
});
}
In this query the parameter q is used as a parameter to the function:
public static Expression<Func<QuoteProductImage, bool>> FilterQuoteProductImagesByQuote(Quote quote)
{
// Match the QuoteProductImage's ItemOrder to the Quote's Id
}
How would I implement this function? Or should I use a different approach alltogether?
If I understand correctly, you want to reuse an expression tree inside another one, and still allow the compiler to do all the magic of building the expression tree for you.
This is actually possible, and I have done it in many occasions.
The trick is to wrap your reusable part in a method call, and then before applying the query, unwrap it.
First I would change the method that gets the reusable part to be a static method returning your expression (as mr100 suggested):
public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}
Wrapping would be done with:
public static TFunc AsQuote<TFunc>(this Expression<TFunc> exp)
{
throw new InvalidOperationException("This method is not intended to be invoked, just as a marker in Expression trees!");
}
Then unwrapping would happen in:
public static Expression<TFunc> ResolveQuotes<TFunc>(this Expression<TFunc> exp)
{
var visitor = new ResolveQuoteVisitor();
return (Expression<TFunc>)visitor.Visit(exp);
}
Obviously the most interesting part happens in the visitor.
What you need to do, is find nodes that are method calls to your AsQuote method, and then replace the whole node with the body of your lambdaexpression. The lambda will be the first parameter of the method.
Your resolveQuote visitor would look like:
private class ResolveQuoteVisitor : ExpressionVisitor
{
public ResolveQuoteVisitor()
{
m_asQuoteMethod = typeof(Extensions).GetMethod("AsQuote").GetGenericMethodDefinition();
}
MethodInfo m_asQuoteMethod;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (IsAsquoteMethodCall(node))
{
// we cant handle here parameters, so just ignore them for now
return Visit(ExtractQuotedExpression(node).Body);
}
return base.VisitMethodCall(node);
}
private bool IsAsquoteMethodCall(MethodCallExpression node)
{
return node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == m_asQuoteMethod;
}
private LambdaExpression ExtractQuotedExpression(MethodCallExpression node)
{
var quoteExpr = node.Arguments[0];
// you know this is a method call to a static method without parameters
// you can do the easiest: compile it, and then call:
// alternatively you could call the method with reflection
// or even cache the value to the method in a static dictionary, and take the expression from there (the fastest)
// the choice is up to you. as an example, i show you here the most generic solution (the first)
return (LambdaExpression)Expression.Lambda(quoteExpr).Compile().DynamicInvoke();
}
}
Now we are already half way through. The above is enough, if you dont have any parameters on your lambda. In your case you do, so you want to actually replace the parameters of your lambda to the ones from the original expression. For this, I use the invoke expression, where I get the parameters I want to have in the lambda.
First lets create a visitor, that will replace all parameters with the expressions that you specify.
private class MultiParamReplaceVisitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> m_replacements;
private readonly LambdaExpression m_expressionToVisit;
public MultiParamReplaceVisitor(Expression[] parameterValues, LambdaExpression expressionToVisit)
{
// do null check
if (parameterValues.Length != expressionToVisit.Parameters.Count)
throw new ArgumentException(string.Format("The paraneter values count ({0}) does not match the expression parameter count ({1})", parameterValues.Length, expressionToVisit.Parameters.Count));
m_replacements = expressionToVisit.Parameters
.Select((p, idx) => new { Idx = idx, Parameter = p })
.ToDictionary(x => x.Parameter, x => parameterValues[x.Idx]);
m_expressionToVisit = expressionToVisit;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression replacement;
if (m_replacements.TryGetValue(node, out replacement))
return Visit(replacement);
return base.VisitParameter(node);
}
public Expression Replace()
{
return Visit(m_expressionToVisit.Body);
}
}
Now we can advance back to our ResolveQuoteVisitor, and hanlde invocations correctly:
protected override Expression VisitInvocation(InvocationExpression node)
{
if (node.Expression.NodeType == ExpressionType.Call && IsAsquoteMethodCall((MethodCallExpression)node.Expression))
{
var targetLambda = ExtractQuotedExpression((MethodCallExpression)node.Expression);
var replaceParamsVisitor = new MultiParamReplaceVisitor(node.Arguments.ToArray(), targetLambda);
return Visit(replaceParamsVisitor.Replace());
}
return base.VisitInvocation(node);
}
This should do all the trick.
You would use it as:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
Expression<Func<Quote, FilteredViewModel>> selector = q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => ExpressionHelper.FilterQuoteProductImagesByQuote().AsQuote()(q, qpi)))
};
selector = selector.ResolveQuotes();
return _context.Context.Quotes.Select(selector);
}
Of course I think you can make here much more reusability, with defining expressions even on a higher levels.
You could even go one step further, and define a ResolveQuotes on the IQueryable, and just visit the IQueryable.Expression and creating a new IQUeryable using the original provider and the result expression, e.g:
public static IQueryable<T> ResolveQuotes<T>(this IQueryable<T> query)
{
var visitor = new ResolveQuoteVisitor();
return query.Provider.CreateQuery<T>(visitor.Visit(query.Expression));
}
This way you can inline the expression tree creation. You could even go as far, as override the default query provider for ef, and resolve quotes for every executed query, but that might go too far :P
You can also see how this would translate to actually any similar reusable expression trees.
I hope this helps :)
Disclaimer: Remember never copy paste code from anywhere to production without understanding what it does. I didn't include much error handling here, to keep the code to minimum. I also didn't check the parts that use your classes if they would compile. I also don't take any responsability for the correctness of this code, but i think the explanation should be enough, to understand what is happening, and fix it if there are any issues with it.
Also remember, that this only works for cases, when you have a method call that produces the expression. I will soon write a blog post based on this answer, that allows you to use more flexibility there too :P
Implementing this your way will cause an exception thrown by ef linq-to-sql parser. Within your linq query you invokes FilterQuoteProductImagesByQuote function - this is interpreted as Invoke expression and it simply cannot be parsed to sql. Why? Generally because from SQL there is no possibility to invoke MSIL method. The only way to pass expression to query is to store it as Expression> object outside of the query and then pass it to Where method. You can't do this as outside of the query you will not have there Quote object. This implies that generally you cannot achieve what you wanted. What you possibly can achieve is to hold somewhere whole expression from Select like this:
Expression<Func<Quote,FilteredViewModel>> selectExp =
q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.AsQueryable().Where(qpi => q.User.Id == qpi.ItemOrder)))
};
And then you may pass it to select as argument:
_context.Context.Quotes.Select(selectExp);
thus making it reusable. If you would like to have reusable query:
qpi => q.User.Id == qpi.ItemOrder
Then first you would have to create different method for holding it:
public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}
Application of it to your main query would be possible, however quite difficult and hard to read as it will require defining that query with use of Expression class.
Is it possible to complete this method? Is it possible in the latest version of C#? Thinking about this as a DSL to configure a system for watching for certain property changes on certain objects.
List<string> list = GetProps<AccountOwner>(x => new object[] {x.AccountOwnerName, x.AccountOwnerNumber});
// would return "AccountOwnerName" and "AccountOwnerNumber"
public List<string> GetProps<T>(Expression<Func<T, object[]>> exp)
{
// code here
}
In C# 6, you'd use:
List<string> list = new List<string>
{
nameof(AccountOwner.AccountOwnerName),
nameof(AccountOwner.AccountOwnerNumber)
};
Before that, you could certainly break the expression tree apart - the easiest way of working out how is probably to either use an expression tree visualizer, or use the code you've got and put a break point in the method (just make it return null for now) and examine the expression tree in the debugger. I'm sure it won't be very complicated - just a bit more than normal due to the array.
You could possibly simplify it using an anonymous type, if you use:
List<string> list = Properties<AccountOwner>.GetNames(x => new {x.AccountOwnerName, x.AccountOwnerNumber});
Then you could have:
public static class Properties<TSource>
{
public static List<string> GetNames<TResult>(Func<TSource, TResult> ignored)
{
// Use normal reflection to get the properties
}
}
If you don't care about the ordering, you could just use
return typeof(TResult).GetProperties().Select(p => p.Name).ToList();
If you do care about the ordering, you'd need to look at the names the C# compiler gives to the constructor parameters instead - it's a bit ugly. Note that we don't need an expression tree though - we only need the property names from the anonymous type. (An expression tree would work just as well, admittedly.)
Without c# 6 and nameof, you could get a property name from a expression tree like:
using System.Linq.Expressions;
//...
static string GetNameOf<T>(Expression<Func<T>> property)
{
return (property.Body as MemberExpression).Member.Name;
}
Using it like:
GetNameOf(() => myObject.Property);
Not directly usable for an array of objects, but you could make an overload to take an array of expressions... something like:
static string[] GetNameOf(IEnumerable<Expression<Func<object>>> properties)
{
return properties.Select(GetNameOf).ToArray();
}
And use it like
GetNameOf(
new Expression<Func<object>>[]
{
() => x.AccountOwnerName,
() => x.AccountOwnerNumber
}
);
Demonstrating fiddle: https://dotnetfiddle.net/GsV96t
Update
If you go this route, the original GetNameOf for a single property won't work for value types (since they get boxed to object in the Expression and now the expression uses Convert internally). This is easily solvable by changing the code to something like:
static string GetNameOf<T>(Expression<Func<T>> property)
{
var unary = property.Body as UnaryExpression;
if (unary != null)
return (unary.Operand as MemberExpression).Member.Name;
return (property.Body as MemberExpression).Member.Name;
}
Updated fiddle: https://dotnetfiddle.net/ToXRuu
Note: in this updated fiddle I've also updated the overloaded method to return a List instead of an array, since that's what was on your original code
i have 5 datafunctions which all return the same type of object (List<source>)
Now i have to publish them in a WCF in which i have to surround the called code with all kind of error handling code (about 50 lines).
So i thought: because the code (51 lines) is all the same except the one line to get the data, just create one function with all the errorhandling a pass the function to get the data as a parameter to that function.
So i have these functions:
GetAllSources() : List<Source>
GetAllSourcesByTaskId(int taskId) : List<Source>
GetAllSourcesByTaskIdPersonId(int taskId, int personId) : List<Source>
GetAllSourcesByDate(DateTime startDate, DateTime endDate): List<Source>
and i want be able to pass them as a parameter to a function.
How should i declare the called function?
ps
i've read this one
how to pass any method as a parameter for another function
but it uses an Action object which can't return anything (as far as i understand) and i want to return a List
This should work:
List<Source> WithErrorHandling(Func<List<Source>> func)
{
...
var ret = func();
...
return ret;
}
Usage:
var taskId = 123;
var res = WithErrorHandling(() => { GetAllSourcesByTaskId(taskId); });
You can pass a Func which can take many input parameters are return a value:
Func<T1, T2, TResult>
In your case something like this could work:
public List<Source> GetList(Func<List<Source>> getListMethod) {
return getListMethod();
}
Then call using
GetList(() => GetAllSources());
GetList(() => GetAllSourcesByTaskIdPersonId(taskId, personId));
Could you not just pass the List as an argument into your method, might be a bit tidier?
Well, you didn't say anything about the code inside these functions, but if you use linq inside, than your approach is definitely not the best one.
You should use something like this:
IQueriable<SomeType> GetAllSources()
{
return (from source in sources select ...);
}
IQueriable<SomeType> GetAllSourcesByTaskId(int taskId)
{
return (GetAllSources()).Where(source => source.TaskId == taskId);
}