Entity Framework query ignoring my orderby - c#

I have myself this SQL query
SELECT
db_accounts_last_contacts.id,
dbe_accounts_last_contacts.last_contact_date,
db_accounts_last_contacts.description,
db_accounts_last_contacts.follow_up_date,
db_accounts_last_contacts.spoke_to_person_id,
db_accounts_last_contacts.account_idFROM
db_accounts_last_contacts ,
db_companies
WHERE db_companies.id = db_accounts_last_contacts.account_id
ORDER BY db_accounts_last_contacts.last_contact_date DESC
Which returns my results ordered by last_contact_date.
Now I have my Entity framework query
var query = (from c in context.accounts_companies
select new AccountSearchResultModel()
{
LastContacted = (from calc in context.communique_accounts_last_contacts
where calc.account_id == companyId
orderby calc.last_contact_date descending
select calc.last_contact_date).FirstOrDefault()
});
However when I go ahead and do my ToList on it, my results are never ordered
Here is my table un-ordered
Here is my list ordered using the SQL query
Why isn't my entity framework query not picking up my orderby? Or if it is why am I always pulling out the first one?

You need to choose a Property to sort by and pass it as a lambda expression to OrderByDescending
like this:
.OrderByDescending(x => x.calc.last_contact_date);
I hope this helps.
Linq Orderby Descending Query

Sorry for the late answer,
What I had to do in the end was create a view and import it via the EDMX file and then use that to pull out my results.

Related

Linq correlated subquery to same table on multiple columns

