how to write a linq with multiple join - sql

as am a beginner,i want to get the following set of query as linq with a detailed explanation
//my sql
select COL.title as organizationtitle,CL.[title] as
cousestitle,sum(FD.feathers) as totalfeathers,sum(FD.amount) as
totalamount
from [dbo].[FeathersDonated] FD
join [dbo].[Couses] C on FD.corpid=3 and FD.[cousesid]=C.id
join [dbo].[Couses_lang] CL on FD.[cousesid]=CL.cousesid and
CL.language='en-US'
JOIN [dbo].[Organization_lang] COL on COL.orgid=2 and COL.language='en
US'
group by FD.cousesid,CL.[title],CL.[description],COL.title
i have tried the following set of code. please do help
var featherDonated = _GoUoW.FeathersDonated.FindBy(x => x.corpid ==
param.corpid)
.GroupBy(x => x.cousesid).Select(x => new { cousesid = x.Key, amount =
x.Select(a => a.amount).DefaultIfEmpty(0).Sum(), feathers = x.Select(a =>
a.feathers).DefaultIfEmpty(0).Sum() })
.Join(_GoUoW.Couses.GetAll(), feather => feather.cousesid, couse =>
couse.id, (feather, couse) => new { feather, couse })
.Join(_GoUoW.Organization_lang.FindBy(orglang => orglang.language == "en-
US"), couses => couses.couse.orgid, orgid => (param.organizationid > 0 ?
param.organizationid : orgid.orgid), (couses, orgid) => new { couses,
orgid
})
.Join(_GoUoW.Couses_lang.FindBy(couselang => couselang.language == "en-
US"),
organization => organization.orgid.orgid, couselang => couselang.cousesid,
(organization, couselang) => new { organization, couselang })
.Select(x => new
{
x.organization.couses.feather.amount,
x.organization.couses.feather.feathers,
x.couselang.title
//x.organization.orgid.title,
}).ToList();

Related

SQL query takes too long to execute. Need to improve performance of query

I'm using SQL Server, .NET 5 environment. This SQL query takes too long to execute. This query waiting time is almost 3 min but there are only 35 records. Are there better way to optimize this query ?
var allmeetings = await _context.MeetingMaster
.Where(x => x.IsActive == true &&
(x.MeetingParticipants.Any(y => y.ParticipantId == Convert.ToInt32(_currentUserService.CurrentUserId)) ||
x.OrganizedById == Convert.ToInt32(_currentUserService.CurrentUserId)))
.Include(a => a.MeetingParticipants)
.ThenInclude(b => b.Participant)
.Include(a => a.MeetingAgendaItems)
.ThenInclude(e => e.MeetingActionItems)
.ThenInclude(w => w.ActionItemLogs)
.Include(a => a.MeetingAgendaItems)
.ThenInclude(e => e.MeetingActionItems)
.ThenInclude(w => w.ActionItemResposibilities)
.Include(g => g.MeetingAgendaItems)
.ThenInclude(v => v.MeetingAgendaItemTypes)
.ThenInclude(j => j.AgendaItemRef)
.Include(w => w.MeetingAgendaItems)
.ThenInclude(d => d.RestrictedAgendaItemList)
.ThenInclude(s => s.ParticipantRef)
.Include(s => s.MeetingAgendaItems)
.ThenInclude(q => q.MeetingAgendaItemSupportiveDocuments)
.Include(c => c.MonthsRef)
.Include(z => z.YearsRef)
.Include(g => g.MeetingMinutesDoc)
.Include(c => c.Project)
.Include(c => c.Category)
.Include(l => l.MeetingSuggestions)
.Include(q => q.MeetingMattersArises)
.ThenInclude(i => i.MattersAriseResponsibilities)
.ThenInclude(s => s.ResponsiblePerson)
.Include(e => e.MeetingMattersArises)
.ThenInclude(w => w.MattersAriseReviewerComments)
.Include(s => s.MeetingMattersArises)
.ThenInclude(e => e.MattersAriseLogs)
.AsSplitQuery()
.ToListAsync(cancellationToken);
var result = allmeetings.Where(x => x.IsActive == true &&
x.isRecurringMeeting == false &&
(x.MeetingParticipants.Any(y => y.ParticipantId == Convert.ToInt32(_currentUserService.CurrentUserId)) || x.OrganizedById == Convert.ToInt32(_currentUserService.CurrentUserId))).ToList();
var RecMeetings = allmeetings.Where(x => x.IsActive == true &&
x.isRecurringMeeting == true &&
(x.MeetingParticipants.Any(y => y.ParticipantId == Convert.ToInt32(_currentUserService.CurrentUserId)) || x.OrganizedById == Convert.ToInt32(_currentUserService.CurrentUserId))).ToList();
var groupedRecMeetings = RecMeetings.GroupBy(u => u.MeetingRefId.Substring(0, u.MeetingRefId.LastIndexOf('-'))).Select(grp => grp.ToList()).ToList();
var GraeterMeetings = new List<MeetingMaster>();
foreach (var met in groupedRecMeetings)
{
result.AddRange(met.FindAll(x => x.MeetingStatus != "Initiated" ));
GraeterMeetings.AddRange(met.FindAll(x => x.MeetingStatus == "Initiated"));
if(GraeterMeetings.Count != 0)
{
result.Add(GraeterMeetings.OrderBy(x => x.MeetingDate).First());
}
GraeterMeetings.Clear();
}
return result.OrderByDescending(d => d.Id).ToList();
First of all you have so many Includes() and ThenIncludes(), this if really bad for your performance. Is there a way by any chance you can reduce these includes -> only use the necessary one's.
Then i would that you execute the .ToList() query at the end of the statement (befor your return)
Down below is an example how i've done it in the past (with pagination & filtration):
var context = _context.People
.Include(x => x.Parents)
.ThenInclude(x => x.Adress)
.Include(x => x.Job)
.Include(x => x.Hobbies);
var items = string.IsNullOrWhiteSpace(query)
? context
: context.Where(x => x.Name.Contains(query));
var filters = new List<int>();
if (filter != null)
{
filters = filter.Split(',').Select(int.Parse).ToList();
items = items.Where(x => x.Parents.Select(s => s.AdressId).Any(z => filters.Any(y => y == z)));
}
return await items.Skip(page * take).Take(take).ToListAsync();

Joining tables in NHibernate

I have a query below, How can recreate this one to join the tables that will return a list of SurveyProjectNormDTO using NHibernate? Any help please?
using (var session = OpenSession()){
var projectGroupIds = session.Query<ReportingStructureNodeProjectGroups>()
.Where(x => x.NodeID == nodeId);
projectGroupIds.Fetch(x => x.ProjectGroupID).ToFuture();
var projectIds = session.Query<ProjectGroup>().Where(p => projectGroupIds.Contains(p.Id));
projectIds.Fetch(x => x.ProjectID).ToFuture();
var projectNormProjects = session.Query<SurveyProjectNorm>().Where(x => projectIds.Contains(x.SurveyProjectId));
projectNormProjects.Fetch(x => x.ShortLabels).ToFuture();
projectNormProjects.Fetch(x => x.ReportingNames).ToFuture();
projectNormProjects.Fetch(x => x.NormProject).ToFuture();
var response = new List<SurveyProjectNormDTO>();
projectNormProjects.ToList().ForEach(
p =>
{
response.Add(
new SurveyProjectNormDTO { Id = p.Id, ProjectName = p.NormProject.ProjectName, ReportingName = p.ReportingNames.Select(s => s.LocalizedText).FirstOrDefault() });
});
return response;
I am not sure if these let commands will work fine but you can try this. It will do a single hit on the database fetching the properties. Test it and let us know if it works.
var queryResult = (from p in session.Query<SurveyProjectNorm>()
let projectGroupIds = session.Query<ReportingStructureNodeProjectGroups>().Where(x => x.NodeID == nodeId).Select(x => x.Id)
let projectIds = session.Query<ProjectGroup>().Where(x => projectGroupIds.Contains(x.Id)).Select(x => x.Id)
where projectIds.Contains(p.SurveyProjectId)
select p)
.Fetch(x => x.ShortLabels)
.Fetch(x => x.ReportingNames)
.Fetch(x => x.NormProject)
.ToList();
var response = new List<SurveyProjectNormDTO>();
queryResult.ForEach(p =>
response.Add(new SurveyProjectNormDTO {
Id = p.Id,
ProjectName = p.NormProject.ProjectName,
ReportingName = p.ReportingNames.Select(s => s.LocalizedText).FirstOrDefault() }));
return result;

NHibernate projection: How to create AliasToBean projection?

I am trying to convert this inefficient query into one that projects into a dto.
Original query looks like this:
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(x => x.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(x => x.ClientPolicy, () => clientPolicyAlias)
.Where(y => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.List()
.Select(x => new FlatChargeAccessFeeInfo()
{
FlatChargeAccessFeeId = x.Id,
ClientName = x.ClientPolicy.Bid.Client.Name,
PolicyNumber = x.ClientPolicy.PolicyNumber,
ClientPolicyId = x.ClientPolicy.Id,
AgreementAccessFeeId = x.AgreementAccessFee.Id,
ShouldCheckBeGenerated = x.ShouldCheckBeGenerated,
MonthlyFee = x.MontlyFeeAmount.Amount.ToString(),
PolicyYear = x.ClientPolicy.PolicyNumber.Year
})
.ToList();
I tried it like this:
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(x => x.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(x => x.ClientPolicy, () => clientPolicyAlias)
.Where(y => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => feeInfo.FlatChargeAccessFeeId)
.Select(x => x.ClientPolicy.Bid.Client.Name).WithAlias(() => feeInfo.ClientName)
.Select(x => x.ClientPolicy.PolicyNumber).WithAlias(() => feeInfo.PolicyNumber)
.Select(x => x.ClientPolicy.Id).WithAlias(() => feeInfo.ClientPolicyId)
.Select(x => x.AgreementAccessFee.Id).WithAlias(() => feeInfo.AgreementAccessFeeId)
.Select(x => x.ShouldCheckBeGenerated).WithAlias(() => feeInfo.ShouldCheckBeGenerated)
.Select(x => x.MontlyFeeAmount.Amount.ToString()).WithAlias(() => feeInfo.MonthlyFee)
.Select(x => x.ClientPolicy.PolicyNumber.Year).WithAlias(() => feeInfo.PolicyYear)
)
.TransformUsing(Transformers.AliasToBean<FlatChargeAccessFeeInfo>())
.List<FlatChargeAccessFeeInfo>();
and I am getting an error that variable "x" has been referenced in scope but was not defined. What is the proper syntax to convert this?
After help from Andrew, here is the correct version that works
ClientPolicy clientPolicyAlias = null;
Client clientAlias = null;
Bid bidAlias = null;
AgreementAccessFee agreementAccessFeeAlias = null;
FlatChargeAccessFee flatChargeAccessFeeAlias = null;
FlatChargeAccessFeeInfo feeInfo = null;
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(a => a.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(b => b.ClientPolicy, () => clientPolicyAlias)
.JoinAlias(b=>b.Bid,()=>bidAlias)
.JoinAlias(b=>b.Client, ()=>clientAlias)
.Where(c => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.SelectList(list => list
.Select(d => d.Id).WithAlias(() => feeInfo.FlatChargeAccessFeeId)
.Select(e => clientAlias.Name).WithAlias(() => feeInfo.ClientName)
.Select(e => clientAlias.Number).WithAlias(() => feeInfo.ClientNumber)
.Select(f => bidAlias.OptionNumber).WithAlias(() => feeInfo.BidOptionNumber)
.Select(f => bidAlias.Year).WithAlias(()=>feeInfo.PolicyYear)
.Select(g => clientPolicyAlias.Id).WithAlias(() => feeInfo.ClientPolicyId)
.Select(h => agreementAccessFeeAlias.Id).WithAlias(() => feeInfo.AgreementAccessFeeId)
.Select(j => j.ShouldCheckBeGenerated).WithAlias(() => feeInfo.ShouldCheckBeGenerated)
.Select(k => k.MontlyFeeAmount.Amount).WithAlias(()=>feeInfo.MonthlyFee)
)
.TransformUsing(Transformers.AliasToBean<FlatChargeAccessFeeInfo>())
.List<FlatChargeAccessFeeInfo>();
You're close, a few things though:
This select:
.Select(x => x.MontlyFeeAmount.Amount.ToString()).WithAlias(() => feeInfo.MonthlyFee)
will not work. QueryOver attempts to turn your code directly into SQL. If the property does not exist as a column in the database, the query won't work properly (unless you're using a mapped custom type, QueryOver can handle those)
Nested property access won't work either:
.Select(x => x.ClientPolicy.Bid.Client.Name).WithAlias(() => feeInfo.ClientName)
for a similar reason listed above. QueryOver will attempt to turn your property access directly into SQL. You'll need to explicitly join from ClientPolicy to Bid to Client.
In general, remember that you're writing code that's going to be turned into SQL. In fact, I normally write the SQL I want to generate first and then write the QueryOver that corresponds to that. Hope that helps!

NHibernate and JoinAlias throw exception

I have query in HQL which works good:
var x =_session.CreateQuery("SELECT r FROM NHFolder f JOIN f.DocumentComputedRights r WHERE f.Id = " + rightsHolder.Id + " AND r.OrganisationalUnit.Id=" + person.Id);
var right = x.UniqueResult<NHDocumentComputedRight>();
Basically I receive NHDocumentComputedRight instance.
I've tried to implement the same query in QueryOver. I did this:
var right = _session.QueryOver<NHFolder>().JoinAlias(b => b.DocumentComputedRights, () => cp).Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.Select(u => cp).List<NHDocumentComputedRight>();
But I get null reference exception.
How can I implement this query in QueryOver?
Update (added mappings) - NHibernate 3.2:
public class FolderMapping: ClassMapping<NHFolder>
{
public FolderMapping()
{
Table("Folders");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
Set(x => x.DocumentComputedRights, v =>
{
v.Table("DocumentComputedRightsFolder");
v.Cascade(Cascade.All | Cascade.DeleteOrphans);
v.Fetch(CollectionFetchMode.Subselect);
v.Lazy(CollectionLazy.Lazy);
}, h => h.ManyToMany());
Version(x => x.Version, map => map.Generated(VersionGeneration.Never));
}
}
public class DocumentComputedRightMapping : ClassMapping<NHDocumentComputedRight>
{
public DocumentComputedRightMapping()
{
Table("DocumentComputedRights");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
ManyToOne(x => x.OrganisationalUnit, map =>
{
map.Column("OrganisationalUnit");
map.NotNullable(false);
map.Cascade(Cascade.None);
});
}
}
public class OrganisationUnitMapping : ClassMapping<NHOrganisationalUnit>
{
public OrganisationUnitMapping()
{
Table("OrganisationalUnits");
Id(x => x.Id, map =>
{
map.Generator(IdGeneratorSelector.CreateGenerator());
});
//more not important properties...
}
}
Thanks
AFAIK criteria/queryOver can only return the entity it was created for (NHFolder in your example) or columns which are set to entity with aliastobean. you could do a correlated subquery instead.
var subquery = QueryOver.Of<NHFolder>()
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.Select(u => cp.Id);
var right = _session.QueryOver<NHDocumentComputedRight>()
.WithSubquery.Where(r => r.Id).Eq(subquery)
.SingleOrDefault<NHDocumentComputedRight>();
I think you have a problem with the select statement, have you tried something like this:
var right = _session.QueryOver<NHFolder>()
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Select(x => x.DocumentComputedRights)
.Where(h => h.Id == rightsHolder.Id && cp.OrganisationalUnit.Id == person.Id)
.List<NHDocumentComputedRight>();
This is what is working for me so it should work in you case as well.
I would guess that the main reason behind the problem is the lack of proper overload on the Select method. In reality you would like to write it like this:
.JoinAlias(b => b.DocumentComputedRights, () => cp)
.Select(() => cp)
but the Expression<Func<object>> is not there. Hopefully it's going to be included in the next version.

I can't avois this select N+1

I have this mapping ( it comes from adventureworks since I used it in a demo app for an automatic paging collection )
ModelMapper mapper = new ModelMapper(new SimpleModelInspector());
mapper.Class<Contact>(
k => {
k.Id(i => i.ContactID, m => m.Generator(Generators.Native));
k.Schema("Person");
}
);
mapper.Class<Employee>(
k =>
{
k.Id(i => i.EmployeeID, m => m.Generator(Generators.Native));
k.Schema("HumanResources");
k.ManyToOne(c => c.Contact, m => m.Column("ContactID"));
}
);
mapper.Class<SalesOrderHeader>(
k =>
{
k.Id(i => i.SalesOrderID,m=>m.Generator(Generators.Native));
k.Schema("Sales");
k.ManyToOne(c => c.SalesPerson, m => m.Column("SalesPersonID"));
k.ManyToOne(c => c.Contact, m => m.Column("ContactID"));
}
);
var map = mapper.CompileMappingForAllExplicitlyAddedEntities();
cfg.AddDeserializedMapping(map,string.Empty);
and the following ( limited ) query:
var list = NHHelper.Instance.CurrentSession.Query<SalesOrderHeader>()
.Fetch(k => k.Contact)
.Fetch(k => k.SalesPerson)
.Skip(first)
.Take(count)
.ToList();
I can't remove the select N+1 caused by employee-contact, how can I do ?
Consider mapping too can be changed !
EDIT: I add the working solution by #cremor
var list = NHHelper.Instance.CurrentSession.Query<SalesOrderHeader>()
.Fetch(k => k.Contact)
.Fetch(k => k.SalesPerson).ThenFetch(k=>k.Contact)
.Skip(first)
.Take(count)
.ToList();
this will avoid the problem.
Adding .ThenFetch(c => c.Contact) after .Fetch(k => k.SalesPerson) should also fetch the Contact of the Employee.