Left join times out on linq where right table is empty - linq

I have a left join to perform . And take the entire objects from it . However it times out when the item collection is empty on doing .Select (x=>x.object) ;
public Tuple<td_GroupLicense, List<td_UserGroupMap>> ReturnActiveLicenseInfo(int GroupID)
{
Tuple<td_GroupLicense, List<td_UserGroupMap>> Tuple;
#endregion
var GroupUserLicense = from grp in Context.td_Groups
from lic in Context.td_GroupLicenses
.Where(x => grp.IdGroup == x.GroupID && x.GroupID == GroupID)
from ugm in Context.td_UserGroupMaps
.Where(x => grp.IdGroup == x.GroupID && x.GroupID == GroupID)
.DefaultIfEmpty()
select new { GL = lic, UGM = ugm };
td_GroupLicense License = GroupUserLicense.Select(x => x.GL).Distinct().SingleOrDefault();
List<td_UserGroupMap> UserMaps = GroupUserLicense.Select(x => x.UGM)
.OfType<td_UserGroupMap>()
.DefaultIfEmpty().ToList();
Tuple = new Tuple<td_GroupLicense, List<td_UserGroupMap>>(License, UserMaps);
return Tuple;
}
when I do the join then the line in
List<td_UserGroupMap> UserMaps = GroupUserLicense.Select(x => x.UGM)
.OfType<td_UserGroupMap>()
.DefaultIfEmpty().ToList();
times out since the collection UGM in GroupUserLicense is null . It cannot do the operation . I want to put the left join result of License and Usegroupmaps into the corresponding objects . But the Right table is null so the timeout occurs . How to overcome that ? And why is it happening ?

Related

converting a sql server view to linq