I've looked at several other questions related to correlated subqueries but it's still not clear to me how to accomplish what I need. I'm using Entity Framework and C#, and have a table called STEWARDSHIP with the following columns:
STEWARDSHIP_ID (the primary key)
SITE_ID
VISIT_DATE
VISIT_TYPE_ID
I need to identify cases where the same combination of SITE_ID, VISIT_DATE, VISIT_TYPE_ID exists more than once because it could represent a duplicate entry made by end users in error, and then I need to report on the details of these entries. In SQL I would do this by joining to the temporary result of a GROUP BY/HAVING like so:
SELECT * FROM stewardship AS s2,
(SELECT site_id, visit_type_id, CAST(visit_date AS DATE) AS visit_date
FROM stewardship
GROUP BY site_id, visit_type_id, CAST(visit_date AS DATE)
HAVING COUNT(*) > 1) AS s
WHERE s2.site_id = s.site_id
AND s2.visit_type_id = s.visit_type_id
AND CAST(s2.visit_date AS DATE) = s.visit_date
What's the best way to accomplish this in Linq?
Since you're open to a different approach that should be more performant, here is the new SQL to get what I think you're after.
select distinct s1.*
from stewardship s1
inner join stewardship s2 on
s1.stewardship_id <> s2.stewardship_id and
s1.site_id = s2.site_id and
s1.visit_type_id = s2.visit_type_id and
cast(s1.visit_date as date) = cast(s2.visit_date as date)
order by s1.site_id, s1.visit_type_id
Now, to translate that to LINQ, you can use the following statement.
var duplicates = (
from s in Stewardships
join s2 in Stewardships
on new { s.Site_id, s.Visit_type_id, s.Visit_date.Date } equals new { s2.Site_id, s2.Visit_type_id, s2.Visit_date.Date }
where s.Stewardship_id != s2.Stewardship_id
select s)
.Distinct()
.OrderBy(s => s.Site_id)
.ThenBy(s => s.Visit_type_id)
Note that you cannot use anything other than an equijoin for expression joins, so I had to put the non-equijoin (ensuring our matches aren't on the same record via PK) in the where expression. You could also accomplish this with lambdas via the Except() extension method.
The order by is there for readability of the results and to match the SQL statement above.
I hope this helps!
It would be fairly similar to what you've already got.
from s in context.stewardships
group s by new {s.site_id, s.visit_type_id, visit_date} into g
where g.Count() > 1
select g;
This would give you groups of stewardships with similar values. You could "flatten" those results with a SelectMany afterward, but you might find them more useful to work with in groups.
Note that you may need to use SqlFunctions or something to do the equivalent of the cast to date.

Linq using Include with Stored Procedure

I have a stored procedure which returns me some dates, as well as an Id which related to a specific row in a table.
Basically, I am getting a list of all scheduled transactions for all accounts within an account portfolio.
The stored procedure returns a row with an Id (for the scheduled transaction), and some dates which I have minded within the proc.
If my query began with:
from p in Context.scheduled_transactions
then this plan would have worked. But I don't want to get the items like that, because in the proc, I am doing a lot of work to create business dates etc. So, instead of bring back the EF model - my proc just brings back the ID. I was HOPING to do something like this:
var trans = (from p in Context.get_scheduled_payments_by_portfolio(portfolioId)
.Include("account")
.Include("cost_centre")
.Include("z_account_transaction_type")
.Include("z_payment_frequency_type")
.Include("transaction_sub_category")
.Include("transaction_sub_category.transaction_category")
.Include("third_party")
select p).ToList();
But, the EF can't use 'Include' as it doesn't know what I am bring back. Although the id is called 'scheduled_transaction_id' in the proc - EF doesn't know that (understandably).
Is there a way I can tell EF that the ID is for a scheduled_transaction_model - and then use the 'Include'?
Maybe I need to just call the proc, which returns me a list of my objects, which has the scheduled_transaction_id, and all the dates I calculated in the proc, and then somehow, use that List<> in another linq query that can join the other tables?
EDIT:
I might be onto something! This doesn't show a syntax error. Just need to create a new Type... Playing with this:
var trans = (from p in Context.get_scheduled_payments_by_portfolio(portfolioId)
join st in Context.scheduled_transaction
.Include("account")
.Include("cost_centre")
.Include("z_account_transaction_type")
.Include("z_payment_frequency_type")
.Include("transaction_sub_category")
.Include("transaction_sub_category.transaction_category")
.Include("third_party")
on p.scheduled_transaction_id equals st.id
select p).ToList();
var ids = Context.get_scheduled_payments_by_portfolio(portfolioId).ToList();
var trans = (from p in Context.scheduled_transaction
.Include("account")
.Include("cost_centre")
.Include("z_account_transaction_type")
.Include("z_payment_frequency_type")
.Include("transaction_sub_category")
.Include("transaction_sub_category.transaction_category")
.Include("third_party")
where ids.Contains(p.id)
select p).ToList();
Try Contains() method which will translated into SQL's IN(,,) statement.
The answer was, join the proc to the table I was using, and then I can use the .Include()
var trans = (from p in Context.get_scheduled_payments_by_portfolio(portfolioId)
join st in Context.scheduled_transaction
.Include("account")
.Include("cost_centre")
.Include("z_account_transaction_type")
.Include("z_payment_frequency_type")
.Include("transaction_sub_category")
.Include("transaction_sub_category.transaction_category")
.Include("third_party")
on p.scheduled_transaction_id equals st.id
select new {st, p}).ToList();
And then with the new type, I can itterate through the list, and build my objects.

Linq - how to get top records?

I have this code which queries a database
var buildInfoList = (from m in context.BuildInfoes
where m.ManagerInfoGuid == managerGuid
select m).Take(3).ToList();
the code above gives me the first 3 results, how can i change it to take the last 3?
meaning if i have 100 rows in the database, i want to get 98, 99, 100 and not 1, 2, 3
Reverse the order of the query. The basic idea is reverse the order of the entire query, fetch the first three elements, then reverse the order again to put them back in the right order:
var query = from m in context.BuildInfoes
where m.ManagerInfoGuid == managerGuid
select m;
var lastItems = query.OrderByDescending(x => x.ID).Take(3).Reverse().ToList();
PS: If you were using Linq to Objects (but I guess you aren't) you could use TakeLast from morelinq.
Your are not introducing any order here, so you currently get any 3 results which by chance don't happen to be the ones you want. Establish an order:
var buildInfoList = (from m in context.BuildInfoes
where m.ManagerInfoGuid == managerGuid
orderby m.Name descending
select m).Take(3).ToList();
Using orderby you can specify ascending or descending to reverse the order, which will result in returning the first or last 3 elements using Take.
You can use orderby
var buildInfoList = (from m in context.BuildInfoes
where m.ManagerInfoGuid == managerGuid
orderby m.Id descending
select m).Take(3).ToList();
Or, as #MarkByers said, just use Reverse
var buildInfoList = from m in context.BuildInfoes
where m.ManagerInfoGuid == managerGuid
select m;
var count = buildInfoList.Count();
var list = buildInfoList.Skip(count < 3 ? count - 3 : 0).Take(3).ToList();
edit: Why is this solution different than the others? But this doesn't mean is the best one.
First the OP states that the query is over a database and since the query uses Take without specifying the order, I guess is about Linq To Sql.
This solution is not actually the best because it does two queries, one for the count and the other for to get the items. This solution uses only the SQL to get the last 3 items and doesn't do an order over objects.
While testing it with LINQ Pad I noticed that, when no order is specified, LINQ to SQL generates the order over all the columns
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[id], [t0].[A], [t0].[B], [t0].[C])
Obs.:
The Reverse method is not translated, so is good to be called after a ToList() call

Convert function to LINQ to SQL

I have such SQL which select, group and order Url field from Statistic table. getDomain is stored function. I am trying to rewrite this SQL to Linq without any luck. Please someone explain how to do that?
SELECT dbo.getDomain(Url) as url
FROM Statistic
GROUP BY dbo.getDomain(Url)
HAVING COUNT(Url) > 1
ORDER BY COUNT(Url)
First you have to define your UDF in the .DBML file that contains other tables and procedures definitions. Then you can Call any UDF function inline within your LINQ query Like this:
var results = from s in dbo.Statistic
groub s by dbo.getDomain(s.url) into g
where g.Count() > 1
orderby g.Count() ascending
select new
{
URL = dbo.getDomain(g.Key)
};

Using DISTINCT on a subquery to remove duplicates in Entity Framework

I have question about use of Distinct with Entity Framework, using Sql 2005. In this example:
practitioners = from p in context.Practitioners
join pn in context.ProviderNetworks on
p.ProviderId equals pn.ProviderId
(notNetworkIds.Contains(pn.Network))
select p;
practitioners = practitioners
.Distinct()
.OrderByDescending(p => p.UpdateDate);
data = practitioners.Skip(PageSize * (pageOffset ?? 0)).Take(PageSize).ToList();
It all works fine, but the use of distinct is very inefficient. Larger result sets incur unacceptable performance. The DISTINCT is killing me. The distinct is only needed because multiple networks can be queried, causing Providers records to be duplicated. In effect I need to ask the DB "only return providers ONCE even if they're in multiple networks". If I could place the DISTINCT on the ProviderNetworks, the query runs much faster.
How can I cause EF to add the DISTINCT only the subquery, not to the entire resultset?
The resulting simplified sql I DON'T want is:
select DISTINCT p.* from Providers
inner join Networks pn on p.ProviderId = pn.ProviderId
where NetworkName in ('abc','def')
IDEAL sql is:
select p.* from Providers
inner join (select DISTINCT ProviderId from Networks
where NetworkName in ('abc','def'))
as pn on p.ProviderId = pn.ProviderId
Thanks
Dave
I dont think you need a Distinct here but a Exists (or Any as it is called in Linq)
Try this:
var q = (from p in context.Practitioners
where context.ProviderNetworks.Any(pn => pn.ProviderId == p.ProviderId && notNetworkIds.Contains(pn.Network))
orderby p.UpdateDate descending
select p).Skip(PageSize * (pageOffset ?? 0)).Take(PageSize).ToList();

Categories