Right problem I've been trying to sort for ages now!
I'm trying to display the result from a SQL select command and display this information to my view. I can't simple use EntityFramework, not from what I can see anyway, to do the command because I am bringing in data from 2 different tables and displaying as one, this is for a report.
this is the sql command I need to run.
select FirstName, LastName,
(select count(*) from Orders o where U.userID = o.CreatedByUserID and ProductID = 1) as ProductCount
from Users U
order by UserID
Is there anything I can do to run this command? I'd be willing to try a EntityFramwork way of doing it if I can't run the SQL directly.
Thanks in advance for any help!
You could use EF (or another ORM solution). There is clearly a relationship between the orders and the users: U.userID = o.CreatedByUserID. You can just retrieve the users, and then access user.Orders.Count to get the "missing" value.
Assuming ctx is your Context try
var q=from u in ctx.users
join x in ctx.orders.Where(y=>y.ProductID==1).GroupBy(y => y.CreatedByUserID)
on u.UserID equals x.Key into joined_orders
from o in joined_orders.DefaultIfEmpty()
select new {
FirstName=u.FirstName,
LastName=u.LastName,
Count=o.Count()
};
This assumes that you don't have any Relationships defined in your model, otherwise you could just use
var q=from u in ctx.users
select new {
FirstName=u.FirstName,
LastName=u.LastName,
Count=o.Count(x=>x.ProductID==1)
};
Related
I have had an extensive look around on SE, tried all of the suggestions, checked out MSDN how to perform Left Join equivalent in LINQ to SQL and I have constructed my LINQ query according to MSDN example.
However, the result is not what SQL would return and I am completely lost as to where am I going wrong.
Here is some details:
I have two tables, Customers and Reports. A customer can submit many reports or none. In the current state I have many more reports than customers.
LINQ code:
var query = {from c in customers
join r in reports on c.Id equals r.Id into temp
from items in temp.DefaultIfEmpty()
select new {
c.Id,
LastReportDate = items?.DateCreated ?? DateTime.MinValue
}).ToList();
SQL code:
SELECT [Customers].[Id], R.LastReport AS LastReportDate FROM [Customers]
LEFT JOIN (
SELECT Reports.Id, MAX( [Reports].[Created] ) AS LastReport
FROM Reports GROUP BY Reports.Id
) AS r ON [Customers].[Id] = r.[Id]
The problem is that the query returns number of elements equal to number of reports. However, what I want is to get a list with all customers and for those who have submitted a report I wish to display the date of the most recent report, for those who have not submitted anything, I am happy to leave it NULL or DateTime.MinValue
Any help would be greatly appreciated. I guess I am missing a group by call somewhere in my LINQ code...
Im thinking probably something like this:
var query =
from c in customers
join r in reports on c.Id equals r.Id into g
select new
{
c.Id,
LastReportDate = g.Max(x => (DateTime?)x.Created)
};
you are now joining on join r in reports on c.Id equals r.Id into temp
this looks like: join on a customer.Id on Reports.Id, since you say there are 1 to many relation/rapport. I think your table will have a Reports.CustomerId. Is this correct?
So your query should something look like:
var results = customer.Where(c => c.Reports.Any())
.SelectMany(c => {c, c.Reports.Max(r => r.Created)})
.ToList();
the select comes out of my head, so i am probably missing something ;)
Have you tried LinqPad ? There you can type your linq-queries, and directly see your sql code and results. Works like a charm!
That title is not very good, so consider the following. I have five tables:
User {
Id,
ProfileId // -> Profiles.Id
}
Profile {
Id
}
ProfilePermissionSets {
ProfileId // -> Profiles.Id
PermissionSetId // -> PermissionSets.Id
}
UserPermissionSets {
UserId // -> Users.Id
PermissionSetId // -> PermissionSets.Id
}
PermissionSets {
Id
}
Permissions {
Id,
PermissionSetId // -> PermissionSets.Id
}
And I want get all of the permissions for a user that are directly linked to it or indirectly through the profile. The not-quite-there SQL I've come up with so far is this:
SELECT [Pe].[Controller],
[Pe].[Action]
FROM [PermissionSets] AS [PS]
JOIN [UserPermissionSets] AS [UPS]
ON ([UPS].[PermissionSetId] = [PS].[Id])
JOIN [Users] AS [U]
ON ([U].[Id] = [UPS].[UserId])
JOIN [Profiles] AS [P]
ON ([P].[Id] = [U].[ProfileId])
JOIN [ProfilePermissionSets] AS [PPS]
ON ([PPS].[ProfileId] = [P].[Id])
JOIN [Permissions] AS [Pe]
ON ([Pe].[PermissionSetId] = [PS].[Id])
WHERE [U].[Id] = 4;
It returns back a correct count of rows, but it's repeating the controller or action over and over, so it's wrong. I'm hoping someone can help me correct it to show all of the distinct permission sets for the user. Ideally, I'd like to also change it so that it's all discovered starting at the user because that is what I have access to in the method I need to do this (the object is an Entity Framework class named User and will be browsed using LINQ).
UPDATED because I forgot that I really wanted the permissions not the permission sets.
Try this SQL
SELECT [Pe].[Controller],
[Pe].[Action]
FROM [Users] AS [U]
LEFT OUTER JOIN [UserPermissionSets] AS [UPS]
ON ([UPS].[UserId] = [U].[Id])
LEFT OUTER JOIN [ProfilePermissionSets] AS [PPS]
ON ([PPS].[ProfileId] = [U].[ProfileId])
LEFT OUTER JOIN [Permissions] AS [Pe]
ON ([Pe].[PermissionSetId] = [UPS].[PermissionSetId])
OR ([Pe].[PermissionSetId] = [PPS].[PermissionSetId])
WHERE [U].[Id] = 4;
So, messing around on LINQPad, I came up with this as far as the LINQ query:
user.PermissionSets.Union(user.Profile.PermissionSets).SelectMany(
ps =>
ps.Permissions.Select(
p =>
p.Controller + "." + p.Action));
And it produces what I want, BUT it does it by composing the results of a bunch of SQL queries. The biggest impact comes from profiles that have multiple permission sets, like say the Administrator. I don't think there's a way around it, and I only have a User object to work with, so I'm ok with the excess SQL queries, at least for now.
I've found plenty of info on how to select multiple result sets with stored procedures but nothing substantial on how to do so with a linq query.
For example, I can do sub-queries that return mulitple sets of results with something like
var query = (from school in context.Schools
where school.id == someId
select new
{
subSetA = (from student in context.Students
select student).ToList(),
subSetB = (from building in context.Buildings
select building).ToList(),
}).First();
query.subSetA; //Access subSetA
query.subSetB; //Access subSetB
Which works fine, but what if I just want to select both subSetA and subSetB without querying against the school table? I want to select two separate sets of data that gets sent to the server in one query.
Any information as to how to do this with EF 6 would be great.
Well, I'm sure there are many ways to do this, but if you want to avoid introducing a third DbSet into the mix...
var query = (from s in context.Students.Take(1)
select new
{
subSetA = context.Students.ToList(),
subSetB = context.Buildings.ToList(),
})
Then, you can use query.ToList() or maybe using query.Load() and working with context.Students.Local, etc. would work.
I have 2 tables:
Users data with PK UserId
UsersOrders with FK UserID
I want to do LINQ query to give me a list of users with member that is the list of the user orders.
I've tried:
var myNestedData = (from ub in db.Users
join ah in db.UsersOrders on ub.UserId equals ah.UserId
into joined
from j in joined.DefaultIfEmpty()
group j by new { ub.UserId, ub.UserName, ub.UserPhone, ub.Approved } into grouped
where grouped.Key.Approved == true
select new
{
UserId= grouped.Key.UserId,
UserName = grouped.Key.UserName,
UserPhone = grouped.Key.UserPhone,
Orders = grouped
}).ToList();
The problem is that I'm getting inside Orders an Object<a,UsersOrders>, which I don't expect.
Is this the right way to approach a solution in terms of performance?
It seems to me you should be able to keep this simple:
var myNestedData = (from u in db.Users
where u.Approved == true
select new
{
User = u,
Orders = u.UserOrders
}).ToList();
You are trying to get a list of all the Approved users and that users orders. Am I missing something?
ALso the name of u.UserOrders will depend on how you have configured your mapping.
Change it to
Orders = grouped.ToList()
To see if it's a potential performance issue, look at the SQL that is generated to see if it is an N+1 scenario (meaning it runs one query for the parent record and N queries for the child records.
I say potential because while may not be the fasted method, it may not be an issue. If there is no noticeable performance problems I wouldn't worry about it and instead focus on making the app better by improving the experience, which may include improving the perfornamce of other areas of the map.
I'm new to linq and I need a certain join. So I have two tables for certain pages I want to join with Linq that are something like
UserFavorites table:
Page.id
userClickCount
Page table:
id
everyoneClickCount
A user favorite is created when it's clicked or made a favorite so there are only a certain amount of links. I want to show all results from both tables, sort them by the most clicked by the user, then most clicked by everyone.
I have this right now but it sorts by the everyone count.
pages = (from page in context.Page
join ps in
(from favs in context.UserFavorites
select favs) on page.Id equals ps.Page.Id into temp
from t in temp.DefaultIfEmpty()
orderby t.userClickCount descending, t.Page.everyoneClickCount descending, t.Page.PageName ascending
select dash).ToList();
I'm just not sure where to go from here.
Try this:
var pages = context.Pages.Select(page => new {
PageData = page,
Favs = page.UserFavorites
})
.OrderByDescending(page => (page.Fav.Sum(userClickCount))
.ThenBy(page => page.PageData.everyoneClickCount)
.ThenBy(page => page.PageData.Name)
.ToList();
Note that this will order by the sum of all users' click counts. If you just want the current user, replace that line with .OrderByDescending(page => (page.Fav.Where(u => u.userID == userid).Sum(userClickCount))
For complex joins I suggest writing a stored procedure and then using EF and linq to call this SP.