I'm not sure if this is the right forum, if not please move it
My office is in the process of re-writing our app and switching from VB to C# and the entity framework. I've had some success in joining multiple tables all with left outer joins but I'm at my limits on how to convert an sql view like the following:
SELECT dbo.Addresses.AddressId, dbo.AddressTypes.AddressType, dbo.Addresses.AddressTypeId, dbo.Addresses.ParentAddressId, dbo.Addresses.AddressCode, dbo.Addresses.AddressNumber, dbo.Addresses.Address,
dbo.Addresses.SubAddress, dbo.Addresses.Direction, dbo.Addresses.City, dbo.Addresses.StateId, dbo.Addresses.CountryId, ISNULL(dbo.Addresses.AddressNumber + ' ', '') + ISNULL(dbo.Addresses.Direction + ' ', '')
+ ISNULL(dbo.Addresses.Address + ' ', '') + ISNULL(dbo.Addresses.Suffix + ' ', '') + ISNULL(dbo.Addresses.SubAddress + ' ', '') AS FullAddress, dbo.Addresses.RegionId, dbo.Addresses.CountyId,
dbo.Addresses.OccupancyTypeId, dbo.Addresses.PropertyUseTypeId, dbo.Addresses.Comment, dbo.States.StateAbbr, dbo.States.State, dbo.Regions.Region, dbo.Regions.RegionCode, dbo.Counties.County,
dbo.Counties.CountyCode, dbo.Countries.Country, dbo.OccupancyTypes.OccupancyType, dbo.OccupancyTypes.OccupancyTypeCode, dbo.PropertyUseTypes.PropertyUseType, dbo.Party.PartyName,
dbo.PropertyUseTypes.PropertyUseTypeCode, dbo.UserDefFields.UserDefFieldId, dbo.UserDefFields.FieldDesc, dbo.UserDefValues.UserDefValueId, dbo.UserDefValues.UserDefValue, dbo.Addresses.ZipId, dbo.Zips.Zip,
dbo.AddressParties.PartyID, dbo.Addresses.Latitude, dbo.Addresses.Longitude, dbo.Addresses.Inactive, dbo.Addresses.DefaultPass, dbo.Addresses.Suffix, dbo.AddressTypes.AgencyId, dbo.Addresses.LegalDesc,
dbo.AddressParties.Inactive AS PAInactive, dbo.AddressParties.RoleTypeId, dbo.Addresses.POBox, dbo.AddressParties.ExternalValue, dbo.Addresses.DateUpdated, dbo.Addresses.DateInserted, dbo.Addresses.ReportId,
dbo.Addresses.Map, dbo.Addresses.Block, dbo.Addresses.Lot, dbo.Addresses.TaxParcel, dbo.Addresses.ExternalId, dbo.Addresses.Schedule
FROM dbo.UserDefFields RIGHT OUTER JOIN
dbo.UserDefValues ON dbo.UserDefFields.UserDefFieldId = dbo.UserDefValues.UserDefFieldId RIGHT OUTER JOIN
dbo.Party RIGHT OUTER JOIN
dbo.Addresses LEFT OUTER JOIN
dbo.Zips ON dbo.Addresses.ZipId = dbo.Zips.ZipId LEFT OUTER JOIN
dbo.AddressTypes ON dbo.Addresses.AddressTypeId = dbo.AddressTypes.AddressTypeId LEFT OUTER JOIN
dbo.States ON dbo.Addresses.StateId = dbo.States.StateId LEFT OUTER JOIN
dbo.Regions ON dbo.Addresses.RegionId = dbo.Regions.RegionId LEFT OUTER JOIN
dbo.Counties ON dbo.Addresses.CountyId = dbo.Counties.CountyId LEFT OUTER JOIN
dbo.Countries ON dbo.Addresses.CountryId = dbo.Countries.CountryId LEFT OUTER JOIN
dbo.OccupancyTypes ON dbo.Addresses.OccupancyTypeId = dbo.OccupancyTypes.OccupancyTypeId LEFT OUTER JOIN
dbo.PropertyUseTypes ON dbo.Addresses.PropertyUseTypeId = dbo.PropertyUseTypes.PropertyUseTypeId LEFT OUTER JOIN
dbo.AddressParties ON dbo.Addresses.AddressId = dbo.AddressParties.AddressID ON dbo.Party.PartyID = dbo.AddressParties.PartyID ON dbo.UserDefValues.RecordId = dbo.Addresses.AddressId
Into something like the following:
public List<AddressPartyList> GetAddressPartyList(Guid? AddressId, Guid? RoleTypeId = null, bool ShowInactive = false, bool FromWebOnly = false, bool WebAcceptedOnly = false, bool WebRejectedOnly = false)
{
IQueryable<AddressPartyList> thisList;
thisList = myappContext.myappData().AddressParties.Join(
myappContext.myappData().RoleTypes, ap => ap.RoleTypeId, rt => rt.RoleTypeId, (ap, rt) => new { ap, rt }).Join(
myappContext.myappData().Parties, apr => apr.ap.PartyId, p => p.PartyId, (apr, p) => new AddressPartyList
{
AddressId = apr.ap.AddressId,
PartyId = apr.ap.PartyId,
AccountId = p.AccountId,
RoleType = apr.rt.RoleType1,
Salutation = p.Salutation,
FirstName = p.FirstName,
MiddleInitial = p.MiddleInitial,
LastName = p.LastName,
Suffix = p.Suffix,
PartyName = p.PartyName,
Email = p.Email,
Comment = p.Comment,
ExternalId = p.ExternalId,
PartyInactive = p.Inactive,
WebAccountId = p.WebAccountId,
DateUpdated = p.DateUpdated,
DateInserted = p.DateInserted,
PriceLevel = p.PriceLevel,
FromWeb = p.FromWeb,
WebAccepted = p.WebAccepted,
WebRejected = p.WebRejected,
RoleTypeId = apr.ap.RoleTypeId,
InactiveAtAddress = apr.ap.Inactive,
IsBus = p.IsBus,
Sequence = apr.ap.Sequence,
AddressPartyId = apr.ap.AddressPartyId
}).Where(p => p.AddressId == AddressId);
if (!ShowInactive)
{
thisList = thisList.Where(p => p.PartyInactive == false && p.InactiveAtAddress == false);
}
if (RoleTypeId != null)
{
thisList = thisList.Where(p => p.RoleTypeId == RoleTypeId);
}
if (FromWebOnly)
{
thisList = thisList.Where(p => p.FromWeb == true);
}
if (WebAcceptedOnly)
{
thisList = thisList.Where(p => p.WebAccepted == true);
}
if (WebRejectedOnly)
{
thisList = thisList.Where(p => p.WebRejected == true);
}
return thisList.GroupBy(p => p.PartyId).Select(p => p.FirstOrDefault()).OrderBy(p => p.Sequence).ThenBy(p => p.PartyName).ToList();
}
Any help would be appreciated. Also, what would be the syntax for a view with multiple right and left outer joins. I didn't code it, I'm just trying to convert it and I understand it's best to use left outer joins.
Thanks.

Unable to cast object of type 'System.Collections.Generic.HashSet`1[libraryWebProject.Major]' to type 'libraryWebProject.Major'

