Left Join to top 1 using LINQ to entites - c#

I have a list of entities that each have 0 > n associated reviews. I want to join the latest review, if one exists for each entity. I then want to return the status of the latest review as below, but not able to filter lstReviews down to the top 1 by CreatedDate Descending.
var lstMainEntity = this.service.GetAllMainEntities();
var lstReviews = this.service.GetReviews();
var lst = from e in lstMainEntity
from review in lstReviews.Where(r => r.EntityID == e.EntityID).DefaultIfEmpty()
select new
{
ID = e.EntityID,
ReviewStatus = review != null ? review.IsComplete ? "Complete" : "Active"
}
How can i best achieve this?

I am assuming lstMainEntity and lstReviews are IQueryable<..>. If they are IEnumerable<..>, you will have to put a null check in the ReviewStatus assignment. (Performance should be faster if it is an IQueryable)
var lst = from e in lstMainEntity
let review = lstReviews.Where(r => r.EntityID == e.EntityID)
.OrderByDescending(r => r.CreatedDate)
.FirstOrDefault()
select new
{
ID = e.EntityID,
ReviewStatus = review.IsComplete ? "Complete" : "Active"
}

Related

horrible and big LINQ statement optimisation

I have to to a rather large request to a database to fetch a bunch of data, it's however taking a noticeable time to run. is there some way to increase the performance on this? preemptive apologies for the ugly code (I did have a version that segmented this into multiple smaller functions but that was even slower)
from contact in _database.OBJECTCONTACT
where contact.OBJECTCONTACTOWNER.Any(o => o.OBJECTID == id && o.OBJECTTYPE == type) && contact.ACTIVE >= 1 && CheckViewAccess(contact)
group contact by (contact.OBJECTCONTACTPROJECT.Any() ? contact.OBJECTCONTACTPROJECT.First().OBJECTPROJECT.PROJECTNAME : "General") into projectGroup
select new ProjectViewModel()
{
ProjectName = projectGroup.Key,
ContactGroups = (from g in _database.OBJECTGROUP
where g.GROUPTYPE == "CONTACT" && ContactsModule.CheckUserRole("View", g.OBJECTTYPE, g.GROUPNAME)
select new ContactGroupViewModel()
{
CanEdit = ContactsModule.CheckUserRole("Edit", g.OBJECTTYPE, g.GROUPNAME),
GroupId = g.OBJECTGROUPID,
GroupName = g.GROUPNAME,
Contacts = (from c in projectGroup
join l in _database.OBJECTCONTACTLOCATION on c.OBJECTCONTACTLOCATIONID equals l.OBJECTCONTACTLOCATIONID into lgrp from loc in lgrp.DefaultIfEmpty(null)
orderby c.NAME
select new ContactViewModel()
{
Id = (int)c.OBJECTCONTACTID,
Name = c.NAME,
Description = c.DESCRIPTION,
ContactInformation = CreateContactInfoViewmodels(c),
Owners = c.OBJECTCONTACTOWNER.Where(owner => owner.OBJECTTYPE == "AIRPORT")
.Select(owner => ContactOwnerViewModel.FromOwnerId(owner.OBJECTID, owner.OBJECTTYPE)).ToList(),
Projects = c.OBJECTCONTACTPROJECT.Select(proj => proj.OBJECTPROJECT).ToList(),
Typename = GetTypeName(c),
TypeId = c.OBJECTCONTACTTYPEID ?? 0,
ContactGroupId = c.OBJECTGROUPID,
ContactGroup = g.GROUPNAME,
Editable = CheckAccessBool("EDIT", c),
Location = loc != null ? new LocationViewModel()
{
Address = loc.ADDRESS,
GoogleMapLink = loc.GMAPADDRESS,
LocationId = loc.OBJECTCONTACTLOCATIONID,
LatLon = Tuple.Create(loc.LATITUDE, loc.LONGITUDE)
} : null,
}).ToList()
}).ToList()
}).ToList();
I think I should be able to use joins to move the entire DB fetch code to the top (theoretically improving perfomance) but I am having trouble finding the syntax which would suit my needs
Thanks everyone for coming with suggestions. I am in a situation where I'm not able to do much with the database itself so I'm making the best of what I have. my hands a bit tied in regards to the tools at my disposal (also fairly old codebase I think it's EF 5 or something like that)
this version moves the DB transaction to the top (so that is fewer fetches) and does a lot of data manipulation at the bottom.
// general object is created above
var res = (from contact in _database.OBJECTCONTACT.AsEnumerable() // as enumerable used to allow for defaultifempty in join (minor damage to performance)
join oGroup in _database.OBJECTGROUP on contact.OBJECTGROUPID equals oGroup.OBJECTGROUPID into og from objectGroup in og.DefaultIfEmpty(defaultValue: general)
where contact.OBJECTCONTACTOWNER.Any(o => o.OBJECTTYPE == type && o.OBJECTID == id)
// ReSharper disable once PossibleNullReferenceException (it's taken care of by check using .any() )
group new {contact, objectGroup } by (contact.OBJECTCONTACTPROJECT.Any() ? contact.OBJECTCONTACTPROJECT.FirstOrDefault().OBJECTPROJECT.PROJECTNAME : "General") into pGroup
orderby pGroup.Key == "General" ? pGroup.Key : "􏿽" descending
select new ProjectViewModel()
{
ProjectName = pGroup.Key,
ProjectId = pGroup.FirstOrDefault() != null ? (pGroup.FirstOrDefault().contact.OBJECTCONTACTPROJECT.FirstOrDefault() != null ? pGroup.FirstOrDefault().contact.OBJECTCONTACTPROJECT.FirstOrDefault().OBJECTPROJECTID : -1) : -1,
ContactGroups = (from c in pGroup
group c by c.objectGroup into grp
let canEdit = ContactsModule.CheckUserRole("EDIT", grp.Key.OBJECTTYPE, grp.Key.GROUPNAME)
orderby grp.Key.SORTORDER descending
select new ContactGroupViewModel()
{
GroupName = grp.Key.GROUPNAME,
GroupId = grp.Key.OBJECTGROUPID,
CanEdit = canEdit,
Contacts = grp.Select(item => new ContactViewModel()
{
Id = (int)item.contact.OBJECTCONTACTID,
Name = item.contact.NAME,
Description = item.contact.DESCRIPTION,
Editable = canEdit,
ContactInformation = item.contact.OBJECTCONTACTNUMBER.OrderByDescending(num => num.ISMAININFO).Select(num => new ContactInfoViewmodel()
{
Data = num.NUMBERDATA,
IsMain = num.ISMAININFO > 0,
Type = num.OBJECTCONTACTNUMBERTYPE.NAME
}).ToList()
}).ToList()
}).ToList()
}).ToList();
this seems to (on average) take about a 4th of the time the original query needed (still a noticeable time due to the size of database but within acceptable limits)

Convert SQL query to LINQ or lambda expression in C# and use in EF Core

I have 3 table
Tbl_City , Tbl_GroupCities , Tbl_CtrCar .
I want to convert this SQL query to LINQ or lambda expression in C#
declare #fk_group uniqueidentifier
SELECT #fk_group= FK_Group
FROM dbo.Tbl_User
WHERE UserName='meysam'
SELECT dbo.Tbl_City.ID_City, dbo.Tbl_City.Name_City,COUNT( dbo.Tbl_CtrCar.Cur_year)
FROM dbo.Tbl_City
INNER JOIN dbo.Tbl_CtrCar ON dbo.Tbl_City.ID_City = dbo.Tbl_CtrCar.FK_City
WHERE ID_City IN (SELECT FK_City
FROM dbo.Tbl_GroupCities
WHERE Active=1 AND ID_Group=#fk_group)
GROUP BY ID_City , Name_City
I try it but it's not work
var model = _TblUser.FirstOrDefault(x => x.UserName == "sampleUserName");
var q = _TblGroupCities.Where(x => x.IdGroup == model.FkGroup && x.Active == true);
var sample2 =
(from x in _TblCity
join a in _TblGroupCities on x.IdCity equals a.FkCity
where a.Active == true && a.IdGroup == model.FkGroup
select new
{
x.IdCity,
x.NameCity
}).ToList();
Please take a look here the features you have in your query are not yet implemented. GroupBy and i think also subselects will do an
SELECT * FROM TableName
And in memory it will do the group by or even for each row a new SQL query.
Better to use the RawSql method for this purpose.
But if you realy want to learn LINQ and convert your SQL take a look at LINQPad
This issue is done. I found my problem, I don't Understand use two joins and use group by in Linq
I use this linq for the solution and run
var model = _TblUser.SingleOrDefault(x => x.UserName == type.UserName);
var q = _TblGroupCities.Where(x => x.IdGroup == model.FkGroup && x.Active == true);
tblCityViewModel = new List<MohasebKhodro.ViewModels.TblCityViewModel>();
var sample2 =
(from x in _TblCity
join a in _TblGroupCities on x.IdCity equals a.FkCity
where a.Active == true && a.IdGroup == model.FkGroup
select new
{
x.IdCity,
x.NameCity
}).ToList();
foreach (var item in sample2)
{
var er = _TblCtrCar.Where(x => x.FkCity == item.IdCity).Max(x => x.CurYear);
tblCityViewModel.Add(new MohasebKhodro.ViewModels.TblCityViewModel
{
IdCity = item.IdCity,
NameCity = item.NameCity,
MaxCurrentYear = Convert.ToString(er)
});
}

Linq let and if

I am getting:
Non-static method requires a target.
The problem is that Status is null. I don't understand why, because there is a condition which clearly indicates if Status is null return 1.
var filterstatus = (from bq in basequery
let LastStatus = Status == null ? 1
: ((from sd in ems.SampleDatas
where sd.Reference_id == Status.id
&& sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
It's because it's converting the entire expression into SQL, and not doing the short-circuit in memory (the short circuit would be handled by the database).
You can write something like this, which will properly short-circuit in the database (but still generate the right hand side of the query).
var statusID = Status == null ? (int?)null : Status.id;
var filterstatus = (from bq in basequery
let LastStatus =
statusID == null ?
1 :
((from sd in ems.SampleDatas
where sd.Reference_id == statusID && sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
Ideally, though, you'd have two separate queries, depending on Status, as it's already known at that point whether the right hand side is required or not.

Linq new statement selection conditionally

i need to select data to populate a DataGrid in MVC Asp.net with entity framework. When i select all values i need to have values from three tables connected: e, category and product. The connection is always 1 to 0 or 1. I have selected all walue with this code but when there isn't a category.name associated i naturally have an exception. What is the best way to do this?Do i need use if statment in New constructor?Or other?
var products = from e in dbResult
select new
{
e.Id,
e.Title,
e.Price,
e.Quantity,
e.category.nome,
e.Product.Sottotitolo,
e.Procuct.Provenienza
};
Thanks to all
Prior to C# 6 one way would be:
var products = from e in dbResult
select new
{
e.Id,
e.Title,
e.Price,
e.Quantity,
Noma = e.category == null ? "" : e.category.nome,
Sottotitolo = e.Product == null ? "" : e.Product.Sottotitolo,
Provenienza = e.Procuct == null ? "" : e.Procuct.Provenienza
};
With C# 6 you can use the ?. null-conditional member access operator:
var products = from e in dbResult
select new
{
e.Id,
e.Title,
e.Price,
e.Quantity,
Noma = e.category?.nome,
Sottotitolo = e.Product?.Sottotitolo,
Provenienza = e.Procuct?.Provenienza
};
Note that the field values in the latter method will be null instead of empty strings.

Linq To Entities

I have a small problem in my where clause in the linq expression below. If I put the number 3 instead of department.Id I get the desired result but when I use department.Id I get nothing in the resultset.
I also want to get a count for the number of filters for that filter name using the query again using distinct.
var dept = Page.RouteData.Values["department"];
var department = (from d in db.Departments
where d.Name.Replace(" ", "-") == dept
select new {d.Id, d.Name}).FirstOrDefault();
var query = from p in db.Products
join f in db.ProductFilters on p.Id equals f.ProductId into filters
from x in filters.Where(x => x.Product.DepartmentId == department.Id
/* if == 3 it works */)
select new { x.Name, x.Id };
Promoted to answer from comments:
Have you checked that the department instance is as you think it should be after the first linq statement - ie has an Id == 3?
Your first query is not finding any valid department and is therefore returning default which most probably means that departmend.Id == 0.

Categories