How to return distinct rows from and Entity with related Entities - linq

I have an Entity that has an association to other Entities (related entities). I'm trying to return distinct rows from the primary entity which needs to include the data from the related entity so I can use one the related entity's properties downstream.
Below is the statement I'm using but it is not returning any rows. What's the best way to do this?
Below is my code.
return context.UserDisplays.Include("CurrentJob").Where(d => d.UserName == userName).GroupBy(d => d.CurrentJob.JobNo).Select(g => g.FirstOrDefault()).ToList();
Any help would be greatly appreciated!

Edit - For ComplexProperty
I believe once you do a GroupBy all Include methods are ignored. So you will need to iterate the list and call the LoadProperty method on each item. It should look something like this
var list = context.UserDisplays
.Where(d => d.UserName == userName)
.GroupBy(d => d.CurrentJob.JobNo)
.Select(g => g.FirstOrDefault()).ToList();
foreach(var item in list)
{
context.LoadProperty(item, "CurrentJob");
}
return list;
Resource Link

Check out the Distinct (Set Operators) section in this article
http://msdn.microsoft.com/en-us/vcsharp/aa336746
Are you asking for the Distinct UserDisplays? or the Distinct User or the Disticnt Jobs?
I would try say something like
var object = (from userDisplay in context.UserDisplays.Include("CurrentJob")
.Where userDisplay.UserName == userName
Select userDisplay).Distinct();
(sorry, im going off of my VB style but it should be about the same...)

Related

NotSupportedException for LINQ Queries

I am trying to get a list of a database table called oracleTimeCards whose employee id equals to the employeeID in employees list. Here is what I wrote:
LandornetSQLEntities db = new LandornetSQLEntities();
List<OracleEmployee> employees = db.OracleEmployees.Where(e => e.Office.Contains(officeName) && e.IsActive == true).Distinct().ToList();
var oracleTimeCards = db.OracleTimecards.Where(c => employees.Any(e => c.PersonID == e.PersonID)).ToList();
Anyone has any idea?
I'm going to assume you're using Entity Framework here. You can't embed calls to arbitrary LINQ extension methods inside your predicate, since EF might not know how to translate these to SQL.
Assuming you want to find all the timecards for the employees you found in your first query, you have two options. The simplest is to have a navigation property on your Employee class, named let's say TimeCards, that points to a collection of time card records for the given employee. Here's how that would work:
var oracleTimeCards = employees
.SelectMany(e => e.TimeCards)
.ToList();
If you don't want to do this for whatever reason, you can create an array of employee IDs by evaluating your first query, and use this to filter the second:
var empIDs = employees
.Select(e => e.PersonID)
.ToArray();
var oracleTimeCards = db.OracleTimecards
.Where(tc => empIDs.Contains(tc.PersonID))
.ToList();

preserving the order of returning entities when using .Contains(Id)

I want to hydrate a collection of entities by passing in a List of Ids and also preserve the order.
Another SO answer https://stackoverflow.com/a/15187081/1059911 suggested this approach to hydrating the entities which works great
var entities = db.MyEntities.Where(e => myListOfIds.Contains(e.ID)).ToList();
however the order of entities in the collection is different from the order of Ids
Is there a way to preserve the order?
May be that helps:
var entities = db.MyEntities
.Where(e => myListOfIds.Contains(e.ID))
.OrderBy(e => myListOfIds.IndexOf(e.ID)).ToList();
EDIT
JohnnyHK clarified that this will not work with LINQ to Entities. For this to work you need to order IEnumerable instead of IQueryable, since IQueryProvider don't know how to deal with local list IndexOf method when it sends query to server. But after AsEnumerable() OrderBy method deals with local data. So you can do this:
var entities = db.MyEntities
.Where(e => myListOfIds.Contains(e.ID))
.AsEnumerable()
.OrderBy(e => myListOfIds.IndexOf(e.ID)).ToList();
Entity Framework contains a subset of all of the LINQ commands so you won't have all the commands that LINQ to Objects has.
The following approach should give you your list of MyEntities in the same order as supplied by myListOfIds:
var entities = myListOfIds.Join(db.MyEntities, m => m, e => e.ID, (m,e) => e)
.ToList();