I have this code in the code behind file
LibraryArticlesEntities la = new LibraryArticlesEntities();
int id = 17;
if (Request.QueryString["TitleID"] != null)
{
id = Int32.Parse(Request.QueryString["TitleID"]);
}
var gettitle = la.Titles.Where(t => t.ID == id).Select(t => t.Title1);
header.InnerHtml += gettitle;
var sub = la.Titles.Where(t => t.ID == id).Select(t => t.Majors);
foreach (Major major in sub) // the error is here
{
subject.InnerHtml += major.MajorName + " ";
}
Here I'm using a LINQ query to fetch a list of majors but I get this error when I try to iterate over it and display their names:
Unable to cast object of type 'System.Collections.Generic.HashSet`1[libraryWebProject.Major]' to type 'libraryWebProject.Major'.
The relationship between Title and Major is many to many and I have an association table linking Title ID and Major ID
Please try to make sub a List<Major> by adding .ToList(); at the end of your select.
var sub = la.Titles.Where(t => t.ID > 0)
.SelectMany(a => a.Majors.Select(b=>b)).ToList();
This line:
var sub = la.Titles.Where(t => t.ID == id).Select(t => t.Majors);
and the fact that "The relationship between Title and Major is many to many"
implies that the result of the Select is a collection of collections, so your loop will have to be:
foreach (var listOfMajors in sub)
{
foreach (var major in listOfMajors)
{
// Do stuff
}
}
Old answer replaced after it was revealed that the question didn't actually include the code that was in error.
Please try with ToList() and use var in foreach:
var sub = la.Titles.Where(t => t.ID == id)
.Include(t => t.Majors)
.Select(t => t.Majors).ToList();//use tolist() here
foreach (var major in sub) // and add var here please
{
subject.InnerHtml += major.MajorName + " ";
}

linq query crossjoin groupby optimize

i have the following database-model: http://i.stack.imgur.com/gRtMD.png
the many to many relations for Kunde_Geraet/Kunde_Anwendung are in explicit Mapping-Table with additional Information.
i want to optimize the following LINQ-query:
var qkga = (from es in db.Eintrag_Systeme.Where(es => es.Eintrag_ID == id)
from kg in db.Kunde_Geraet.Where(kg => es.Geraet_ID == kg.Geraet_ID)
select new { Kunde = kg.Kunde, Geraet = es.Geraet, Anwendung = es.Anwendung })
.Union(
from es in db.Eintrag_Systeme.Where(es => es.Eintrag_ID == id)
from ka in db.Kunde_Anwendung.Where(ka => es.Anwendung_ID == ka.Anwendung_ID)
select new { Kunde = ka.Kunde, Geraet = es.Geraet, Anwendung = es.Anwendung })
.GroupBy(kga => kga.Kunde, kga => new {Geraet = kga.Geraet, Anwendung = kga.Anwendung});
it would be better, when the result is a IEnumerable(Kunde, IEnumerable(Geraet), IEnumerable(Anwendung)) without the null-Values for the union.
i try it as SQL command
select Count(es.Geraet_ID), null as Anwendung_ID
from Eintrag_Systeme es cross join Kunde_Geraet where es.Geraet_ID = Kunde_Geraet.Geraet_ID AND es.Eintrag_ID = #id
union
select null as Geraet_ID, Count(es.Anwendung_ID)
from Eintrag_Systeme es cross join Kunde_Anwendung where es.Anwendung_ID = Kunde_Anwendung.Anwendung_ID AND es.Eintrag_ID = #id
group by Kunde_ID
but donĀ“t get the Count() of Anwendungen(Apps)/Geraete(Devices) to Lists grouped by Key Kunde(Client)
Don't use join but navigation properties:
from k in context.Kunden
select new
{
Kunde = k,
Geraete = k.Kunde_Geraete.Select(kg => kg.Geraet),
Anwendungen = k.Kunde_Anwendungen.Select(ka => ka.Anwendung)
}
Now you have a basis from which you get counts, etc.

Select only a single column in LINQ

