This code correctly returns one row:
_loadedAssemblies.ForEach(x =>
{
foundTypes.AddRange(from t in x.GetTypes()
where t.GetInterfaces().Contains(typeof(TInterface))
&& t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`')
select t);
}
However, when I use PredicateBuilder, I get zero rows:
var compiledPredicate = CompiledPredicate<TInterface>();
_loadedAssemblies.ForEach(x =>
{
foundTypes.AddRange(from t in x.GetTypes()
where compiledPredicate.Invoke(typeof(TInterface))
select t);
}
private static Func<Type, bool> CompiledPredicate<T>() where T : class
{
// True means all records will be returned if no other predicates are applied.
var predicate = PredicateBuilder.True<Type>();
// Get types that implement the interface (T).
predicate = predicate.And(t => t.GetInterfaces().Contains(typeof(T)));
// If the config file includes filtering by base class, then filter by it.
if (!string.IsNullOrWhiteSpace(_baseClass))
{
Type baseClass = Type.GetType(_baseClass);
predicate = predicate.And(t => t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`'));
}
return predicate.Compile();
}
Someone suggested creating a copy of my loop variable, but I tried that and I still get zero rows. I'm not sure why using PredicateBuilder returns no rows. Any idea what I'm missing?
The change that you mentioned in comments (foundTypes.AddRange(x.GetTypes().AsQueryable().Where(compiledPredicate));) had nothing at all to do with the fact that you were using AsQueryable. In the first case you're passing in a hard coded type to each call of your predicate, in the second you're passing the given item from the sequence. Had you removed the AsQueryable and used Enumerable.Where it would also work, or had you passed the current item, rather than a hard coded type, when invoking it that would also work.
So you can simply do:
foundTypes.AddRange(x.GetTypes().Where(compiledPredicate));
Also, when creating the predicate, there's no need to do as much work as you're doing. Expressions take a fair amount of extra work to deal with. With linq to objects you only need to deal with delegates, which are much less finicky.
private static Func<Type, bool> CompiledPredicate<T>() where T : class
{
Func<Type, bool> predicate = t => t.GetInterfaces().Contains(typeof(T));
if (!string.IsNullOrWhiteSpace(_baseClass))
{
Type baseClass = Type.GetType(_baseClass);
return t => predicate(t) &&
t.BaseType.Name.LeftOf('`') == baseClass.Name.LeftOf('`');
}
return predicate;
}
Related
I have a list of objects.
I have a single item with same type as the list.
I would like to create a generic extension method (called Find) to find an item from the list based on the single obj as well as an arbitrary list of its strongly typed properties.
Here is how I would like to call the method:
var obj = new SomeObject() { ... } ;
var list = new List<SomeObject>() { ... };
// Find similar objects
list.Find(obj, x => x.Id,y => y.Description);
Is this arrangement possible?
FirstOrDefault would work as in the comment below. However, I am looking for a way to use the pattern in different scenarios that might not be a simple lookup.
You can do this
public static T Find<T>(this IEnumerable<T> source, params Func<T,bool>[] condition)
{
return source.FirstOrDefault(o => condition.All(f => f(o))); // use All for && or use Any for ||
}
And to use it
var item = list.Find(x=> x.Id == obj.Id,x=> x.Description == obj.Description);
You can use Object if you want to make this work more generally by casting properties to object. but this will be a bit slower by the way and you must note that the Equals method for custom types must be overriden in order to make it work.
public static T Find<T>(this IEnumerable<T> source, T obj, params Func<T, object>[] condition)
{
return source.FirstOrDefault(o => condition.All(f => f(o).Equals(f(obj))));
}
Then you can call it exactly like this.
var item = list.Find(obj, x => x.Id,y => y.Description);
You can use Where if you want to return all similar items. by just changing FirstOrDefault into Where and the return type of method into IEnumerable<T>
It doesn't seem to me like you're saving a lot of time/effort versus just using a normal Where clause that filters on whichever properties you're after.
var findResults = list.Where(x => x.Id == obj.Id || x.Description == obj.Description);
Maybe you should consider proceeding like this:
var myResult = from currentObject in mylist
where currentObject == mySingleObject // change it by your matching criterias linked by && or || (as in if ...)
// NB: you can also override the == operator in your object definition
// but as mentionned below, it won't work for generic types
select currentObject;
I'm trying to separate out as much as possible an android from the business logic in order to speed things up.
Dotted around the code I have a piece of LINQ that looks like this
var jobItemDoneTest = JobItemsData.GetJobIfDone(wheelpos, theOrder.GetOrderItemStockItemID);
var jobsDoneList = new JobItemsData(theOrder.OrderData.jobItemsID).JobItemDone;
var stillToDo = theOrder
.OrderItemsData
.Where(p => jobsDoneList.All(p2 => p2.orderItemStockItemID != p.orderItemStockItemID))
.Where(t => !t.description.Contains("2hr"))
.Where(t => !t.description.Contains("Staff"))
.ToList();
In other words, there is a comparison between two lists of to filter out some results.
What I'm trying to do is remove the instances of this to create a generic method in a business object class.
So far, I've got this
public List<T> GetWorkStillToDo<T, U>(List<T> orderItems, List<U> jobItems, params object[] searchList1, string searchList2)
{
var stillToDo = orderItems.Where(p=>jobItems.Add(p2=>p2.orderItemStockItemID != p.orderItemStockItemID);
}
The problem is that if I want to search on different properties for p=> and p2=> and then filter on the where conditions, I'm getting lost and can't think of a way to iterate from n = 1 to n in the searchList object array and include them in a LINQ
Is
p2=>searchList[0].ToString() != searchList2
permitted within LINQ and how can I create the Where part of the query?
Where clauses in LINQ are a Func<object, bool> object where object can be a generic if you have well-defined rules around them. The construction syntax is just a normal lambda. So:
Func<object, bool> where = obj => obj.Propery == delta;
return someEnumOrList.Where(where);
Wrapping this into a parameter lets you ad-hoc query against your items. You may need to provide an extension method, something like a WhereAll that takes an array of Func<> objects and checks them all. One idea for that would be:
public static IEnumerable<T> WhereAll(this IEnumerable<T> enumerable, Func<object, bool>[] clauses)
{
var result = enumerable;
foreach(var c in clauses)
{
result = result.Where(c);
}
return result;
}
I have this method in a class called Invoice:
public static Expression<Func<Invoice, bool>> IsAllocated()
{
return i => i.TotalAmountDue == i.GetAllocationsTotal();
}
I have a list like this:
IQueryable<Invoice> invoices
And I need to filter it like that (it's Linq to Entity):
var filteredInvoices = invoices.Where(i => Invoice.IsAllocated());
In this line I'm getting two errors:
Cannot resolve method ... candidates are .... one in Enumerable and the other on in Queryable.
And also:
Cannot convert expression type Expression<Func<Invoice,bool>> to
return type 'bool'
I've tried a lot of things I've found in SO with no luck. Can someone say me what is missing here or at least, which one of the two errors is at the root of the problem?
Your method returns an appropriate expression tree already - you just need to call it, not call it in a lambda expression:
var filteredInvoices = invoices.Where(Invoice.IsAllocated());
Expression are representation and not delegate by themselves. You should create a delegate out of it first
static Expression<Func<Invoice, bool>> IsAllocatedExpr()
{
return i => i.TotalAmountDue == i.GetAllocationsTotal();
}
public static Func<Invoice, bool> IsAllocated = IsAllocatedExpr().Compile();
and then
var filteredInvoices = invoices.Where(i => Invoice.IsAllocated(i));
In our application we want to have standard methods for various conditions in our database. For instance, we have different types of transactions, and we want to create standard methods for retrieving them within other queries. However, this gives us the error:
Method '' has no supported translation to SQL
The method might look like this:
public static bool IsDividend(this TransactionLog tl)
{
return tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
}
To be used as such:
var dividends = ctx.TransactionLogs.Where(x => x.IsDividend());
Of course, if I copy the logic from IsDividend() into the Where clause, this works fine, but I end up duplicating this logic many places and is hard to track down if that logic changes.
I think if I would convert this to an expression like this it would work, but this is not as preferable a setup as being able to use methods:
public Expression<Func<TransactionLog, bool>> IsDividend = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
var dividends = ctx.TransactionLogs.Where(IsDividend);
Is there a way to force Linq to evaluate the method as an expression? Or to "transform" the method call into an expression within a linq query? Something like this:
var dividends = ctx.TransactionLogs.Where(tl => ToExpression(tl.IsDividend));
We are using Linq-to-SQL in our application.
Well having static property containing the expressions seems fine to me.
The only way to make it work with Methods would be to create a method which returns this expression, and then call it inside where:
public class TransactionLog
{
Expression<Func<TransactionLog, bool>> IsDividend() {
Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
return expression;
}
}
public class TransactionLogExtension
{
Expression<Func<TransactionLog, bool>> IsDividend(this TransactionLog log) {
Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
return expression;
}
}
and use it via
var dividends = ctx.TransactionLogs.Where(TransactionLog.IsDividend());
or as extension method
var dividends = ctx.TransactionLogs.Where(x.IsDividend());
But none of it is will work with var dividends = ctx.TransactionLogs.Where(x => x.IsDividend()); because x => x.IsDividend(); itself is an expression tree and your database provider can't translate "IsDividend" into an SQL statement.
But the other two options will at least allow you to pass in parameters (which doesn't work if you store the Expressions as instance or static properties).
I think that LINQ to SQL doesn't fully supports even common and build-in functions. (At least EF does not do it). And moreover - when it deals with user defined methods. I predict that your variant with expression will fall as well as the variant with method call unless you call it after enumeration (ToList or similar method). I suggest to keep the balance between 1) stored procedures at server, 2) common conditions hardcoded in Where clauses in C#, 3) expression trees generation in C#. All these points are relatively complex, for sure (and to my mind there is no easy and general solution).
Try using Extension Methods, like so:
public static class TransactionLogExtensions {
public static IQueryable<TransactionLog> OnlyDividends(this IQueryable<TransactionLog> tl)
{
return tl.Where(t=>t.SourceTypeID == (int)JobType.Dividend || t.SourceTypeID == (int)JobType.DividendAcct);
}
}
Call it like so:
var dividends=db.TransactionLogs.OnlyDividends();
or
var dividends=db.TransactionLogs.OnlyDividends().OrderBy(...);
For this scenario you can use Func. Linq works very good with those.
Here is the simple example of using Func in Linq query. Feel free to modify and use.
Func<int,bool> isDivident = x => 3==x;
int[] values = { 3, 7, 10 };
var result = values.Select (isDivident );
So, I am using the Linq entity framework. I have 2 entities: Content and Tag. They are in a many-to-many relationship with one another. Content can have many Tags and Tag can have many Contents. So I am trying to write a query to select all contents where any tags names are equal to blah
The entities both have a collection of the other entity as a property(but no IDs). This is where I am struggling. I do have a custom expression for Contains (so, whoever may help me, you can assume that I can do a "contains" for a collection). I got this expression from: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1
Edit 1
I ended up finding my own answer.
After reading about the PredicateBuilder, reading all of the wonderful posts that people sent to me, posting on other sites, and then reading more on Combining Predicates and Canonical Function Mapping.. oh and I picked up a bit from Calling functions in LINQ queries (some of these classes were taken from these pages).
I FINALLY have a solution!!! Though there is a piece that is a bit hacked...
Let's get the hacked piece over with :(
I had to use reflector and copy the ExpressionVisitor class that is marked as internal. I then had to make some minor changes to it, to get it to work. I had to create two exceptions (because it was newing internal exceptions. I also had to change the ReadOnlyCollection() method's return from:
return sequence.ToReadOnlyCollection<Expression>();
To:
return sequence.AsReadOnly();
I would post the class, but it is quite large and I don't want to clutter this post any more than it's already going to be. I hope that in the future that class can be removed from my library and that Microsoft will make it public. Moving on...
I added a ParameterRebinder class:
public class ParameterRebinder : ExpressionVisitor {
private readonly Dictionary<ParameterExpression, ParameterExpression> map;
public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) {
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) {
return new ParameterRebinder(map).Visit(exp);
}
internal override Expression VisitParameter(ParameterExpression p) {
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement)) {
p = replacement;
}
return base.VisitParameter(p);
}
}
Then I added a ExpressionExtensions class:
public static class ExpressionExtensions {
public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) {
// build parameter map (from parameters of second to parameters of first)
var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with parameters from the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
return first.Compose(second, Expression.And);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
return first.Compose(second, Expression.Or);
}
}
And the last class I added was PredicateBuilder:
public static class PredicateBuilder {
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
}
This is my result... I was able to execute this code and get back the resulting "content" entities that have matching "tag" entities from the tags that I was searching for!
public static IList<Content> GetAllContentByTags(IList<Tag> tags) {
IQueryable<Content> contentQuery = ...
Expression<Func<Content, bool>> predicate = PredicateBuilder.False<Content>();
foreach (Tag individualTag in tags) {
Tag tagParameter = individualTag;
predicate = predicate.Or(p => p.Tags.Any(tag => tag.Name.Equals(tagParameter.Name)));
}
IQueryable<Content> resultExpressions = contentQuery.Where(predicate);
return resultExpressions.ToList();
}
Please let me know if anyone needs help with this same thing, if you would like me to send you files for this, or just need more info.
Summing it up...
contentQuery.Where(
content => content.Tags.Any(tag => tags.Any(t => t.Name == tag.Name))
);
So is that what you're expecting?
I'm a little confused.
This is what the question itself asks for:
contentQuery.Where(
content => content.Tags.Any(tag => tag.Name == "blah")
);
I'm not sure what the thought process was to get to the questioner's code, really, and I'm not entirely sure exactly what its really doing. The one thing I'm really sure of is that .AsQueryable() call is completely unnecessary -- either .Tags is already an IQueryable, or the .AsQueryable() is just going to fake it for you -- adding extra calls in where there doesn't need to be any.
The error is related to the 'tags' variable. LINQ to Entities does not support a parameter that is a collection of values. Simply calling tags.AsQueryable() -- as suggested in an ealier answer -- will not work either because the default in-memory LINQ query provider is not compatible with LINQ to Entities (or other relational providers).
As a workaround, you can manually build up the filter using the expression API (see this forum post) and apply it as follows:
var filter = BuildContainsExpression<Element, string>(e => e.Name, tags.Select(t => t.Name));
var query = source.Where(e => e.NestedValues.Any(filter));
tags.Select(testTag => testTag.Name)
Where does the tags variable gets initialized from? What is it?
NOTE: please edit the question itself, rather than replying with an answer -- this is not a discussion thread, and they can re-order themselves at any time
If you're searching for all Contents that are marked with any one of a set of tags:
IEnumerable<Tag> otherTags;
...
var query = from content in contentQuery
where content.Tags.Intersection(otherTags).Any()
select content;
It looks like you might be using LINQ To SQL, in which case it might be better if you write a stored procedure to do this one: using LINQ to do this will probably not run on SQL Server -- it's very likely it will try to pull down everything from contentQuery and fetch all the .Tags collections. I'd have to actually set up a server to check that, though.