So I'm on Linq-To-Entities with an asp.net mvc project.
I always get a little stumped with this sort of query.
My schema is:
ProductTag
+TagName
+<<ProductNames>>//Many-to-many relationship
ProductName
+FullName
+<<Tag>>//Many-to-many relationship
PurchaseRecord
+Amount
+<<ProductName>>//one productname can be linked to Many purchase records.
I need to get the sum of all purchases for a given tag.
This is what I've tried.
ProductTag thetag//could be some tag
decimal total = myentities.PurchaseRecords
.Where(x => thetag.ProductNames.Any
(a => a.FullName == x.ProductName.FullName))
.Sum(s => s.Amount);
I've tried changing a couple of things, tried using Contains, but I know I'm fundamentally wrong somewhere.
I keep getting :
Unable to create a constant value of type 'ProductName'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Update
So with #Alexandre Brisebois's help below it worked simply like this:
var total= item.ProductNames.SelectMany(x => x.PurchaseRecords)
.Sum(s => s.Amount);
When you get this sort of error, you need to do all evaluations outside of the linq query and pass the values in as variables.
the problem with your query is that thetag.ProductNames.Any() is out of context.
This evaluation is not converted to SQL since it is not a string/guid or int.
You will need to query for this object within your query and evaluate from this object.
I'm not sure if that was clear.
You would need to do something like
var query1 = (from x in tags where x.tagID = id select x.ProductNames)
.SelectMany(...)
The select many is because you are selecting a collection ProductNames and need to bring it back as a flat set/collection fo that you can do a .Any() on it in the next query.
Then use this and do an query1.Any(logic)
decimal total = myentities.PurchaseRecords.
Where(x => query1.Any
(a => a.FullName == x.ProductName.FullName))
.Sum(s => s.Amount);
By doing this you will stay in linq to entity and not convert to linq to objects.
The ForEach is not an option since this will iterate over the collection.
you can use AsEnumerable method to perform certain portions of query in C# rather than on sql server. this is usually required when you have part of data in memory (Collection of objects) so using them in query is not easy. you have to perform part of query execution on .net side. for your problem plz try
decimal total = myentities.PurchaseRecords.AsEnumerable()
.Where(x => thetag.ProductNames.Any
(a => a.FullName == x.ProductName.FullName))
.Sum(s => s.Amount);
plz visit this link to find more about AsEnumerable
Related
In linq is there a difference between:
EFDbContext _db = new EFDbContext();
1)_db.UserQuizes
.Where(uq => uq.UserId == currentUserId && uq.QuizId == quizId)
.Include(qz => qz.Quiz.VerbalQuizes.Select(q => q.Question)).First()
2)_db.UserQuizes
.Include(qz => qz.Quiz.VerbalQuizes.Select(q => q.Question))
.Where(uq => uq.UserId == currentUserId && uq.QuizId == quizId).First()
3)_db.UserQuizes
.Include(qz => qz.Quiz.VerbalQuizes.Select(q => q.Question))
First(uq => uq.UserId == currentUserId && uq.QuizId == quizId)
Notice that first query uses include after where and second before where, but result is the same. Also how to see actual sql query? In this particular case perfomance is my main goal, can i improve the query? i need to change two properties : UserQuizes property, and UserQuizes-> VerbalQuizes-> Question property.
Would it be better to split up it two queries or use it like as it is
Ordering of instructions like you've shown often won't make a difference in EF or LINQ to SQL. The query builder turns your entire LINQ statement into an abstract logical representation, and then another pass converts the logical structure into a SQL statement. So the WHERE predicates are all going to end up in the same place. The predicates inside a First() just get pushed over to the WHERE clause. The Include statements also get accumulated and projected to JOINs to include the extra columns needed to produce the included entity.
So the short answer is that EF will usually produce the most logical SQL statement regardless of the order in which you constructed your LINQ statement. If you need to tune it further, you should look at a stored procedure where you can hand-craft the SQL.
I know that lots of question related to this error, but I can not find out a way to convert my query to meet my query. My error: 'LINQ to Entities does not recognize the method 'Boolean TryGetValue(Int32, System.Collections.Generic.List1[System.Nullable1[System.Int32]] ByRef)' method, and this method cannot be translated into a store expression.'
My mind is melting down!
var groupedKeyAndValueOfProjectIdAndZoneIds = groupedProjectDelegationByProjectId.ToDictionary(keySelector: x => x.ProjectId, elementSelector: x => x.ZoneIds);
...
var data = projects
.Select(p => new Project
{
Id = p.Id,
ProjectName = p.Name,
Zones = p.Zones.Where(z =>
(zoneIds.Contains(z.Id) || (groupedKeyAndValueOfProjectIdAndZoneIds.TryGetValue(p.Id, out outValue) ? outValue.Contains(z.Id) : false)))
...
Given that groupedKeyAndValueOfProjectAndZones is Dictionary<int, List<int>>.
Please help me.
The problem you are having is that you are trying to mix two sources of data together. Underneath the hood LINQ to Entities wants to take the expression you are expressing in LINQ and translate it into a SQL query. In other words when you are writing a select in LINQ you are getting 1:1 mapping in SQL. When you throw a dictionary of data the way groupedKeyAndValueOfProjectAndZones is into the mix LINQ to Entities doesn't know how to represent this as its an in memory data source that has no SQL equivalent to run.
To fix this you need to either move the data contained in groupedKeyAndValueOfProjectAndZones into the database and query it from there or you need to provide the filtering you are doing post the LINQ to Entities query
I have a set of related entities. I'm using linq to group a collection of an entity type by a property on a related entity and then doing a sum calculation on a property of another related entity:
Vehicles.GroupBy(v => v.Mechanics.Engine.Size)
.Select(g => g.Sum(s => s.Passengers.Count));
I'm trying to do as much as possible via linq to entities because there is a large number of records in the db. However, the generated sql includes 9 select statements and an outer apply which takes more than 5 times as long to execute as writing the simplified sql code to achieve the same in one select statement.
How do I improve the generated sql?
You're in fact counting the number of passengers per engine size. So, the navigation properties permitting, you could also do:
Passengers.GroupBy(p => p.Vehicle.Mechanics.Engine.Size)
.Select(g => g.Count())
This will probably generate more joins and less subqueries. And only one aggregating statement in stead of two in the original query, of which one (Count) is repeated for each size.
Perhaps try the query like this:
Vehicles
.Select(x => new
{
EngineSize = x.Mechanics.Engine.Size,
PassengersCount = xs.Passengers.Count,
})
.ToArray()
.GroupBy(v => v.EngineSize)
.Select(g => g.Sum(s => s.PassengersCount));
This will execute in a single query, but may pull back too much data to make it faster. It's worth timing and profiling to see which is better.
You could also consider a hybrid approach whereby you bypass LINQ query generation yet use EF to project results into strong types like this:
public List<Vechicles> GetVehcileInformation(string VehicleType){
var QueryString = Resources.Queries.AllVehicles;
var parms = new List<SqlParameters>();
parms.Add(new SqlParameter("VehicleType", VehicleType );
try{
using (var db = new MyEntities()){
var stuff= db.SqlQuery<Vehicles>(QueryString, parms.ToArray());
return stuff.ToList();
}
}catch(exception iox){Log.ErrorMessage(iox);}
}
The idea is that the group by is done at DB layer which gives you more control than in LINQ. You get the speed of direct SQL Queries but get back strongly typed results! The query string itself is stored in a resources file as a string with Parameter place holders like this:
Select * from Table Where FieldName = #VehicleType...
I have this method:
public virtual IEnumerable<Invoice> GetHomePageInvoices(IList<Area> areas, FinancialYearLookup financialYear)
{
var homePageInvoices = _db.Invoices.Where(x => areas.Any(z => z.Id == 3)).ToList();
...
}
Basically I'm trying to find any invoices where the area matches with any of those in the parameter area.
I'm getting the error:
Unable to create a constant value of type 'Models.Area'. Only
primitive types ('such as Int32, String, and Guid') are supported in
this context.
Can anyone explain why this is happening and how to fix?
You cannot use an IList<Area> in the context of your Linq Provider (presumably Linq to Entities) - just extract the id's beforehand and use a Contains query which does work on a collection of primitives:
List<int> ids = areas.Select( x=> x.Id).ToList();
var homePageInvoices = _db.Invoices
.Where(x => ids.Contains(x.Id))
.ToList();
Also I assume you did not want to compare with a fixed value of 3 - so I changed your query accordingly - provided the Invoice entity has an Id property.
a. It looks like you have a typo here:
z => z.Id == 3
but the main problem is
b. I'm guessing you're using Linq to Entities but its not clear. In any case what is happening is that the query builder is trying to turn that 'areas.Any(...)' into SQL and it can't do it because areas is not an IQueryable from your database, but a local variable. I suggest you use something like this WhereIn custom Linq operator as described here or here. That will build a SQL in clause containing all the items in areas that you might want to match against.
I found this link which explains my problem and has an answer, but don't seem to be able to make it work.
Here's what I have for DataLoadOptions:
options.LoadWith<Support>(p => p.PostBase);
options.LoadWith<Support>(p => p.PostMaterial);
options.LoadWith<Support>(p => p.PostPosition);
options.LoadWith<Support>(p => p.PostSize);
options.LoadWith<Support>(p => p.PostType);
options.LoadWith<Support>(p => p.Signs);
options.LoadWith<Support>(p => p.SupportComments);
options.LoadWith<Support>(p => p.SupportInspections);
options.LoadWith<Support>(p => p.SupportPhotos);
options.LoadWith<Sign>(p => p.SignBacking);
options.LoadWith<Sign>(p => p.SignComments);
options.LoadWith<Sign>(p => p.SignCondition);
options.LoadWith<Sign>(p => p.SignDelineator);
options.LoadWith<Sign>(p => p.SignFace);
options.LoadWith<Sign>(p => p.SignIllumination);
options.LoadWith<Sign>(p => p.SignToSignObstructions);
options.LoadWith<Sign>(p => p.UniformTrafficControlCode);
options.LoadWith<SignToSignObstruction>(p => p.SignObstruction);
I think that will give a good explanation of my object graph. I'm trying to query for Support objects that match a certain search criteria (perhaps someone wants supports with post type of blah).
If I try just pulling back all Supports, I get about 2200 Supports and it takes 17k queries.
I attempted the grouping solution mentioned in the other question, but I wonder if either I'm doing it wrong or my situation is just too complex. I removed the search criteria and just tried returning all Supports. This results in about 21k queries and pulls back about 3000 Supports. Here is my query:
var group =
from support in roadDataContext.Supports
join sign in roadDataContext.Signs on support.SupportID equals sign.SupportID
group sign by sign.Support
into signGroup
select signGroup;
Am I just missing something simple? Thanks.
We made the same mistake with our L2S data layer. Our load options are ridiculous in some cases. It was a hard lesson learned.
This is known as the SELECT N+1 problem. 1 for the parent entity, and N for the number of associated entities being eager-loaded. You'd expect L2S to just be smart enough and get it all in one giant query, but this is unfortunately not the case. It will create one giant query, which tells it the IDs of the associations to load, then one by one retrieves those associations.
Perhaps the best work-around is to use projection so your LINQ query returns a new object, rather than an entity. For example:
var fooDtos = from foo in db.Foo
where foo.bar == "What a great example"
select new fooDTO { FooName = foo.Name, FooBar = foo.Bar };
This query returns an IEnumerable<FooDTO> instead of IQueryable<Foo>. This has two benefits. First of all you're instructing L2S specifically which columns to retrieve, so it doesn't do a SELECT *. Also, you don't need DataLoadOptions anymore because you can query any table you want in the query and select from any table to generate the DTO.