Entity Framework include with a condition - c#

I have two entities, which are related, ActiveContract and BudgetSource. I'm trying to grab all the BudgetSource which are marked as isActive = true, along with all associated ActiveContracts which are also marked isActive = true. I've tired:
var d = budgetSourceRep.All.Where(x => x.isAcitve)
.OrderBy(x => x.SourceName)
.Include(z => z.ActiveContracts.Where(q => q.isActive))
.Select(y => new EditSelectItemViewModel
{
Id = y.Id,
SourceName = y.SourceName,
DisplayOnNew = y.DisplayOnNew,
NumberOfTimesUsed = y.ActiveContracts.Count()
}).ToList();
but that gives me an error
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties. Parameter name: path
I then changed it to put the filter in the projection:
var d = budgetSourceRep.All.Where(x => x.isAcitve)
.OrderBy(x => x.SourceName)
.Include(z => z.ActiveContracts)
.Select(y => new EditSelectItemViewModel
{
Id = y.Id,
SourceName = y.SourceName,
DisplayOnNew = y.DisplayOnNew,
NumberOfTimesUsed = y.ActiveContracts.Count(a => a.isActive)
}).ToList();
that works, but I'm assuming it's going to make a second query to do that? If so, is there a way to do it in one shot.

that works, but i'm assuming it's going to make a second query to do that?
No, it is not. You can see for yourself by looking at the generated SQL for this query.
It is within the bounds of theoretical possibility for the query provider to have successfully translated your first query into SQL, but doing so is...hard. This simply isn't a feature that the developers of EF chose to put into their query provider. Instead you are forced to project out a collection of related entities in some way to filter them, you cannot use Include to do it for you.

Related

Linq one to many with filter

I have an Entity Framework database that I'm querying, so I'm using linq-to-entities.
Here's my query:
// 'Find' is just a wrapper method that returns IQueryable
var q = r.Find(topic =>
topic.PageId != null &&
!topic.Page.IsDeleted &&
topic.Page.IsActive)
// These are standard EF extension methods, which are used to include
linked tables. Note: Page_Topic has a one-to-many relationship with topic.
.Include(topic => topic.Page.Route)
.Include(topic => topic.Page_Topic.Select(pt => pt.Page.Route))
// HERE'S THE QUESTION: This select statement needs to flatten Page_Topic (which it does). But it seems to do it in the wrong place. To explain, if I were to include another column that depended on Page_Topic (for example: 'PillarRoutName2', I'd have to apply the same flattening logic to that column too. Surely the filtering of Page_Topic should be done higher up the query in a DRY way.
.Select(x => new
{
TopicName = x.Name,
HubRouteName = x.Page.Route.Name,
PillarRouteName = x.Page_Topic.FirstOrDefault(y => y.IsPrimary).Page.Route.Name
}).ToList();
Surely the filtering of Page_Topic should be done higher up the query in a DRY way.
Correct! And it's easy to do this:
.Select(x => new
{
TopicName = x.Name,
HubRouteName = x.Page.Route.Name,
FirstTopic = x.Page_Topic.FirstOrDefault(y => y.IsPrimary)
})
.Select(x => new
{
TopicName = x.TopicName,
HubRouteName = x.HubRouteName,
PillarRouteName = x.FirstTopic.Page.Route.Name,
PillarRoutName2 = x.FirstTopic. ...
}).ToList();
Depending on where you start to get properties from FirstTopic you can also use x.Page_Topic.FirstOrDefault(y => y.IsPrimary).Page or .Page.Route in the first part.
Note that you don't need the Includes. They will be ignored because the query is a projection (Select(x => new ...).

Linq avoid calling function twice

