I have a list of objects in table tblB which is retrieved from db in following format.
public class playdata
{
public string consumerid { get; set; }
public string play_time { get; set; }
public string genre{ get; set; }
.
.
.
public int mycounter{ get; set; }
}
I have a table tblA which has a column colA which has int from 0 to 1000.
I wish to create a linq query similar to sql as below..
Select x.i as numindex, y.consumerid,y.play_time,y.genre
From
(Select colA as i from tblA) x
left join
(
Select consumerid, play_time,genre,mycounter from tblB
)y on y.mycounter > x.i
I tried the following unsuccessfully..
I came to find that Enumerable.Range(0, 1001) generated a numeric series so no need to get that table data..
List<playdata> plays = .....
var q= (from s in Enumerable.Range(0, 1001)
join p in plays on s < p.mycounter into t
from rt in t.DefaultIfEmpty() select new{
numindex=s,
consumerid=p.consumerid,
play_time =p.play_time,
genre=p.genre
}).ToList();
I see two errors in second line..
p is not in scope of left side of equals .. I have also tried..p.mycounter>s with same result.
The other error is in into where it shows.. expected contextual keyword equals
The errors go away if I change the second line to..
join p in plays on s equals p.mycounter into t
All help is sincerely appreciated.
Thanks
You have to use the alternative way of representing left outer join in LINQ:
var q =
(from i in Enumerable.Range(0, 1001)
from p in plays.Where(x => x.mycounter > i).DefaultIfEmpty()
select new
{
numindex = i,
consumerid = p?.consumerid,
play_time = p?.play_time,
genre = p?.genre
}).ToList();
Note that in LINQ to Objects you have to account for right side of the left outer join returning null when there is no matching element, otherwise you'll get NullReferenceException.
Related
We have two objects, Dates and ActiveEvents. Want to perform inner join on these with less than or equal to condition in linq. Same as ref of below SQL where consider #Tables are C# objects
Select A. from #Activities A
Inner Join #Dates D ON A.ActivityDate <= D.ProcessDate
Tried with below but it's not giving correct results.
var filteredActivity = (from e in ActiveEvents
from p in dates
where e.ActivityDate <= p.Date
select new ActiveEvent
{
ActivityDate = p.Date,
EventId = e.EventId
}).ToList();
And
var filteredActivity = (from e in ActiveEvents
from p in dates.Where(r => e.ActivityDate <= r)
select new ActiveEvent
{
ActivityDate = p.Date,
EventId = e.EventId
}).ToList();
Can you please suggest any better way to do this?
You can try this way
var filteredActivity = (from e in ActiveEvents
join p in dates
where e.ActivityDate <= p.ProcessDate
select new ActiveEvent
{
ActivityDate = p.Date,
EventId = e.EventId
}).ToList();
P/s: Ideally, between 2 tables should contain the foreign key to join like this join p in dates on e.Key equals p.ForeignKey
Based on your example, the query is filtering on ProcessDate but your linq query is filtering on p.Date. Are those the same field? The first example you gave should be correct.
I've been using Linq to SQL and have started using Entity Framework for a new project. I use LinqPad to test my code before incorporating it into Visual Studio. While debugging in VS, I noticed my counts differed. When I inspected the SQL created by my code in VS, I noticed that it didn't translate correctly.
My code in VS:
var adviceLineCallTotalViewModelList =
from a in db.AdviceLineCalls
.Include(a => a.Agency)
.Include(a => a.Staff)
.Include(a => a.StatusOfAdviceLineCaller)
.Include(a => a.AdviceLineCallSubjectMatter)
join ag in db.Agencies on a.AgencyNumber equals ag.AgencyNumber
join st in db.StatusOfAdviceLineCallers on a.StatusOfAdviceLineCallerID
equals st.StatusOfAdviceLineCallerID
join s in db.Staffs on a.StaffID equals s.StaffID
join sm in db.AdviceLineCallSubjectMatters on a.AdviceLineCallSubjectMatterID
equals sm.AdviceLineCallSubjectMatterID into grp
from sm in grp.DefaultIfEmpty()
where s.Employed == true
select new AdviceLineCallTotalViewModel()
{
AdviceLineCallID = a.AdviceLineCallID,
AdviceLineCallSubjectMatterID = sm.AdviceLineCallSubjectMatterID,
AdviceLineCallSubjectMatterDesc = sm.AdviceLineCallSubjectMatterDesc,
StatusOfAdviceLineCallerID = st.StatusOfAdviceLineCallerID,
StatusOfAdviceLineCallerDesc = st.StatusOfAdviceLineCallerDesc,
AgencyNumber = a.AgencyNumber,
AgencyNumberNameFacility = ag.AgencyNumberNameFacility,
CallDate = a.CallDate,
CallLength = a.CallLength,
Comments = a.Comments,
StaffID = a.StaffID,
LastName = s.LastName
};
When I debug and look at the SQL generated, I see:
SELECT
[Extent1].[AdviceLineCallID] AS [AdviceLineCallID],
[Extent5].[AdviceLineCallSubjectMatterID] AS [AdviceLineCallSubjectMatterID],
[Extent5].[AdviceLineCallSubjectMatterDesc] AS [AdviceLineCallSubjectMatterDesc],
[Extent3].[StatusOfAdviceLineCallerID] AS [StatusOfAdviceLineCallerID],
[Extent3].[StatusOfAdviceLineCallerDesc] AS [StatusOfAdviceLineCallerDesc],
[Extent1].[AgencyNumber] AS [AgencyNumber],
[Extent2].[AgencyNumberNameFacility] AS [AgencyNumberNameFacility],
[Extent1].[CallDate] AS [CallDate],
[Extent1].[CallLength] AS [CallLength],
[Extent1].[Comments] AS [Comments],
[Extent1].[StaffID] AS [StaffID],
[Extent4].[LastName] AS [LastName]
FROM [dbo].[AdviceLineCall] AS [Extent1]
INNER JOIN [dbo].[Agency] AS [Extent2] ON [Extent1].[AgencyNumber] = [Extent2].[AgencyNumber]
INNER JOIN [dbo].[StatusOfAdviceLineCaller] AS [Extent3] ON [Extent1].[StatusOfAdviceLineCallerID] = [Extent3].[StatusOfAdviceLineCallerID]
INNER JOIN [dbo].[Staff] AS [Extent4] ON [Extent1].[StaffID] = [Extent4].[StaffID]
INNER JOIN [dbo].[AdviceLineCallSubjectMatter] AS [Extent5] ON [Extent1].[AdviceLineCallSubjectMatterID] = [Extent5].[AdviceLineCallSubjectMatterID]
WHERE 1 = [Extent4].[Employed]
The last "INNER JOIN" should be a "LEFT OUTER JOIN" because of the lines:
join sm in db.AdviceLineCallSubjectMatters on a.AdviceLineCallSubjectMatterID equals sm.AdviceLineCallSubjectMatterID into grp
from sm in grp.DefaultIfEmpty()
Right???
NOTE: I included the "Include" statements after reading another post about why the "LEFT OUTER JOIN" is not being included. I get the same results with or without the "Includes."
I've used DefaultIfEmpty() in other, simpler queries before and have not run into this problem.
As an EF newbie, I'm not sure if I'm doing something wrong or if EF in my project got corrupted some how. I'm using EF 6.2.
EDIT:
I created a new Visual Studio Project and used the following code:
var adviceLineCallTotalViewModelList = from a in db.AdviceLineCalls
join ag in db.Agencies on a.AgencyNumber equals ag.AgencyNumber
join st in db.StatusOfAdviceLineCallers on a.StatusOfAdviceLineCallerID equals st.StatusOfAdviceLineCallerID
join s in db.Staffs on a.StaffID equals s.StaffID
join sm in db.AdviceLineCallSubjectMatters on a.AdviceLineCallSubjectMatterID equals sm.AdviceLineCallSubjectMatterID into grp
from sm_left in grp.DefaultIfEmpty()
where s.Employed == true
select new AdviceLineCallTotalViewModel()
{
AdviceLineCallID = a.AdviceLineCallID,
AdviceLineCallSubjectMatterID = sm_left == null ? 0 : sm_left.AdviceLineCallSubjectMatterID,
AdviceLineCallSubjectMatterDesc = sm_left == null ? String.Empty : sm_left.AdviceLineCallSubjectMatterDesc,
StatusOfAdviceLineCallerID = st.StatusOfAdviceLineCallerID,
StatusOfAdviceLineCallerDesc = st.StatusOfAdviceLineCallerDesc,
AgencyNumber = a.AgencyNumber,
AgencyNumberNameFacility = ag.AgencyNumberNameFacility,
CallDate = a.CallDate,
CallLength = a.CallLength,
Comments = a.Comments,
StaffID = a.StaffID,
LastName = s.LastName
};
This retrieves the correct number of rows (5104). It also correctly creates the last join as a LEFT OUTER JOIN:
LEFT OUTER JOIN [dbo].[AdviceLineCallSubjectMatter] AS [Extent5] ON [Extent1].[AdviceLineCallSubjectMatterID] = [Extent5].[AdviceLineCallSubjectMatterID]
However, this same line of code in my current project only returns 5 records, and the last join is incorrectly translated into an INNER JOIN.
Does this mean EF or something got corrupted in my current project? As a newbie to MVC and EF, I'm not sure what to do.
Change
join sm in db.AdviceLineCallSubjectMatters ...
from sm in grp.DefaultIfEmpty() ...
....
select
AdviceLineCallSubjectMatterID = sm.AdviceLineCallSubjectMatterID
into
join sm in db.AdviceLineCallSubjectMatters ...
from sm_left in grp.DefaultIfEmpty() ...
....
select
AdviceLineCallSubjectMatterID = sm_left == null? 0 : sm_left.AdviceLineCallSubjectMatterID
Depending on the .net framework version you could alter the select null checking to a more clean way (check #Jacob Proffitt comment)
If your AdviceLineCall entity already has references declared for the related entities (I.e. a.AdviceLineCallSubjectMatter) then you do not need to declare DbSets in your context and manually join these tables in a query. EF handles all of this for you via the mapped references.
var adviceLineCallTotalViewModelList = db.AdviceLineCalls
.Where( a=> a.Staff.Employed)
.Select(a => new {
a.AdviceLineCallId,
a.SubjectMatter.AdviceLineCallSubjectMatterID,
a.SubjectMatter.AdviceLineCallSubjectMatterDesc,
a.StatusOfAdviceLineCaller.StatusOfAdviceLineCallerID,
a.StatusOfAdviceLineCaller.StatusOfAdviceLineCallerDesc,
a.Agency.AgencyNumber,
a.Agency.AgencyNumberNameFacility,
a.CallDate,
a.CallLength,
a.Comments,
a.Staff.StaffID,
a.Staff.LastName})
.ToList() // I'd recommend using .Take() to limit the # of rows returned.
.Select(x => new AdviceLineCallTotalViewModel()
{
AdviceLineCallID = x.AdviceLineCallID,
AdviceLineCallSubjectMatterID = x.AdviceLineCallSubjectMatterID,
AdviceLineCallSubjectMatterDesc = x.AdviceLineCallSubjectMatterDesc,
StatusOfAdviceLineCallerID = x.StatusOfAdviceLineCallerID,
StatusOfAdviceLineCallerDesc = x.StatusOfAdviceLineCallerDesc,
AgencyNumber = x.AgencyNumber,
AgencyNumberNameFacility = x.AgencyNumberNameFacility,
CallDate = x.CallDate,
CallLength = x.CallLength,
Comments = x.Comments,
StaffID = x.StaffID,
LastName = x.LastName
});
The first .Select() extracts just the fields you want from the object model. Provided your entities are mapped as Optional (null-able FK) then it will compose the suitable joins automatically. In cases where something like the SubjectMatter is #null, those two fields requested from that referenced entity will be null/default. When you .Select() that data into your ViewModel you are dealing with POCO objects post the .ToList() or .Take() so you should handle any logic around what to do with missing optional dependencies here. If you want to filter data out, do it in the Where() clause to ensure only relevant data is returned from SQL.
I found the cause of my problem and why the same C# code was being translated into SQL differently, and it all had to do with the Required Data Annotation.
The table, AdviceLineCallSubjectMatter is a new addition to the DB. So only new AdviceLineCall records will have a AdviceLineCallSubjectMatterID so I made it a nullable int.
There are certain AdviceLineCall fields that are "Required," and the new int? AdviceLineCallSubjectMatterID was added to my AdviceLineCall Model class with the Required data annotation.
public partial class AdviceLineCall
{
.
.
.
[Required(ErrorMessage = "Subject Matter is required")]
[DisplayName("Subject Matter")]
public int? AdviceLineCallSubjectMatterID { get; set; }
.
.
.
}
public partial class AdviceLineCallSubjectMatter
{
public AdviceLineCallSubjectMatter()
{
AdviceLineCalls = new HashSet<AdviceLineCall>();
}
[DisplayName("Subject Matter")]
public int AdviceLineCallSubjectMatterID { get; set; }
[StringLength(3)]
[DisplayName("Subject Matter")]
public string AdviceLineCallSubjectMatterDesc { get; set; }
public virtual ICollection<AdviceLineCall> AdviceLineCalls { get; set; }
}
}
When I comment out the Required data annotation in the AdviceLineCall Model class, my C# is translated to the expected SQL with a LEFT OUTER JOIN on AdviceLineCallSubjectMatter.
I am not sure why the Required data annotation has this effect???
NOTE: In the temporary project I created to test the same query, I created the DB Context and Model classes via Code First From DB and didn't add the Required data annotations.
I have several tables, the main one is called DefectRecord, others are called DefectArea, DefectLevel...etc and the one called DefectAttachment. And this problem is about joining DefectRecord with other tables to get a ViewModel for further use. What the hard part I am facing is about the DefectAttachment table.
DefectRecord has a 1-to-many relation with DefectAttachment. While there may be NO attachment at all for one defect record, there may be multiple attachments.
Logically I tried to perform a left join among DefectRecord & DefectAttachment, but there is one more requiredment:
If there is multiple attachments, select ONLY the oldest one(i.e. the
one with oldest CreatedDate field value)
I am stuck at this requirement, how can I perform this with LINQ-to-Entities? Below is the code of what I have now:
var ret = (from dr in defectRecordQuery
join ft in filterQuery on dr.FilterID equals ft.FilterID
join l in levelQuery on dr.LevelID equals l.LevelID
join a in attachmentQuery on dr.DefectRecordID equals a.DefectRecordID into drd
from g in drd.DefaultIfEmpty()
select new DefectRecordViewModel
{
DefectRecordCode = dr.Code,
DefectAttachmentContent = g == null ? null : g.FileContent,
LookupFilterName = ft.FilterName,
}).ToList();
The *Query variable are the IQueryable object which get the full list of corresponding table.
Group your results by the Code and FilterName and then for the content take that of the item in the group that has the oldest date
var ret = (from dr in defectRecordQuery
join ft in filterQuery on dr.FilterID equals ft.FilterID
join l in levelQuery on dr.LevelID equals l.LevelID
join d in attachmentQuery on dr.DefectRecordID equals d.DefectRecordID into drd
from g in drd.DefaultIfEmpty()
group g by new { dr.Code, ft.FilterName } into gg
select new DefectRecordViewModel
{
DefectRecordCode = gg.Key.Code,
DefectAttachmentContent = gg.OrderByDescending(x => x.CreateDateTime).FirstOrDefault() == null? null: gg.OrderByDescending(x => x.CreateDateTime).FirstOrDefault().FileContent,
LookupFilterName = gg.Key.FilterName,
}).ToList();
If using C# 6.0 or higher then you can do:
DefectAttachmentContent = gg.OrderByDescending(x => x.CreateDateTime)
.FirstOrDefault()?.FileContent,
What i aim to achive with this is to have a grid or listview where the colums get data from different sql tabels.
Here are the different tabels
TradeItemIdentification
id GTIN
TradeItemDescriptionInformation
id brandName tradeItemFunctionalName
ClassificationCategory
id additionalClassificationCategoryCode
I don know if it is possible to show a grid with all of these attributes except the id's. What is the smartest way to do achive this, if possible?
They are all linked to this table. With these "many ot many" tabels.
TradeItemBasic
id
TradeItemIdentificationOnTradeItem
tradeItemId identificationId
TradeItemDescriptionInformationsOnTradeItem
tradeItemId descriptionId
And the same for the last table. So they have a connection.
Here is some joins of the tabels that i have made so far.
public List<string> GetAllProductsInfo()
{
var gtins = (from gtinss in _db.TradeItemIdentificationOnTradeItems
join gtin in _db.TradeItemIdentifications on gtinss.tradeItemIdentificationId equals gtin.id
select gtin.gtin);
var brandNames = (from descriptions in _db.TradeItemDescriptionInformationsOnTradeItems
join description in _db.TradeItemDescriptionInformations on descriptions.tradeItemDescriptionInformationId equals description.id
select description.brandName);
var article = (from articleNumbers in _db.ClassificationCategoryOnGDSNTradeItemClassifications
join articleNumber in _db.ClassificationCategories on articleNumbers.gDSNTradeItemClassificationId equals articleNumber.id
select articleNumber.additionalClassificationCategoryCode);
var allInfo = gtins.Concat(brandNames).Concat(article).ToList();
return allInfo;
}
This is what ive got so far, i am no able to get all of the items that i wanted. But when i do it like this the results is not divided in to sections, but instead it is all just gets put out as one long list.
Just join the different tables and select the properties you want to display.
There are two different use cases. If you want to display all lines where some data is missing then use left joins. Else use inner joins (just replace the left join). That reduce your resultset.
Here is an example how to join your tables:
SELECT tib.id, tii.GTIN, tidi.brandName, tidi.tradeItemFunctionalName
FROM TradeItemBasic AS tib
LEFT JOIN TradeItemIdentificationOnTradeItem AS tii2ti ON tib.id = tii2ti.tradeItemId
LEFT JOIN TradeItemIdentification AS tii ON tii.id = tii2ti.identificationId
LEFT JOIN TradeItemDescriptionInformationsOnTradeItem AS tidi2ti ON tib.id = tidi2ti.tradeItemId
LEFT JOIN TradeItemDescriptionInformation AS tidi ON tidi.id = tidi2ti.descriptionId
For the ClassificationCategory you don't post a mapping table, so i remove the selected property.
Hope that helps.
I solved it.
I Made a new C# class. Called ProductInfo that looks like this
public class ProductInfo
{
public ProductInfo()
{
}
public List<string> Gtin { get; set; }
public List<string> BrandName { get; set; }
public List<string> ArticleNr { get; set; }
}
this class takes three types of lists.
And here is the method for getting the specific information from the different classes.
public ProductInfo GetAllProductsInfo()
{
var gtins = (from gtinss in _db.TradeItemIdentificationOnTradeItems
join gtin in _db.TradeItemIdentifications on gtinss.tradeItemIdentificationId equals gtin.id
select gtin.gtin).ToList();
var brandNames = (from descriptions in _db.TradeItemDescriptionInformationsOnTradeItems
join description in _db.TradeItemDescriptionInformations on descriptions.tradeItemDescriptionInformationId equals description.id
select description.brandName).ToList();
var article = (from articleNumbers in _db.ClassificationCategoryOnGDSNTradeItemClassifications
join articleNumber in _db.ClassificationCategories on articleNumbers.gDSNTradeItemClassificationId equals articleNumber.id
select articleNumber.additionalClassificationCategoryCode).ToList();
ProductInfo pr = new ProductInfo { Gtin = gtins, BrandName = brandNames, ArticleNr = article };
return pr;
}
with this method i get three lists from the tabels that i wanted. And after i made the joins i just create a new ProductInfo and add the results to the right lists in the new class.
I have the following working TSQL query in ms SQL 2008
SELECT
Date,
COUNT(click) AS clicks,
COUNT(sale) AS sales,
count(lead) as leads
FROM
(
SELECT ClickDate as date ,ID AS click ,CAST(NULL AS int) AS sale , CAST(null as int) as lead
FROM clicks
UNION ALL
SELECT Date,null, ID ,NULL
FROM sales
UNION ALL
SELECT Date,null, NULL ,ID
FROM leads
) t
GROUP BY Date
How would i convert this to LINQ to SQL?
I wrote this LINQ but it doesn't work.
public class mydata
{
public DateTime date { get; set; }
public int? click { get; set; }
public int? sale { get; set; }
public int? lead { get; set; }
}
var clicks = from c in Clicks
select new mydata
{
date = c.ClickDate, click = c.ID, sale = null, lead = null
};
var sales = from s in Sales
select new mydata
{
date = s.Date, click = null, sale = s.ID, lead = null
};
var leads = from l in Leads
select new mydata
{
date = l.Date, click = null, sale = null, lead = l.ID
};
var v = clicks.Concat(sales).Concat(leads);
var res = from x in v
group x by x.date into xg
select new
{
date = xg.Key, clicks = xg.Count(z => z.click != null)
};
}
How do i correct this LINQ query?
Update:
i Modified the LINQ query based on David B recommendation.
i'm still getting the following error:
"All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists."
David's correct about the first two issues. For your final problem (3), Count() does not work like it does in SQL. It expects a predicate that returns a bool. Your using it with ints (i.e. z.click, z.sales, etc.)
The problem is that the anonymous types in the projections are not identical... ID is int in one and Nullable<int> in another.
Instead of using an anonymous type in your projections, use this:
public class ConcatTarget
{
public DateTime TheDate {get;set;}
public int? ID {get;set;}
public string sale {get;set;}
public string lead {get;set;}
}
Even though no instances are actually constructed, LinqToSql uses the class's shape to translate the query.
As for Count, perhaps you meant .Count(x => x.Prop != null) ?
Ok, apparently you've hit upon a buggy translation behavior as described here.
What's happening is the sql translator sees the null assignments, and throws them away. This causes an incorrect number of sql columns to be selected between the sets.
Here's a possible workaround:
int? myNull = null;
var clicks =
from c in Clicks
select new mydata
{
date = c.ClickDate,
click = c.ID,
sale = c.ID + myNull,
lead = myNull + c.ID //note - expressions must be unique
};
The basic idea is to create unique expressions the query translator can't throw away. This is harder than it sounds (above is my ninth attempt).
Here's the other two tables:
var sales = from s in Sales
select new mydata
{
date = s.Date,
click = s.ID + myNull,
sale = s.ID,
lead = myNull + s.ID
};
var leads = from l in Leads
select new mydata
{
date = l.Date,
click = l.ID + myNull,
sale = myNull + l.ID,
lead = l.ID
};
If you had more than 2 columns-to-be-nulled, you could resort to subtraction, division, multiplication, etc.