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.
Related
I've got this horrible EF code in a code base and I am scratching my head trying to figure out what it's trying to do. If I were looking to put this in a stored procedure instead how would this query look in SQL?
public void LoadNotifyListItems(UserProfileModel user, DbContext pc)
{
var allowedEvents = (from r in user.Roles
join near in pc.NotifyEventAllowedRoles on r.RoleId equals near.RoleId
join ne in pc.NotifyEvents on near.NotifyEventId equals ne.Id
select ne).Distinct();
var NotifyListItems = from ne in allowedEvents
join pune in pc.UserNotifyEvents
on new { NotifyEventId = ne.Id, UserId = user.Id }
equals new { pune.NotifyEventId, pune.UserId }
into loj
from pune in loj.DefaultIfEmpty()
select new NotifyListItem
{
Event = ne,
Value = pune ?? new UserNotifyEvent
{
NotifyEventId = ne.Id
}
};
}
The issue I am having is the entirety of pc.UserNotifyEvents table is being queried. Monitoring the DB, EF is making this query when the LoadNotifyListItems method is being run:
[Extent1].[UserId] AS [UserId],
[Extent1].[NotifyEventId] AS [NotifyEventId],
[Extent1].[NotifyPrimaryEmail] AS [NotifyPrimaryEmail],
[Extent1].[NotifyAlternateEmail] AS [NotifyAlternateEmail],
[Extent1].[NotifySmsNumber] AS [NotifySmsNumber],
[Extent1].[Threshold1] AS [Threshold1],
[Extent1].[Threshold2] AS [Threshold2]
FROM [UserNotifyEvents] AS [Extent1]
This isn't needed and its ingesting 200,000 rows everytime. I am thinking of moving the query to a Stored Proc and pass in userId as a parameter instead
Please let me know if there is not enough information to go off here.
The first part of the query is joining a list of roles in memory (user.Roles) with an IQueryable from the DbContext. This is probably the cause of your issue. You should use the pc.Roles property instead, and introduce a where clause to filter by the id of the provided user.
If user info is in same database as other tables
Pass the user id to the stored proc. Inside the sproc, join user-roles to the other tables WHERE userid = passed-in parameter.
If user info is NOT in same database as other tables
First, get a list of role ids for the user.
var roleIds = user.Roles.Select(i=>i.RoleId).Distinct().ToList();
Then you're going to pass that list of role IDs to your stored proc. Try something like this Stack Overflow article, which tells you how to pass those role IDs as distinct strings to a stored proc.
The rest of this appears to be regular table joins within the database. The most critical part is getting those roles IDs for the user.
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 am using Linq to SQL in Linqpad to get to some data.
I have 3 tables I need to use to do the following steps:
1) Select customers by postcode (Customer table)
2) Get all transaction ID's for these customers (Transaction table)
3) Get itemized items for all transaxction ID's (Itemized table)
So i start out easy enough and grab the customers:
string pc = "123";
var cust =
from c in Customers
where c.PostCode.StartsWith(pc) == true
select c;
Now I need to create a new Linq object that has the lookup from transaction table based on the "CustomerID" field but I am not sure how to do this. Ive experimented with some foreach loops but cant get syntax right. Did some googling and saw posts saying not to use foreach loops with linq objects as you should use inbuilt Linq functionality but I couldnt find any examples doing what I needed.
I apologise for such a basic question but I have just started using Linq.
How do I create the next Linq object with all transaction records based on the CustoomerID field?
You can use single query with joins. If you have navigation properties in your entities:
from c in Customers
from t in c.Transactions
from i in t.ItemizedItems
where c.PostCode.StartsWith(pc)
select i
Labda syntax:
Customers.Where(c => c.PostCode.StartsWith(pc))
.SelectMany(c => c.Transactions)
.SelectMany(t => t.ItemizedItems);
If you don't have navigation properties:
from c in Customers
join t in Transactions on c.ID equals t.CustomerID
join i in t.ItemizedItems on t.ID equals i.TransactionID
where c.PostCode.StartsWith(pc)
select i
Below is the SQL Query I am trying to translate
SELECT dbo.Contracts.Supplier
FROM dbo.Contracts INNER JOIN dbo.Products ON dbo.Contracts.Product = dbo.Products.Product
where dbo.Products.ProductGroup='Crude'
GROUP BY dbo.Contracts.Supplier
Am I doing something wrong because I do not get same results with the following LINQ
var result = from c in context.Contracts
join p in context.Products on c.Product equals p.Product1
where p.Product1.Equals("Crude")
group c by c.Supplier into g
select new { supplier = g.Key };
It is generating a weird statement
SELECT
1 AS [C1],
[Distinct1].[Supplier] AS [Supplier]
FROM ( SELECT DISTINCT
[Extent1].[Supplier] AS [Supplier]
FROM [dbo].[Contracts] AS [Extent1]
WHERE N'Crude' = [Extent1].[Product]
) AS [Distinct1]
Using distinct would work but to get same results, LINQ should be generating a statement like so (it's like it is ignoring the join):
SELECT distinct dbo.Contracts.Supplier
FROM dbo.Contracts INNER JOIN dbo.Products ON dbo.Contracts.Product = dbo.Products.Product
where dbo.Products.ProductGroup='Crude'
I'm assuming that you are using 'EntityFramework' or 'Linq To SQL'. If so, you should be able to use navigation properties to navigate to product and filter invalit results out. This way your query might look something like this:
var result = (from c in context.Contracts
where c.Products.Any(p => p.ProductGroup == "Crude")
select c.Supplier).Distinct();
It will automatically convert into correct query (in this case possibly without join even, just using Exists sql keyword) and return distinct suppliers. This is if I understand your objective correctly - you want to obtain all suppliers assigned to contracts that contain product from 'Crude' product group.
Basically you should try to avoid using joins from linq to sql or linq to entities as much as possible when you can use navigation properties. System will probably be better at converting them into specific sql.
I have the following LINQ query, that is returning the results that I expect, but it does not "feel" right.
Basically it is a left join. I need ALL records from the UserProfile table.
Then the LastWinnerDate is a single record from the winner table (possible multiple records) indicating the DateTime the last record was entered in that table for the user.
WinnerCount is the number of records for the user in the winner table (possible multiple records).
Video1 is basically a bool indicating there is, or is not a record for the user in the winner table matching on a third table Objective (should be 1 or 0 rows).
Quiz1 is same as Video 1 matching another record from Objective Table (should be 1 or 0 rows).
Video and Quiz is repeated 12 times because it is for a report to be displayed to a user listing all user records and indicate if they have met the objectives.
var objectiveIds = new List<int>();
objectiveIds.AddRange(GetObjectiveIds(objectiveName, false));
var q =
from up in MetaData.UserProfile
select new RankingDTO
{
UserId = up.UserID,
FirstName = up.FirstName,
LastName = up.LastName,
LastWinnerDate = (
from winner in MetaData.Winner
where objectiveIds.Contains(winner.ObjectiveID)
where winner.Active
where winner.UserID == up.UserID
orderby winner.CreatedOn descending
select winner.CreatedOn).First(),
WinnerCount = (
from winner in MetaData.Winner
where objectiveIds.Contains(winner.ObjectiveID)
where winner.Active
where winner.UserID == up.UserID
orderby winner.CreatedOn descending
select winner).Count(),
Video1 = (
from winner in MetaData.Winner
join o in MetaData.Objective on winner.ObjectiveID equals o.ObjectiveID
where o.ObjectiveNm == Constants.Promotions.SecVideo1
where winner.Active
where winner.UserID == up.UserID
select winner).Count(),
Quiz1 = (
from winner2 in MetaData.Winner
join o2 in MetaData.Objective on winner2.ObjectiveID equals o2.ObjectiveID
where o2.ObjectiveNm == Constants.Promotions.SecQuiz1
where winner2.Active
where winner2.UserID == up.UserID
select winner2).Count(),
};
You're repeating join winners table part several times. In order to avoid it you can break it into several consequent Selects. So instead of having one huge select, you can make two selects with lesser code. In your example I would first of all select winner2 variable before selecting other result properties:
var q1 =
from up in MetaData.UserProfile
select new {up,
winners = from winner in MetaData.Winner
where winner.Active
where winner.UserID == up.UserID
select winner};
var q = from upWinnerPair in q1
select new RankingDTO
{
UserId = upWinnerPair.up.UserID,
FirstName = upWinnerPair.up.FirstName,
LastName = upWinnerPair.up.LastName,
LastWinnerDate = /* Here you will have more simple and less repeatable code
using winners collection from "upWinnerPair.winners"*/
The query itself is pretty simple: just a main outer query and a series of subselects to retrieve actual column data. While it's not the most efficient means of querying the data you're after (joins and using windowing functions will likely get you better performance), it's the only real way to represent that query using either the query or expression syntax (windowing functions in SQL have no mapping in LINQ or the LINQ-supporting extension methods).
Note that you aren't doing any actual outer joins (left or right) in your code; you're creating subqueries to retrieve the column data. It might be worth looking at the actual SQL being generated by your query. You don't specify which ORM you're using (which would determine how to examine it client-side) or which database you're using (which would determine how to examine it server-side).
If you're using the ADO.NET Entity Framework, you can cast your query to an ObjectQuery and call ToTraceString().
If you're using SQL Server, you can use SQL Server Profiler (assuming you have access to it) to view the SQL being executed, or you can run a trace manually to do the same thing.
To perform an outer join in LINQ query syntax, do this:
Assuming we have two sources alpha and beta, each having a common Id property, you can select from alpha and perform a left join on beta in this way:
from a in alpha
join btemp in beta on a.Id equals btemp.Id into bleft
from b in bleft.DefaultIfEmpty()
select new { IdA = a.Id, IdB = b.Id }
Admittedly, the syntax is a little oblique. Nonetheless, it works and will be translated into something like this in SQL:
select
a.Id as IdA,
b.Id as Idb
from alpha a
left join beta b on a.Id = b.Id
It looks fine to me, though I could see why the multiple sub-queries could trigger inefficiency worries in the eyes of a coder.
Take a look at what SQL is produced though (I'm guessing you're running this against a database source from your saying "table" above), before you start worrying about that. The query providers can be pretty good at producing nice efficient SQL that in turn produces a good underlying database query, and if that's happening, then happy days (it will also give you another view on being sure of the correctness).