I have 2 queries. First gets the full name of a committee head if the committee has more that 1 member.
var result = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Where(f => f.ExpertCommitteeMembers.Count > 1)
.Select(f => f.ExpertCommitteeMembers
.Where(m => m.IsCommitteeHead)
.FirstOrDefault().Expert.FullName)
.FirstOrDefaultAsync();
The second one gets the full name of the only committee member if the committee has only 1 member
var result2 = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Where(f => f.ExpertCommitteeMembers.Count == 1)
.Select(f => f.ExpertCommitteeMembers
.FirstOrDefault().Expert.FullName)
.FirstOrDefaultAsync();
Is it possible to check how many members does the committee have and then return the correct name all in the same query? Or do I first have to check how many members does the committee have and then run the appropriate query seperatly?
If I understand correctly you can try to let condition into inner linqwhere
var result = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Select(f => f.ExpertCommitteeMembers.Where(m =>
(m.IsCommitteeHead &&
f.ExpertCommitteeMembers.Count > 1)||f.ExpertCommitteeMembers.Count == 1).FirstOrDefault().Expert.FullName)
.FirstOrDefaultAsync();
you can Create a create anonymous object in linq query select. which will contain FullName and count.
var result = await db.ExpertCommittees
.Where(f => f.Id == committeeId)
.Where(f => f.ExpertCommitteeMembers.Count > 1)
.Select(f => new
{
FullName = f.ExpertCommitteeMembers
.Where(m => m.IsCommitteeHead)
.FirstOrDefault().Expert.FullName,
Count = f.ExpertCommitteeMembers.Count
})
.FirstOrDefaultAsync();
A generated Join, as it is generated from ExpertCommittees to
ExpertCommitteeMembers, with a Navigation-Collection, will always do a Left Join,
what you want is an Inner Join. It will give you only items with entities in both tables.
This will be something like
db.ExpertCommittees.Join(db.ExpertCommittemember, x=>someid, y=>somid,
(comittee, member) => new { comittee, member});
But this will give you one line per member.... with the possibility to filter by "IsComitteeHead" or whatever.
You can append Grouping, or use a GroupJoin directly, to have a List of "Commitees", each with a List of it's members ...and only if there are members.
Without knowing the data structure, ID's, Foreign-Keys and table name, we cannot produce a valid query.
Related
I know that in Linq I have to do the OrderBy after doing a Select - Distinct, but I'm trying to order by an Included entity property that get lost after the Select.
For example:
var accounts = _context.AccountUser
.Include(o => o.Account)
.Where(o => o.UserId == userId || o.Account.OwnerId == userId)
.OrderByDescending(o => o.LastAccessed)
.Select(o => o.Account)
.Distinct();
As I'm doing the Where by an or of two different parameters, there is a good chance to obtain duplicated results. That's why I'm using the Distinct.
The problem here is that after I do the Select, I don't have the LastAccessed property anymore because it doesn't belong to the selected entity.
I thing the structure of the AccountUser and Account can be inferred from the query itself.
If you have the bi-directional navigation properties set up:
var accountsQuery = _context.AccountUser
.Where(o => o.UserId == userId || o.Account.OwnerId == userId)
.Select(o => o.Account)
.Distinct()
.OrderByDescending(a => a.AccountUser.LastAccessed);
When Selecting the Account you do not need .Include() Keep in mind that any related entities that you access off the Account will be lazy-loaded. I recommend using a .Select() to extract either a flattened view model or a view model hierarchy so that the SQL loads all needed fields rather than either eager-loading everything or tripping lazy-load calls.
Since LINQ doesn't implement DistinctBy and LINQ to SQL doesn't implement Distinct that takes an IEqualityComparer, you must substiture GroupBy+Select instead:
var accounts = _context.AccountUser
.Include(o => o.Account)
.Where(o => o.UserId == userId || o.Account.OwnerId == userId)
.GroupBy(o => o.Account).Select(og => og.First())
.OrderByDescending(o => o.LastAccessed)
.Select(o => o.Account);
I had written a Query in NHibernate as below:
var queryResult = CurrentSession.QueryOver()
.Where(r => r.StatusId == 1)
.JoinQueryOver(a => a.ActorList)
.Where(s=>s.IsActor==1)
.List()
.Distinct()
.ToList();
I am trying to retrieve only Where(s=>s.IsActor==1), But It Is Getting Records
Where(s=>s.IsActor==0) also...
How can I get only IsActor==1 records?
Thanks in Advance
You need to specify a predicate in the join, so that it is applied to the join not the top where:
(will look something like ...LEFT JOIN actor on actor.Id = p.ActorId AND IsActor = 1)
Actor actorAlias = null;
var queryResult = CurrentSession.QueryOver()
.Where(r => r.StatusId == 1)
.Left.JoinQueryOver(r => r.ActorList, () => actorAlias, a => a.IsActor==1)
.List()
.Distinct()
.ToList();
I am trying to query a collection and child collection using EF 7. Here's the code:
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID )
.Include(g => g.Address.Where(p => p.AddressTypeID == 1))
.ThenInclude(p=> p.City)
.ToListAsync();
> Error CS1061 'IEnumerable<Address>' does not contain a definition for
> 'City' and no extension method 'City' accepting a first argument of
> type 'IEnumerable<Address>' could be found (are you missing a using
> directive or an assembly reference?) Contacts.DNX 4.5.1, Contacts.DNX
> Core 5.0
It works fine when I just use
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID )
.Include(g => g.Address)
.ThenInclude(p=> p.City)
.ToListAsync();
But this will load all the addresses for the customer where I only want the recent address for which the AddressTypeID is 1.
Any idea how to do this?
You can try anonymous projection, that will fetch translate your query into SQL.
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID)
.Select(cntct=> new
{
contact = cntct,
address = cntct.Address.Where(p => p.AddressTypeID == 1),
city = cntct.Address.Where(p => p.AddressTypeID == 1)
.Select(h=>h.City),
}.ToList();
You can't filter in Include. In any version of entity framework.
If you need to load a subset of the collection then you need to Join instead of using navigation property and filter whenever you need using Where clause
Like this (simplified, extra steps for readability):
var filteredAddresses = Addresses.Where(x=>x.AddressTypeId==1);
var customersWithAddress = Customers.Join(filteredAddresses, x=>x.Id,x=>x.CustomerId,(c,a)=> new {
Customer=c,
Address=a
});
Or if you need a single customer, assuming you have Customer navigation property in Address:
var addressWithCustomer = Addresses
.Where(x=>x.AddressTypeId==1 && x.CustomerId == customerId)
.Include(x=>x.Customer)
.Include(x=>x.City)
.Single();
A lot of times, it is better to approach queries which involve conditional nested entities, to start with the nested entity, apply the conditions to this nested fellow and then project out the parent entity, since it is always easier to reach to the parent entities from the nested enumerable ones. (many to one)
in our case, we can apply the filter out on the Address entity and then group it on the Contact entity.
var customerID = 86795;
var query = await _context.Addresses
.Where(a => a.Contact.CustomerID == customerID
&& a.Contact.RegistrationDate.Year == 2016
&& a.AddressTypeID == 1)
.Include(a => a.Contact)
.Include(a => a.City)
.GroupBy(a => a.Contact)
.Take(20) // by the way, you should apply some orderby for a predicatble Take
.ToListAsync();
and if you absolutely want a list of Contacts as the output of the above query, you can do this.
var contacts = query.Select(g =>
{
g.Key.Addresses = g.ToList();
return g.Key;
}).ToList();
// now you can work off the Contacts list, which has only specific addresses
This will basically give you a grouped list of all Contacts with CustomerID, and with those address types and registration years only. The important thing here is to iterate through the group to get the addresses, and not use the grouping.Key.Addresses navigation. (grouping.Key will be the Contact entity)
Also, I don't know if CustomerID is a primary key on the Contact entity, but if it is, it looks like you would just need a list of matching addresses for one Contact. In that case, the query would be:
var query = await _context.Addresses
.Where(a => a.Contact.CustomerID == customerID && a.AddressTypeID == 1)
.Include(a => a.Contact)
.Include(a => a.City)
.ToListAsync();
Include The Collection for Eager Load then use Any instead of Where ... to Select specific items in the child of the wanted entity.
var customerID = 86795;
var query = await _context.Contacts
.Where(g => g.CustomerID == customerID )
.Include(g => g.Address.Any(p => p.AddressTypeID == 1))
.ThenInclude(p=> p.City)
.ToListAsync();
I've Resource and ResourceDetail.
MemberPoint with memberId and ResourceId.
I would like to get Resources Details for a member.
In SQL,
Select d.* From ResourceDetails d Inner Join
Resource on r d.ResourceId = r.Id Inner Join
MemberPoint mp on r.id = mp.ResourceId
where mp.memberId = 1
In EF,
var query = _context.ResourceDetails
.Include(d => d.Resource)
.Include(r => r.Resource.Memberpoints)
.Where(e => e.Resource.Memberpoints.Where(m => m.MemberId))
I got error when I write above EF query.
Error: unknown method 'Where(?)'of System.Linq.IQueryable
You can try using include this way:
var query = _context.MemberPoint.Include("Resource.ResourceDetails")
.Where(m => m.MemberId == 111111);
Or try joining on resourceId and selecting an anonymous type with the data you need:
var query = (from m in _context.MemberPoint
join rd in _context.ResourceDetails on m.ResourceId equals rd.ResourceId
where m.MemberId == 11111
select new
{
Member = m,
ResourceDetail = rd
})
You are using EF completely incorrectly.
What you want is actually
If ResourceDetails has one Resource and each reasource has one member (unlikely).
var query = _context.ResourceDetails
.Include(d => d.Resource)
.Include(r => r.Resource.Memberpoints)
.Where(d => d.Resource.Memberpoints.MemberId == 1);
If ResourceDetails has one Resource and each resource can have multiple Members.
var query = _context.ResourceDetails
.Include(d => d.Resource)
.Include(r => r.Resource.Memberpoints)
.Where(d => d.Resource.Memberpoints.Any(m => m.MemberId == 1));
If ResourceDetails has multiple Resources (unlikely) and each resource can have multiple Members.
var query = _context.ResourceDetails
.Include(d => d.Resource)
.Include(r => r.Resource.Memberpoints)
.Where(d => d.Resource.Any(r => r.Memberpoints.Any(m => m.MemberId == 1)));
Okay. So what about the join you wanted? Well that is the job of the ORM. The ORM mapping already knows how ResourceDetails are linked to Members.
So what was that error you got?
Well, the sig of IQueryable.Where() takes a Func<T, bool> and returns an IQueryable<T>.
So in your example, the inner Where is wrong because you are giving it a Func<T, int>. The outter Where is wrong because you are passing a IQueryable<T> to it (although the compiler doesn't know that because its all sorts of wrong already).
TL:DR
In general, don't join with EntityFramework/Linq. EF should have the associations in the mappings and already knows how to join entities together.
Assuming MemberId is unique as per your query example. Try this
var query = _context.ResourceDetails
.Include(d => d.Resource)
.Include(r => r.Resource.Memberpoints)
.Where(e => e.ResourceId == e.Resource.Memberpoints.Where(m => m.MemberId == 1))
I have a L2S repository query which I'm stuggling to write in a nice way. It looks something like...
_orderRepository
.GetAllByFilter(o => o.CustomerId == id)
.Select(o =>
new CustomerOrderRecord
(
o.Id,
o.PartNumber,
o.Date
// ... etc, more order details
/* Here I need the last DateTime? the customer placed
an order for this item, which might be null.
So I end up with the following horrible part of
the query */
o.Customer.CustomerOrderRecords
.Where(x => x.PartNumber == o.PartNumber)
.OrderByDescending(x => x.Date).FirstOrDefault()
== null ? null :
o.Customer.CustomerOrderRecords
.Where(x => x.PartNumber == o.PartNumber)
.OrderByDescending(x => x.Date).First().Date;
)).ToList();
So hopefully you can see the problem that I'm having to write the whole query chain twice just to do the null check when receiving the LastOrdered value.
This needs to be written in-line (I think) because GetAllByFilter returns an IQueryable.
I tried to use an intermediate variable within the select statement, so I'd have something a bit like the following, but I couldn't get anything like that to compile.
.Select(o =>
new CustomerOrderRecord
(
o.Id,
o.PartNumber,
o.Date
// ... etc, more order details
var last = o.Customer.CustomerOrderRecords
.Where(x => x.PartNumber == o.PartNumber)
.OrderByDescending(x => x.Date).FirstOrDefault()
== null ? null : last.Date;
)).ToList();
Is there a syntax trick available which solves this problem?
Try using Select to fetch the Date member:
o.Customer.CustomerOrderRecords
.Where(x => x.PartNumber == o.PartNumber)
.OrderByDescending(x => x.Date)
.Select(x => (DateTime?)x.Date)
.FirstOrDefault()