Using Linq to Objects a query might need to filter based on the result of a function and return the value of that function.
For example
files.Where(x => string.IsNullOrWhiteSpace(x.getProperty(propName)))
.GroupBy(x => x.getProperty(propName));
Does the compiler recognize that the value is going to be required for grouping and keep it?
If it doesn't then there must be a way to select to an anonymous type and query the Where and GroupBy statements against that. Is it possible to do this with an anonymous type?
I am able to declare a class and use that.
class fileSelector
{
internal string prop;
internal myFile file;
}
var groups = files
.Select(x => new fileSelector() { prop = x.getProperty(propName), file = x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop);
But is there a way to do this with an anonymous type?
This is what I tried for an anonymous type
var groups = files.Select(x => new { x.getProperty(propName), x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop);
But this gives the error
Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
Final answer
var groups = files
.Select(x => new { prop = x.getProperty(propName), file = x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop, x => x.file);
Does the compiler recognize that the value is going to be required for grouping and keep it?
No, since getProperty might have an intended side effect.
If it doesn't then there must be a way to select to an anonymous type and query the Where and GroupBy statements against that. Is it possible to do this with an anonymous type?
Yes. Your code should work as-it-is by just replacing new fileSelector() {...} with new {...}. Note, though, that in your code (and in the modified version using the anonymous type), the elements of the grouping are fileSelector and the anonymous type, not myFile. See Scott Chamberlain's solution for how to fix that.
Alternatively, you could use the let clause to store intermediary values:
var groups = from file in files
let prop = file.getProperty(propName)
where !string.IsNullOrWhiteSpace(prop)
group file by prop;
Does the compiler recognize that the value is going to be required for grouping and keep it?
No, it will touch the value twice.
You where actually quite close with your final example, you can do it with a annonamous type, just give names for each of the members of the anonymous type then add a element selector to make the body of the grouping the file property.
var groups = files
.Select(x => new { prop = x.getProperty(propName), file = x })
.Where(x => !string.IsNullOrWhiteSpace(x.prop))
.GroupBy(x => x.prop, x => x.file);

Entity Framework - Select single record ordered by a property

I have a table and one of the properties of the table is TotalDue.I wish to first order it by TotalDue and then select the "top" record which in this case would be the record with the highest value.
homeVM.LastSaleAmount = (from i in salesService.GetSalesOrderHeaders()
.OrderByDescending(a => a.TotalDue).First();
This is what I've tried so far but I think .First() needs a parameter and I think I need a select as well but not really sure.
You can try with Take method, is like top, but in Linq world.
homeVM.LastSaleAmount = salesService.GetSalesOrderHeaders().OrderByDescending(a => a.TotalDue).Take(1);
https://msdn.microsoft.com/en-us/library/vstudio/bb503062%28v=vs.100%29.aspx
You're mixing method syntax and query syntax, and your use of query syntax isn't necessary and making this harder. Just remove it:
homeVM.LastSaleAmount = salesService.GetSalesOrderHeaders()
.OrderByDescending(a => a.TotalDue)
.Select(a => a.TotalDue)
.First();
You are trying to put an entire entity into LastSaleAmount. use .Select(a => a.TotalDue) like:
homeVM.LastSaleAmount = salesService.GetSalesOrderHeaders()
.OrderByDescending(a => a.TotalDue).Select(a => a.TotalDue).First();

Nhibernate Projection Query DTO, use method in stead of property

This works:
projections.Add(Projections.Property(Member<MailOrder>.From(x => x.AssigneeCode).QualifiedPath), Member<MailOrderItem>.From(x => x.AssigneeCode).Path);
projections.Add(Projections.Property(Member<MailOrder>.From(x => x.AssigneeName).QualifiedPath), Member<MailOrderItem>.From(x => x.AssigneeName).Path);
projections.Add(Projections.Property(Member<MailOrder>.From(x => x.AssigneeType).QualifiedPath), Member<MailOrderItem>.From(x => x.AssigneeType).Path);
This does not off course
projections.Add(Projections.Property(Member<IMailOrderAssignee>.From(x => x.AssigneeCode).QualifiedPath), Member<MailOrderItem>.From(x => x.Code).Path);
projections.Add(Projections.Property(Member<IMailOrderAssignee>.From(x => x.AssigneeName).QualifiedPath), Member<MailOrderItem>.From(x => x.GetName()).Path);
projections.Add(Projections.Property(Member<IMailOrderAssignee>.From(x => x.AssigneeType).QualifiedPath), Member<MailOrderItem>.From(x => x.GetType()).Path);
This does not work because of two things:
there is no persister for theinterface
Methods are used in Property way.
I've searched a lot in the world of Nhiberante, but it seems to me that this is quite difficult.
The IMailOrderAssignee is an interface for two rootentity's (let's call them RootX and RootY). In the context of my MailOrders, it isn't important what root it is, as long as I have a reference to it + The Name and it's code and emailaddresses.
The IMailOrderAssignee is mapped with the any - tag in the mapping file. (that works brilliant, but I could do it with an discriminator also).
My question:
Is it possible to use the result
of a method in a projection query so
the result is in the DTO?
Is it possible to uses contracts
in projection querys (I guess
not...)
Why not do the projection in memory?
Example:
var criteria = someCriteriaThatReturnsPersistentEntities;
var items = criteria.List<IMailOrderAssignee>();
var projected = items.Select(i => new
{
Prop1 = i.SomeMethod(),
Etc
});

Entity Framework How to fill NotMapped Property that need a join?

I have a PostInfo table and LikeInfo Table,PostInfo have list of LikeInfo (for post likes).PostInfo table have a NotMapped property named of "LikeCount".
I want select list of PostInfoes and join to LikeInfo table and calcualte count of LikeInfoes of post and fill count of this to LikeCount property.
Here is my sample:
var query = context.PostInfoes.Where(x => x.UserId == userId).Include(x => x.LikeInfoes);
foreach (var item in query)
{
item.LikeCount = item.LikeInfoes.Count;
}
return query.ToList();
That is not good way for me because I do foreach on query and set LikeCount manualy however I dont want/need include full peoperties of LikeInfo table in this case.
I'm finding the best and easy way to fill this property.
Since EF6 does not allow projecting to entity type, you need to use LINQ to Entities query with intermediate anonymous type projection containing all necessary data, then switch to LINQ to Objects (via AsEnumerable()) and do the final projection, using a delegate block to perform the necessary unmapped property fixups:
var result = context.PostInfoes
.Where(x => x.UserId == userId)
.Select(x => new { Info = x, LikeCount = x.LikeInfoes.Count() })
.AsEnumerable()
.Select(x =>
{
x.Info.LikeCount = x.LikeCount;
return x.Info;
})
.ToList();

Categories