The EntityModel is defined as:
Personnel has a link to a Country
When executing this code in LinqPad, I see that the SQL which is generated is not optimized (all fields are returned) in the first query ? What am I missing here or doing wrong ?
Query 1 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
personnelIds.Dump();
Query 1 SQL
exec sp_executesql N'SELECT [t0].[Id], [t0].[Version], [t0].[Identifier], [t0].[Name], , [t0].[UpdatedBy] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
Query 2 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds2 = Personnels.Where(p => p.Country == Country).Select(p => p.Id).ToArray();
personnelIds2.Dump();
Query 2 SQL
exec sp_executesql N'SELECT [t0].[Id] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
The database used is SQL Express 2008. And LinqPad version is 4.43.06
//var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = context.Personnels
.Where(p => p.Country.Id == 100000581)
.Select(p => p.Id)
.ToArray();
personnelIds.Dump();
Try this, it should be better.
Personnels collection will be populated via lazy loading when accessed, hence retrieving all of the fields from the DB. Here's what's happening...
// retrieves data and builds the single Country entity (if not null result)
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
// Country.Personnels accessor will lazy load and construct all Personnel entity objects related to this country entity object
// hence loading all of the fields
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
You want something more like this:
// build base query projecting desired data
var personnelIdsQuery = dbContext.Countries
.Where( c => c.Id == 100000581 )
.Select( c => new
{
CountryId = c.Id,
PersonnelIds = c.Personnels.Select( p => p.Id )
}
// now do enumeration
// your example shows FirstOrDefault without OrderBy
// either use SingleOrDefault or specify an OrderBy prior to using FirstOrDefaul
var result = personnelIdsQuery.OrderBy( item => item.CountryId ).FirstOrDefault();
OR:
var result = personnelIdsQuery.SingleOrDefault();
Then get the array of IDs if not null
if( null != result )
{
var personnelIds = result.PersonnelIds;
}
Try can also try grouping personnel into a single query
var groups =
(from p in Personnel
group p by p.CountryId into g
select new
{
CountryId = g.Key
PersonnelIds = p.Select(x => x.Id)
});
var personnelIds = groups.FirstOrDefault(g => g.Key == 100000581);
Do you have the ForeignKey explicitly defined in your POCO for Personnel? It's common to leave it out in EF, but adding it would massively simplify both this code and the resulting SQL:
public class Personnel
{
public Country Country { get; set; }
[ForeignKey("Country")]
public int CountryId { get; set; }
. . .
}
> update-database -f -verbose
var ids = db.Personnel.Where(p => p.CountryId == 100000581).Select(p => p.Id).ToArray();

Linq: Nested queries are better than joins, but what if you use 2 nested queries?

In her book Entity Framework Julie Lerman recommends using nested queries in preference to joins (scroll back a couple of pages).
In her example see populates 1 field this way, but what id you want to populate 2?
I have an example here where I would prefer to populate the Forename and Surname with the same nested query rather than 2 separate ones. I just need to know the correct syntax to do this.
public static List<RequestInfo> GetRequests(int _employeeId)
{
using (SHPContainerEntities db = new SHPContainerEntities())
{
return db.AnnualLeaveBookeds
.Where(x => x.NextApproverId == _employeeId ||
(x.ApproverId == _employeeId && x.ApprovalDate.HasValue == false))
.Select(y => new RequestInfo
{
AnnualLeaveDate = y.AnnualLeaveDate,
Forename = (
from e in db.Employees
where e.EmployeeId == y.EmployeeId
select e.Forename).FirstOrDefault(),
Surname = (
from e in db.Employees
where e.EmployeeId == y.EmployeeId
select e.Surname).FirstOrDefault(),
RequestDate = y.RequestDate,
CancelRequestDate = y.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
})
.OrderBy(x => x.AnnualLeaveDate)
.ToList();
}
}
There's nothing wrong with your query, but you can write it in a way that is much simpler, without the nested queries:
public static List<RequestInfo> GetRequests(int employeeId)
{
using (SHPContainerEntities db = new SHPContainerEntities())
{
return (
from x in db.AnnualLeaveBookeds
where x.NextApproverId == employeeId ||
(x.ApproverId == employeeId && x.ApprovalDate == null)
orderby x.AnnualLeaveDate
select new RequestInfo
{
AnnualLeaveDate = x.AnnualLeaveDate,
Forename = x.Employee.Forename,
Surname = x.Employee.Surname,
RequestDate = x.RequestDate,
CancelRequestDate = x.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
}).ToList();
}
}
See how I just removed your from e in db.Employees where ... select e.Forename) and simply replaced it with x.Employee.Forename. When your database contains the correct foreign key relationships, the EF designer will successfully generate a model that contain an Employee property on the AnnualLeaveBooked entity. Writing the query like this makes it much more readable.
I hope this helps.
try this
using (SHPContainerEntities db = new SHPContainerEntities())
{
return db.AnnualLeaveBookeds
.Where(x => x.NextApproverId == _employeeId ||
(x.ApproverId == _employeeId && x.ApprovalDate.HasValue == false))
.Select(y =>
{
var emp = db.Emplyees.Where(e => e.EmployeeId == y.EmployeeId);
return new RequestInfo
{
AnnualLeaveDate = y.AnnualLeaveDate,
Forename = emp.Forename,
Surname = emp.Surname,
RequestDate = y.RequestDate,
CancelRequestDate = y.CancelRequestDate,
ApproveFlag = false,
RejectFlag = false,
Reason = string.Empty
};
).OrderBy(x => x.AnnualLeaveDate).ToList();
}

Resources