I have a number of similar methods that contain linq queries - here's an example. The only difference is the .Where clause fed by the param.
public Supplier FindAny(int ID)
{
return CompareView.Select()
.Where(p => p.Supplier.ID == ID)
.Select(p => p.Supplier)
.FirstOrDefault();
}
I'm looking to see whether the code can be slimmed down through the use of expressions. To date I've found a few examples of expressions in use, but nothing that has helped me convert the code successfully.
Firstly, it would be good to know if expressions can be utilised with this style of code, and if it can, any pointers would be appreciated.
A straight forward translation would be:
public Supplier FindAny(Func<WhateverTypePIs, bool> func)
{
return CompareView.Select()
.Where(func)
.Select(p => p.Supplier)
.FirstOrDefault();
}
..which would allow this:
var supplier = FindAny(p => p.Supplier.ID == ID);
Note: You'll have to fill in the WhateverTypePIs generic type .. as that isn't shown in your original question (it's whatever p's type is in your current expression).
Does this help:
public Supplier FindAny(Func<WhateverTypePIs, bool> func)
{
return CompareView.FirstOrDefault(func).Supplier
}
Related
I'm getting this error when trying to do a linq query:
LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable`1[FEI.Entities.EF6.Tables.HORSE_IDENTITY_GENDER] GetHorseIdentityGenderQuery(FEI.Entities.EF6.Tables.HORSE_DOCUMENT_PART)' method, and this
method cannot be translated into a store expression.
I've read a lots of previous questions where people get the same error, but I understand that it's because LINQ to Entities requires the whole linq query expression to be translated to a server query, and therefore you can't call an outside method in it. I haven't been able to convert my scenario into something that works yet, and my brain is starting to melt down, so I was hoping someone could point me in the right direction. We're using Entity Framework and the specification pattern (and I'm new to both).
Here's the code that uses the specification:
HORSE_DOCUMENT HorseDocForPart = Bll.GetHorseDocumentForPartUpload(horseId, identityType);
Here's the code that provides from method GetHorseDocumentForPartUpload
public HORSE_DOCUMENT GetHorseDocumentForPartUpload(int horseID, HorseDocGender identityType)
{
// Get the unique horse document
var horseDoc = Tables
.HORSE_DOCUMENT
.Where(hd =>
hd.HORSE_UID == horseID &&
hd.HORSE_IDENTITY_TYPE.HORSE_IDENTITY_TYPE_FULL_CODE == identityType.ToString() &&
!hd
.HORSE_DOCUMENT_PART
.Any(hdp =>
hdp.VALIDATION_STATUS != HorseDocPartStatus.REFUSED.ToString() &&
GetHorseIdentityGenderQuery(hdp).Any(hig => hig.IS_FULL)
)
).SingleOrDefault();
return horseDoc;
}
Here's the last code :
public IEnumerable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(HORSE_DOCUMENT_PART horseDocPart)
{
var possibleDocs = Tables
.DOCUMENTs
.Where(doc => doc.DOC_OWNER_UID == horseDocPart.HORSE_DOCUMENT_PART_UID);
return horseDocPart
.HORSE_DOCUMENT
.HORSE_IDENTITY_TYPE
.HORSE_IDENTITY_GENDER
.Join(
possibleDocs,
hig => hig.DOCUMENT_GENDER_CODE.DOCUMENT_GENDER_CODE_UID,
doc => doc.DOCUMENT_GENDER_CODE_UID,
(dgc, doc) => dgc
);
}
You return IEnumerable from the method
public IEnumerable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(...)
This is deferred but using IEnumerable does not allow Linq-To-Sql execution, you should be using IQueryable as such.
public IQueryable<HORSE_IDENTITY_GENDER> GetHorseIdentityGenderQuery(...)
Please see more detailed explanation from Returning IEnumerable<T> vs. IQueryable<T>
IQueryable<Organization> query = context.Organizations;
Func<Reservation, bool> predicate = r => !r.IsDeleted;
query.Select(o => new {
Reservations = o.Reservations.Where(predicate)
}).ToList();
this query throws "Internal .NET Framework Data Provider error 1025" exception but the query below does not.
query.Select(o => new {
Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();
I need to use the first one because I need to check a few if statements for constructing the right predicate. I know that I can not use if statements in this circumstance that is why I pass a delegate as parameter.
How can I make the first query work?
While the other answers are true, note that when trying to use it after a select statement one has to call AsQueryable() explicitly, otherwise the compiler will assume that we are trying to use IEnumerable methods, which expect a Func and not Expression<Func>.
This was probably the issue of the original poster, as otherwise the compiler will complain most of the time that it is looking for Expression<Func> and not Func.
Demo:
The following will fail:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).Any(expr))
.Load()
While the following will work:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
.Load()
After creating the bounty (rats!), I found this answer, which solved my problem. (My problem involved a .Any() call, which is a little more complicated than this question...)
In short, here's your answer:
IQueryable<Organization> query = context.Organizations;
Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;
query.Select(o => new { Reservations = o.Reservations.Where(expr) })
.ToList();
Read the referenced answer for an explanation of why you need the local variable expr, and you can't directly reference another method of return type Expression<Func<Reservation, bool>>.
Thanks for pinging me. I guess I was on the right track after all.
Anyway, to reiterate, LINQ to Entities (thanks to Jon Skeet for correcting me when I got mixed up in my own thought process in the comments) operates on Expression Trees; it allows for a projection to translate the lambda expression to SQL by the QueryProvider.
Regular Func<> works well for LINQ to Objects.
So in this case, when you're using the Entity Framework, any predicate passed to the EF's IQueryable has to be the Expression<Func<>>.
I just experienced this issue in a different scenario.
I have a static class full of Expression predicates which I can then combine or pass to an EF query. One of them was:
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(statuses.Contains);
}
This was throwing the 1025 error due to the Contains method group call. The entity framework expected an Expression and found a method group, which resulted in the error. Converting the code to use a lambda (which can be implicitly cast to an Expression) fixed the error
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(x => statuses.Contains(x));
}
Aside: I then simplified the expression to ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));
Had a similar problem. Library of ViewModels that look like this:
public class TagViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
{
Id = t.Id,
Name = t.Name,
};
This works:
var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
.ToArrayAsync();
But, this won't compile:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
})
.ToArrayAsync();
Because the second .Select is a mess - the first one is actually called off of an ICollection, which is not IQueryable, so it consumes that first Expression as a plain Func, not Expression<Func.... That returns IEnumerable<..., as discussed on this page. So .AsQueryable() to the rescue:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
But that creates a new, weirder problem: Either I get Internal Framework...Error 1025, or I get the post variable with a fully loaded .Post property, but the .Tags property has an EF proxy object that seems to be used for Lazy-Loading.
The solution is to control the return type of Tags, by ending use of the Anonymous class:
public class PostViewModel
{
public Post Post { get; set; }
public IEnumerable<TagViewModel> Tags { get; set; }
Now select into this and it all works:
var post = await db.Posts.Take(10)
.Select(p => new PostViewModel {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
Consider the following code
var q = from e in myCollection.AsQueryable<Entity>() where e.Name == "test" select e;
The actual query is very complex and I don't like building it using QueryBuilder instead of LINQ.
So I want to convert it back to IMongoQuery to use in myCollection.Group() call since there is no GroupBy support through LINQ.
Is it possible?
Edited answer:
I realized that there already is an official way to get the Mongo query from a LINQ query (I should have known!). You have to downcast the IQueryable<T> to a MongoQueryable<T> to get access to the GetMongoQuery method:
var linqQuery = from e in collection.AsQueryable<Entity>() where e.Name == "test" select e;
var mongoQuery = ((MongoQueryable<Entity>)linqQuery).GetMongoQuery();
Original answer:
At the moment there is no officially supported way to do that, but in the near future we do intend to make it easy to find out what MongoDB query the LINQ query was mapped to.
In the short term you could use the following undocumented internal methods to find out what MongoDB query the LINQ query is mapped to:
var linqQuery = from e in collection.AsQueryable<Entity>() where e.Name == "test" select e;
var translatedQuery = (SelectQuery)MongoQueryTranslator.Translate(linqQuery);
var mongoQuery = translatedQuery.BuildQuery();
But at some point you might need to switch from these undocumented methods to officially supported methods (the undocumented methods might change or be renamed in the future).
A quick extension based on Robert Stam's answer:
public static IMongoQuery ToMongoQuery<T>(this IQueryable<T> linqQuery)
{
var mongoQuery = ((MongoQueryable<T>)linqQuery).GetMongoQuery();
return mongoQuery;
}
public static WriteConcernResult Delete<T>(this MongoCollection<T> col, IQueryable<T> linqQuery)
{
return col.Remove(linqQuery.ToMongoQuery());
}
public static WriteConcernResult Delete<T>(this MongoCollection<T> col, Expression<System.Func<T, bool>> predicate)
{
return col.Remove(col.AsQueryable<T>().Where(predicate).ToMongoQuery());
}
example:
myCollection.Remove(myCollection.AsQueryable().Where(x => x.Id == id).ToMongoQuery());
myCollection.Delete(myCollection.AsQueryable().Where(x => x.Id == id));
myCollection.Delete(x => x.Id == id);
Whenever the tags argument is not empty I get a NotSupportedException:
Local sequence cannot be used in LINQ to SQL implementation of query operators except the
Contains() operator.
[WebMethod]
public static object GetAnswersForSurvey(string surveyName, int? surveyYear, IEnumerable<string> tags, IEnumerable<string> benchmarks)
{
IQueryable<DAL.Answer> results = new DataClassesDataContext().Answers
.OrderBy(a => a.Question.Variable);
if (!String.IsNullOrEmpty(surveyName)) results = results.Where(a => a.Survey.Name == surveyName);
if (surveyYear.HasValue) results = results.Where(a => a.Survey.Year == surveyYear.Value);
if (tags.Any()) results = results.Where(answer => answer.Question.Tags.Select(t => t.Label).Intersect(tags).Any());
if (benchmarks.Any()) results = results.Where(answer => benchmarks.Contains(answer.Question.BenchmarkCode));
return results.Select(a => new {
a.Question.Wording,
a.Demographic,
Benchmark = a.Question.BenchmarkCode,
a.Question.Scale,
a.Mean,
a.MEPMean,
a.NSSEMean
});
}
I understand it may not be possible to do it the way I'm trying. If it is impossible, can anyone offer any alternatives?
A number of the general purpose Linq to Object methods are not support within Linq to SQL.
http://msdn.microsoft.com/en-us/library/bb399342.aspx
An alternative might be to complete several sub-queries against sql and then perform your intersection operations as Linq to Objects
I think the reason is that the implementation of System.Data.Linq.Table.Intersect returns an IEnumerable, which in turn does not implement a parameterless version of Any().
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.