I have a LINQ query which contains a method GetInstanceForDatabase()
principlesList.Select(p => p.GetInstanceForDatabase()).ToList()
where
List<PrincipleInstance>() principlesList = ...
// (contains list of principle like "Manual Trades", "OPM", "Flora")
GetInstanceForDatabase() is a method which takes all other info about a principle (like manual trades).
My problem is that I want to sort out only principle like only "Manual Trades".
I want to put a where clause. I tried but it is fails.
To get a single item use:
query.First(x => x.property == "Manual Trades");
// or
query.FirstOrDefault(x => x.property == "Manual Trades");
var list = p.GetInstanceForDatabase().where(x => x.propertyName == "Manual Trades").ToList();
I'm sure you're GetInstanceForDatabase needs to return your collection that you then filter for the 'Manual Trades' but I can't really tell how you get your list of PrincipalInstances from the question.
This is the correct syntax of using Where in LINQ
principlesList.Select(p => p.GetInstanceForDatabase()).Where(p => p.SomeProperty == "SomeValue").ToList();
Related
I need to set a variable's value to the value of a property that is nested in several Lists. Each list only has one item except for one list. I'm trying to do this:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.Where(x => x.Name == "Misc Expenses").myListE[0].price;
This produces a compile time error that says myListD does not contain a definition for myListE. What is the correct syntax?
After the .Where clause, you need to to .First() (or .ToList()) in order to apply the Where clause:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.Where(x => x.Name == "Misc Expenses").First().myListE[0].price;
Technically, though, you can replace that .Where with .First directly, too:
var myValue = myListA[0].myListB[0].myListC[0].
myListD.First(x => x.Name == "Misc Expenses").myListE[0].price;
I am new to EF and LINQ.
The following two pieces of code work:
dbContext.Categories.Where(cat => [...big ugly code for filtering...] );
&
dbContext.Products.Where(prod => prod.PROD_UID == 1234)
.SelectMany(prod => prod.Categories.Where(
cat => [...big ugly code for filtering...] );
But I want somehow to create only one, reusable, expression or delegate for my filter. I have the following:
private static Expression<Func<Category, bool>> Filter(filter)
{
return cat => [...big ugly code for filtering...] ;
}
but I cannot use it in SelectMany.
I am aware that:
Where clause of standard query accepts Expression<Func<Category,bool>> and returns IQueryable<Category>
Where clause of SelectMany accepts Func<Category,bool> and returns IEnumerable<Category>.
What is the best way to accomplish this? Are any tricks here?
PS: I want in the end to get all categories of a product.
It looks like you're trying to use SelectMany as a filter. SelectMany is used to flatten a collection of collections (or a collection of a type that contains another collection) into one flat collection.
I think what you want is:
dbContext.Products.Where(prod => prod.PROD_UID == 1234)
.SelectMany(prod => prod.Categories)
.Where(filter);
In which case you can reuse the same expression to filter.
EDIT
Based on your updated question it looks like you are applying Where to an IEnumerable<T> property, so the compiler is binding to IEnumerable.Where which takes a Func instead of an Expression.
You should be able to just call AsQueryable() on your collection property to bind to IQueryable.Where():
dbContext.Products.Where(prod => prod.PROD_UID == 1234)
.SelectMany(prod => prod.Categories
.AsQueryable()
.Where(filter);
The next option would be to compile the expression to turn it into a Func:
dbContext.Products.Where(prod => prod.PROD_UID == 1234)
.SelectMany(prod => prod.Categories
.Where(filter.Compile());
But it wouldn't surprise me if the underlying data provider isn't able to translate that to SQL.
All you need to do is call the Filter function before executing the query and store it in a local variable. When the query provider sees a method it attempts to translate that method into SQL, rather than executing the method and using the result. Whenever it encounters a local variable it doesn't attempt to translate it into SQL but rather evaluates the variable to its value, and then uses that value in the query.
As for the problems that you're having due to the fact that the relationship collection isn't an IQueryable, it's probably best to simply approach the query differently and just pull directly from the categories list instead:
var filter = Filter();
dbContext.Categories.Where(filter)
.Where(cat => cat.Product.PROD_UID == 1234);
After analyzing in more detail the SQL generated by EF, I realized that having the filter inside SelectMany is not more efficient.
So, both suggestions (initial one from DStanley and Servy's) should be ok for my case (many-to-many relation between Categories and Products)
/* 1 */
dbContext.Products.Where(prod => prod.PROD_UID == 1234)
.SelectMany(prod => prod.Categories)
.Where( Filter ); // reuseable expression
this will result into a INNER JOIN
/* 2 */
dbContext.Categories.Where( Filter ) // reuseable expression
.Where(c => c.Products.Where(prod.PROD_UID == 1234).Any());
this will result into a EXISTS (sub-select)
The execution plan seems to be absolutely identical for both in my case; so, I will choose for now #2, and will keep an eye on performance.
I have an entity framework object called batch, this object has a 1 to many relationship to items.
so 1 batch has many items. and each item has many issues.
I want to filter the for batch items that have a certain issue code (x.code == issueNo).
I have written the following but Im getting this error:
items = batch.Select(b => b.Items
.Where(i => i.ItemOrganisations
.Select(o => o
.Issues.Select(x => x.Code == issueNo))));
Error 1:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<bool>>' to 'bool'
Error 2:
Cannot convert lambda expression to delegate type 'System.Func<Ebiquity.Reputation.Neptune.Model.Item,bool>' because some of the return types in the block are not implicitly convertible to the delegate return type
Select extension method needs a lambda expression that returns a boolean, but the inner o.Issues.Select returns an IEnumerable of boolean to the outer Select(o => o which result in the exception you're getting.
Try using Any instead which verifies that at least one element verifies the condition:
items = batch.Select(
b => b.Items.Where(
i => i.ItemOrganisations.Any(
o => o.Issues.Any(x => x.Code == issueNo)
)
)
);
If I understand correctly, you're trying to select through multiple layers of enumerables. In those cases you need SelectMany which flattens out the layers, not Select. LINQ's syntax sugar is made specifically to make SelectMany easier to reason about:
var items = from item in batch.Items
from org in item.ItemOrganizations
from issue in org.Issues
where issue.Code == issueNo
select item;
The compiler translates that into something like this:
var items = batch.Items
.SelectMany(item => item.ItemOrganizations, (item, org) => new {item, org})
.SelectMany(#t => #t.org.Issues, (#t, issue) => new {#t, issue})
.Where(#t => #t.issue.Code == issueNo)
.Select(#t => #t.#t.item);
You can always wrap this in a Distinct if you need to avoid duplicate items:
var items = (from item in batch.Items
from org in item.ItemOrganizations
from issue in org.Issues
where issue.Code == issueNo
select item).Distinct();
It's hard to tell what you're trying to do based on your code but I think you're looking for something like this;
var issue = batch.Select(b => b.Items).Select(i => i.Issues).Where(x => x.Code == issueNo).Select(x => x).FirstOrDefault();
The above query will return the first issue where the Issues Code property is equal to issueNo. If no such issue exists it will return null.
One problem (the cause of your first error) in your query is that you're using select like it's a where clause at the end of your query. Select is used to project an argument, when you do Select(x => x.Code == issueNo) what you're doing is projecting x.Code to a bool, the value returned by that select is the result of x.Code == issueNo, it seems like you want that condition in a where clause and then you want to return the issue which satisfies it which is what my query is doing.
items = from b in batch.Include("Items")
where b.Items.Any(x=>x.Code==issueNo)
select b;
You're getting lost in lambdas. Your LINQ chains are all embedded in each other, making it harder to reason about. I'd recommend some helper functions here:
static bool HasIssueWithCode(this ItemOrganization org, int issueNo)
{
return org.Issues.Any(issue => issue.Code == issueNo);
}
static bool HasIssueWithCode(this Item items, int issueNo)
{
return items.ItemOrganizations.Any(org => org.HasIssueWithCode(issueNo));
}
Then your answer is simply and obviously
var items = batch.Items.Where(item => item.HasIssueWithCode(issueNo));
If you inline these functions, the result is the exact same as manji's (so give manji credit for the correct answer), but I think it's a bit easier to read.
I want to select a distinct list.
The following code is not working:
public IQueryable<BusinessObjects.Order> GetByBusinessId(Guid Id)
{
rentalEntities db = DataContextFactory.CreateContext();
List<Rental.BusinessObjects.Order> transformedList = new List<BusinessObjects.Order>();
foreach (Rental.DataObjects.EntityModel.Order item in db.Orders.Where(x => x.BusinessID == BusinessId).ToList())
{
transformedList.Add(OrderMappers.ToBusinessObject(item));
}
return( transformedList.AsQueryable()).Distinct();
}
Try this:
return Rental.DataObjects.EntityModel.Order item in db.Orders
.Where(x => x.BusinessID == BusinessId)
.Distinct()
.Select(item => OrderMappers.ToBusinessObject(item));
This should move the distinct operation to the underlying database call as it's applied before the query is materialized - this is more efficient as the duplicate rows aren't retrieved from the database server. If for some reason you don't want to do that, then check your equals implementation (as mentioned by Sorin)
You may want to check how your business objects implement Equals(), my guess is they are are different even if they have (let's say) the same ID.
You might like to try the DistinctBy() extension method from the MoreLinq library. This lets you easily control the exact semantics of how two objects are compared for distinctness. For instance:
return transformedList.AsQueryable().DistinctBy(orderBO => orderBO.OrderId);
http://morelinq.googlecode.com/files/morelinq-1.0-beta.zip
So I have a Blog object which has a list of tag objects (List<Tag>).
I'm trying to create a method that takes a list of tags and returns a list of blogs that contain all the tags in the passed in list.
I was able to make a method that will return a list of blogs if it matches one tag, but not a list of tags.
to do that I have this
entities.Blogs.Where(b => b.Tags.Any(t => t.Name == tagName))
But I can't figure out how to do something like this
entities.Blogs.Where(b => b.Tags.Any(t => t.Name == tags[0] AND t.Name == tags[1] AND t.Name == tags[2] etc.......))
Is there any way to do this?
Thank you!
I'm using LINQ to Entities
Logically, I think you want something like:
entities.Blogs.Where(b => tags.All(t => b.Tags.Any(bt => bt.Name == t)))
Alternatively:
HashSet<string> tagNames = new HashSet<string>(tags);
return entities.Blogs
.Where(b => tagNames.IsSubsetOf(b.Tags.Select(x => x.Name)));
If this is using LINQ to Entities, I doubt that this will work - but it should work if you're just using LINQ to Objects. Even then, it's not going to be terribly efficient. I suspect there's a more efficient way of doing things, but I can't immediately think of it... it feels like you want a join, but then it gets tricky again.
You can do something like this:
List<Tag> tags = GetTags...;
IQueryable<Blog> blogs = entities.Blogs; // start with all
foreach(var tag in tags){
var thisTag = tag; //this is needed to prevent a bug
blogs = blogs.Where(entry=>entry.Tags.Any(entryTag=>entryTag.TagId==thisTag.TagId));
}
return blogs.OrderBy....;
This will chain together the Where clauses to require that all the Tags be present for a blog entry to be returned.