Linq and Lambda expression for a complex sql query involving joins

Using Linq to Entity (Entity Framework) in MVC 3 project.
My model:
Table - Users
UserID (PK)
...
Table - Clients
ClientID (PK)
Table - PropertyItems
PropertyItemID (PK)
Table - MemberContactPreference (Contains PropertyItems selected by Users- many to many)
UserID(FK)
PropertyItemID(FK)
Table ClientProperties (Contains PropertyItems that belong to Clients - many to many)
ClientID (FK)
PropertyItemID (FK)
I want to list all the distinct users that have selected all the properties selected by clients.
My Approach :
I got a list of all properties for a particular client in
Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID)
Iqueryable<User> UsersMatchingClientProperties = GetAllUsers();
foreach (ClientProperty property in clientproperties)
{
UsersMatchingClientProperties = (from uem in UsersMatchingClientProperties
join ucp in GetAllMemberContactPreferences on
ucp.UserID == uem.UserID
where uem.MemberContactPreferences.SelectMany(
mcp => mcp.PropertyItemID == property.PropertyItemID)
select uem).Distinct;
}
It gives the right result only first time. As it doesn't reduce the number of items in UsersMatchingClientProperties with each iteration. actually it replaces the collection with new resultset. I want to filter out this collection with each iteration.
Also, any suggestions to do this in Lambda expression without using Linq.
Thanks
That generation of an iqueryable in a for loop seems like a dangerous thing, which could end up in a monster sql join being executed at once.
Anyway, I don't think you need that. How about something like this?
// for a given client, find all users
// that selected ALL properties this client also selected
Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID)
Iqueryable<User> allUsers= GetAllUsers();
Iqueryable<MemberContactPreference> allMemberContactProperties = GetAllMemberContactPreferences();
Iqueryable<User> UsersMatchingClientProperties = allUsers
.Where(user => allMemberContactProperties
.Where(membP => membP.UserID==user.UserID)
.All(membP => clientProperties
.Select(clientP => clientP.PropertyID)
.Contains(membP.PropertyID)
)
);
Here is an alternative query in case you want the users that selected ANY property for a given client
// for a given client, find all users
// that selected ANY properties this client also selected
Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID)
Iqueryable<User> allUsers= GetAllUsers();
Iqueryable<MemberContactPreference> allMemberContactProperties = GetAllMemberContactPreferences();
Iqueryable<User> UsersMatchingClientProperties = clientproperties
.Join(allMembersContactProperties, // join clientproperties with memberproperties
clientP => clientP.PropertyItemID,
membP => membP.PropertyItemID,
(clientP, membP) => membP)) // after the join, ignore the clientproperties, keeping only memberproperties
.Distinct() // distinct is optional here. but perhaps faster with it?
.Join(allUsers, //join memberproperties with users
membP => membP.UserID,
user => user.UserID,
(membP, user) => user)) // after the join, ignore the member properties, keeping only users
.Distinct();
I trust Hugo did a good job suggesting ways to improve your query (+1). But that does not yet explain the cause of your problem, which is the modified closure pitfall.
I think that after your loop there is some code that actually executes the query in UsersMatchingClientProperties. At that moment the query is executed with the last value of the loop variable property! (The loop variable is the closure in each query delegate that is created in an iteration, and it is modified by each iteration).
Change the loop like this:
foreach (ClientProperty property in clientproperties)
{
var property1 = property;
...
and use property1 in the query. That should solve the cause of the problem. But as said, it looks like the whole process can be improved.

Entity Framework 4 - What is the syntax for joining 2 tables then paging them?

I have the following linq-to-entities query with 2 joined tables that I would like to add pagination to:
IQueryable<ProductInventory> data = from inventory in objContext.ProductInventory
join variant in objContext.Variants
on inventory.VariantId equals variant.id
where inventory.ProductId == productId
where inventory.StoreId == storeId
orderby variant.SortOrder
select inventory;
I realize I need to use the .Join() extension method and then call .OrderBy().Skip().Take() to do this, I am just gettting tripped up on the syntax of Join() and can't seem to find any examples (either online or in books).
NOTE: The reason I am joining the tables is to do the sorting. If there is a better way to sort based on a value in a related table than join, please include it in your answer.
2 Possible Solutions
I guess this one is just a matter of readability, but both of these will work and are semantically identical.
1
IQueryable<ProductInventory> data = objContext.ProductInventory
.Where(y => y.ProductId == productId)
.Where(y => y.StoreId == storeId)
.Join(objContext.Variants,
pi => pi.VariantId,
v => v.id,
(pi, v) => new { Inventory = pi, Variant = v })
.OrderBy(y => y.Variant.SortOrder)
.Skip(skip)
.Take(take)
.Select(x => x.Inventory);
2
var query = from inventory in objContext.ProductInventory
where inventory.ProductId == productId
where inventory.StoreId == storeId
join variant in objContext.Variants
on inventory.VariantId equals variant.id
orderby variant.SortOrder
select inventory;
var paged = query.Skip(skip).Take(take);
Kudos to Khumesh and Pravin for helping with this. Thanks to the rest for contributing.
Define the join in your mapping, and then use it. You really don't get anything by using the Join method - instead, use the Include method. It's much nicer.
var data = objContext.ProductInventory.Include("Variant")
.Where(i => i.ProductId == productId && i.StoreId == storeId)
.OrderBy(j => j.Variant.SortOrder)
.Skip(x)
.Take(y);
Add following line to your query
var pagedQuery = data.Skip(PageIndex * PageSize).Take(PageSize);
The data variable is IQueryable, so you can put add skip & take method on it. And if you have relationship between Product & Variant, you donot really require to have join explicitly, you can refer the variant something like this
IQueryable<ProductInventory> data =
from inventory in objContext.ProductInventory
where inventory.ProductId == productId && inventory.StoreId == storeId
orderby inventory.variant.SortOrder
select new()
{
property1 = inventory.Variant.VariantId,
//rest of the properties go here
}
pagedQuery = data.Skip(PageIndex * PageSize).Take(PageSize);
My answer here based on the answer that is marked as true
but here I add a new best practice of the code above
var data= (from c in db.Categorie.AsQueryable().Join(db.CategoryMap,
cat=> cat.CategoryId, catmap => catmap.ChildCategoryId,
cat, catmap) => new { Category = cat, CategoryMap = catmap })
select (c => c.Category)
this is the best practice to use the Linq to entity because when you add AsQueryable() to your code; system will converts a generic System.Collections.Generic.IEnumerable to a generic System.Linq.IQueryable which is better for .Net engine to build this query at run time
thank you Mr. Khumesh Kumawat
You would simply use your Skip(itemsInPage * pageNo).Take(itemsInPage) to do paging.

LINQ to Entities multiple columns need 1 to be distinct

I am trying to select multiple columns from an entity object but I want 1 property to be distinct. I am very new to both LINQ and Entity Framework so any help will be useful.
Here is my LINQ query so far:
var listTypes = (from s in context.LIST_OF_VALUES
orderby s.SORT_INDEX
select new { s.LIST_TYPE, s.DISPLAY_TEXT });
I want s.LIST_TYPE to be distinct. I figure using the groupby keyword is what I want (maybe?) but I have not found a way to use it that works.
Thank you.
Assuming DISPLAY_TEXT matches LIST_TYPE somehow (so you don't lose any information):
var distinct = context.LIST_OF_VALUES
.OrderBy(s => s.SORT_INDEX)
.GroupBy(s => s.LIST_TYPE)
.Select(g => new { g.Key, g.First().DISPLAY_TEXT });

Resources