LINQ To Entities Multiselect - c#

Please advise,, ALL the resources I've perused , books, videos tutorials none have what I would assume to be a simple, common scenario.
-----What is the Most efficient and standard way of querying through bridge tables with Link To Entities-----
I'm trying to to a query with Linq To Entities.I is my understanding that LinqToSQL is deprecated even if informally..
I have a standard
one to many (bridge table)
and the bridge table many to one for the final table
and lastly a specific key for the original table
If it helps there's a User table atop this mess with a one to many user to roles
Tables:
User, ( Not in the Query I have a specific KeyId for this table for the where clause),
Role,
&
RolePermission, ( Bridge / map whatever you want to call it, it has FK's for Role & Permission )
[unfortunately it has other cols or this wouldn't be so stressful. I.E. its not
abstracted in the entity framework its actually present],
& Permission.
Summary I want every permission for every role for this user, User ID lets say 5
I have a list of roles per user so (to start) I intended on feeding this query the role ID and calling it multiple times ,,, appending its results to a list 4Ea RoleId This is assuredly not ideal
Ideal would be to utilize the UserID & RoleID in a single query...
How do you do Multiple Inner Joins in Linq to Entities
This link above claimed that you can just pull his off by requesting all the tables involved without specifically joining them the PK-FK fields ?!?!?! Say What ??!
This is Just Peuedo code folks I've typed up several dozen attempts at this scenario
parameter p_RoleId --- potential version of this could get RoleId's per passed in UserID ideally
List<Permissions> listToReturn = new List<Permissions>();
var result=(from p in context.Permissions
from rp in m.RolePermissions
where m.roleID = p_RoleId
listToReturn result.ToList();
I really just want to know how to correctly hook these tables together and specify some where clause.
Two table examples are everywhere.. but there or four when you have a bridge table I found nothing

try this
var result = (from p in context.Permissions
join px in m.RolePermissions on p.roleID equals px.roleID
select p);

Well you can definitely do what you are writing in your pseudo code. After all what you do is an implied join. But it might not be very efficient.
You are essentially doing a
select <something> from Permissions , RolePermissions where permissions.id = permissionsRoles.id;
And you do not need a foreign key for that. However in your case you have many to many
//with the assumption that you have a
//navigation property between roles and rolepermissions in your model
var result = connection.Permissions
.Where(i => i.Id == RolePermissionsid)that
.SelectMany(i => i.RolePermissions).ToList();

Is this what you want?
var result=(from p in context.Permissions
join rp in m.RolePermissions on p.RoleId equals rp.RoleId
select p)

Related

LINQ Grouping a List of Objects into Anonymous Type

I am having difficulty trying to use LINQ to query a sql database in such a way to group all objects (b) in one table associated with an object (a) in another table into an anonymous type with both (a) and a list of (b)s. Essentially, I have a database with a table of offers, and another table with histories of actions taken related to those offers. What I'd like to be able to do is group them in such a way that I have a list of an anonymous type that contains every offer, and a list of every action taken on that offer, so the signature would be:
List<'a>
where 'a is new { Offer offer, List<OfferHistories> offerHistories}
Here is what I tried initially, which obviously will not work
var query = (from offer in context.Offers
join offerHistory in context.OffersHistories on offer.TransactionId equals offerHistory.TransactionId
group offerHistory by offerHistory.TransactionId into offerHistories
select { offer, offerHistories.ToList() }).ToList();
Normally I wouldn't come to SE with this little information but I have tried many different ways and am at a loss for how to proceed.
Please try to avoid .ToList() calls, only do if really necessary. I have an important question: Do you really need all columns of OffersHistories? Because it is very expensive grouping a full object, try only grouping the necessary columns instead. If you really need all offerHistories for one offer then I'm suggesting to write a sub select (this is also cost more performance):
var query = (from offer in context.Offers
select new { offer, offerHistories = (from offerHistory in context.OffersHistories
where offerHistory.TransactionId == offer.TransactionId
select offerHistory) });
P.s.: it's a good idea to create indexes for foreign key columns, columns that are used in where and group by statements, those are going to make the query faster,

Linq select in list with 2 many-to-many relationships

I have the following database structure:
USER <--> [user_client] <--> CLIENT <--> [client_application] <--> APPLICATION
USER, CLIENT and APPLICATION tables contain unique keys. user_client and client_application are many-to-many tables to map USERs to CLIENTs and CLIENTs to APPs.
I am using MVC5 / C#. The many-to-many tables are hidden in my Model by the Entity Framework.
What I want to achieve is the following: for a given USER, which has a list of CLIENTs, get the combined list of the distinct APPLICATIONs that all his CLIENTs have.
Could you please help with the logic and the Linq query (preferably in fluent syntax if possible)? Is it possible to do it in a single query without looping through the list of clients?
Thank you in advance.
Reda
Not sure it matches your schema but what about
user.clients.SelectMany(c => c.applications).Distinct()
The key is to use SelectMany instead of Select which will give you a IEnuerable<Application> instead of a IEnumerable<IEnumerable<Application>>
var user = context.Users.Where(u => u.Id == 1).Single();
var applications = user.Clients
.SelectMany(c => c.Application)
.GroupBy(a = a.Id)
.Select(a => a.First());
I want to collaborate to this question, not providing an exact solution, but additional information.
I applied #tafia answer to my own problem, but with a slight modification to the SelectMany method. I replaced it with just Select.
File selectedFile = _unitOfWork.FileRepository.GetById(idFile)
selectedFile.FilePaper.Select(c => c.Paper).Distinct().ToList()
You can read about the difference between Select and SelectMany, here.
I applied my modified solution on the following group of tables:
Though your table seems to be different:
I'm not sure if that solution proposed by #tafia works there.
PS. If you want to make the middle tables appear in EF, a possible "fix" is adding a primary key to them (id).

EF Query Help. Difficult query with interhited types and self referencing

I'm still new to EF, and I have figured out some basic queries. I'm stumped on one of the more difficult things I am trying to acheive.
I have 4 entities
User, Tenant, Building, Apartment
Tenant inherits from User
At this point, a user who is not a Tenant can be thought of as a Landlord.
User (Landlord) has one to many Buildings
Buildings have one to many Apartments
Apartment has one to many Tenants (one active at any given time)
I am trying to figure out how to create an EF query that will give me the list of Tenants for a given User (Landlord).
The SQL that gives me what I want is:
SELECT u2.User_ID AS TenantUser_ID, u2.UserName
FROM Users u
LEFT JOIN rt_UserBuilding ub ON u.User_ID = ub.User_ID
LEFT JOIN Buildings b ON ub.Building_ID = b.Building_ID
LEFT JOIN Apartments a ON a.Building_ID = b.Building_ID
LEFT JOIN Tenants t ON a.Apartment_ID = t.Apartment_ID
LEFT JOIN Users u2 ON t.User_ID = u2.User_ID
WHERE u.User_ID = 1 AND t.User_ID IS NOT NULL
Assuming your Building class has property
User Landlord
the simplest way is to do this:
context.Tenants.Where(tenant => tenant.
Apartment.
Building.
Landlord.Id == yourLandlord.Id
).ToList();

Doing Eager Loading and Projection in Linq to Entities

This is a spin off from another question I posted a few days ago that was successfully answered but it really didn't get into the underlying issue I was having but wasn't able to express fully in my original query.
I have a table Product. Product is related to ProductDescription in a one-to-many relationship. ProductDescription can have more than one row for each product. It will have multiple rows if there are multiple translations for the description of the product. Product has a many-to-one relationship with Language. Language has a language code (en, es, etc.) as well as a LanguageId (found in ProductDescription as well).
I want to give my users the ability to request a product and further tell the application to only return descriptions for that product in a specific language.
The problem I'm having is that I understand I need to use projection to accomplish the task in the 3rd paragraph of this question. Something like this:
var query = inventoryRepository.Products
.Where(wherePredicate)
.Select( a=> new Product
{
ProductDescriptions = inventoryRepository.ObjectContext.ProductDescriptions
.Where(a => a.Languages.AlternateCode.Equals("en", StringComparison.CurrentCultureIgnoreCase))
});
However I have about 15 properties in the Products table as well as 4 other child tables of Products that need to be loaded for the result set in addition to just the languages piece. Is there any way for me to do eager loading AND projection so that I don't have to go through and map all of these properties and children objects manually? Here is where my code stands right now:
var query = inventoryRepository.ObjectSet
.Include("ProductDescriptions")
.Include("ProductAllowedWarehouses")
.Include("ProductToCategories")
.Include("PriceLevels")
.Include("AttachmentAssociations.Attachment").AsExpandable()
.Where(wherePredicate);
No Select necessary which is really nice. Once I change ProductDescriptions to a projection I add a Select and then I don't get any of the other properties / children populated for free.
I would read this blog about projections+eager loading. You are going to run into issues when it comes to the eager loading. Recommendation is to use a .Any and perform a sub select.
Why can't you just add a filter/where on ProductDescriptions?
var query = inventoryRepository.ObjectSet
.Include("ProductDescriptions")
.Include("ProductAllowedWarehouses")
.Include("ProductToCategories")
.Include("PriceLevels")
.Include("AttachmentAssociations.Attachment").AsExpandable()
.Where(wherePredicate)
.Where(a=>a.ProductDescriptions.Languages.AlternateCode.Equals("en", StringComparison.CurrentCultureIgnoreCase);

Linq Sort Nested Table

Follow up question to this:
Linq Combine Left Join Data
Say I have the following db tables:
Users
-------
UserId (PK)
UserName
Roles
-----
RoleId (PK)
RoleName
UserRoles
---------
UserId (PK)
RoleId (PK)
Users 1-M UserRoles M-1 Roles
Using LinqToSQL, I can return the following set (thanks to response from prior question):
[User1], [Role1, Role2, Role3]
[User2], [Role2, Role3]
[User3], [Role3]
The twist is I am trying to sort by Roles. How can I sort the result by Roles?
Clarification
I have a grid, when the user clicks on the Roles column, the rows would be sorted by that column.
So to start the grid would look like this:
[User1], [Role1, Role2, Role3]
[User2], [Role2, Role3]
[User3], [Role3]
Then if they sort on Roles column it would look like this
[User3], [Role3]
[User2], [Role2, Role3]
[User1], [Role1, Role2, Role3]
Just change the original answer very slightly:
from u in dataContext.Users
select new { User = u, Roles = u.UserRoles.Select(ur => ur.Role)
.OrderBy(r => r.RoleName) };
(That's assuming you want to sort each element of the result by the roles it contains. If that's not correct, please explain what you want in more detail.)
Could you not simpy use something like this?
// users is the collection you already got from Linq2Sql
var usersSorted = from u in users order by u.Roles select u;
int ascending = 1; //set to -1 for descending
from u in Users
orderby u.Roles.Count * ascending
select new { u, u.Roles.OrderBy(x => x.RoleName) }
Your query will need to cater for the many to many though (not shown).
Hey #zzz, the answers I've seen so far seem to indicate how to sort the rows for each user, whereas, if I understand your clarification, you do want that, but what you're asking for is how to then sort those statements alphabetically. I'll try to provide an answer to that question.
Though your request is very common, regrettably, SQL does not have a native way to convert a table (the Roles column) to a comma delimited string. This normally isn't a problem because you can simply return the Roles field as
{
UserName = u.UserName,
RolesList = string.Join(", ",
u.UserRoles.Select(ur => ur.Role.RoleName).ToArray())
}
This will work, surprisingly, even though I just mentioned that there is no equivalent function to string.Join in SQL. That's because LINQ is smart enough to just have SQL return the table and to apply the string.Join() at the last minute, in memory.
The problem comes when you then try to sort by the RoleList field as it is created in memory and not in SQL. If you do you'll get the following error message.
NotSupportedException: Method
'System.String Join(System.String,
System.String[])' has no supported
translation to SQL.
This leaves you with two choices:
Write a stored procedure to do this that utilizes a custom function to convert a table to a comma separated list.
OR bring the entire result set back into memory by returning it as .ToList() and then performing the sort ie (/my whole query/).ToList().OrderBy(q => q.Roles);
The second option will be much slower if you have a large dataset. If you user list is never going to grow very large or this query will only get called infrequently when an admin loads the user management screen, then the memory option may not be noticeably slower; however, if this query will be called frequently and/or the user table will get large, then this may not be a viable option.
I would suggest a third option. Reappraise the requirements. In this case, the user may really need a filtering feature where they can look at all users who are in a, b, c roles. If that is the true need, then sorting is not only much harder to implement, but it may also be a worse solution.
Good luck!

Categories

Resources