Entity Framework - Results from subquery, speed issue? - c#

I've made a query to get a list of articles, each bound to a "header",
so i retrieve the header plus the related articles and their properties.
The query works, however in its current style it is
Somewhat messy ( in my opinion)
The .ToList() takes way longer than i would expect.
Does anyone see any obvious reason for the speed-issue?
var offerheaders =
from o in dbcontext.F_CAB_OFFER_HEADERS
where
o.OFHD_FK_BUYER == userinfo.orgaTypeSequence
&& o.OFHD_VALID_FROM <= userinfo.selectedDate
&& o.OFHD_VALID_TO >= userinfo.selectedDate
&& o.OFHD_DELETED_YN == 0
&& o.OFHD_DELETED_BY_OWNER_YN == false
&& o.OFHD_OFFER_TYPE == userinfo.offerType
orderby o.OFHD_NO ascending
select o;
var offerlist =
from ofhd in offerheaders
select new {
ofhd = new {
OfferNo = ofhd.OFHD_NO,
OfferSequence = ofhd.OFHD_SEQUENCE,
ValidFrom = ofhd.OFHD_VALID_FROM,
ValidTo = ofhd.OFHD_VALID_TO,
OfferType = ofhd.OFHD_OFFER_TYPE,
Maingroup = new { cdmg_seq = ofhd.F_CAB_CD_MAIN_GROUP_TYPE.CDMG_SEQUENCE, Desc = ofhd.F_CAB_CD_MAIN_GROUP_TYPE.CDMG_DESC },
Supplier = new {
Name = ofhd.F_CAB_GROWER.F_CAB_ORGANISATION.ORGA_NAME,
Pic = ofhd.F_CAB_GROWER.F_CAB_ORGANISATION.ORGA_FK_PICTURE,
Seq = ofhd.F_CAB_GROWER.GROW_SEQUENCE
},
Caption = ofhd.OFHD_CAPTION,
Seperate = ofhd.OFHD_SHOW_SEPARATE_YN,
//ofdts = (from ofdt in dbcontext.F_CAB_OFFER_DETAILS.Where(x => x.OFDT_FK_OFFER_HEADER == ofhd.OFHD_SEQUENCE && x.OFDT_NUM_OF_ITEMS > 0 && x.OFDT_LATEST_DELIVERY_DATE_TIME > compareDateTime && x.OFDT_LATEST_ORDER_DATE_TIME > compareDateTime)
ofdts = from ofdt in dbcontext.F_CAB_OFFER_DETAILS
join props in dbcontext.F_CAB_CAB_PROP on ofdt.OFDT_FK_CAB_CODE equals props.PROP_FK_CABC_SEQ
join cabcode in dbcontext.F_CAB_CD_CAB_CODE on ofdt.OFDT_FK_CAB_CODE equals cabcode.CABC_SEQUENCE
join cabgroup in dbcontext.F_CAB_CD_CAB_GROUP on cabcode.CABC_FK_CAB_GROUP equals cabgroup.CDGR_SEQUENCE
join grouptype in dbcontext.F_CAB_CD_GROUP_TYPE on cabgroup.CDGR_FK_GROUP_TYPE equals grouptype.CDGT_SEQUENCE
join maingrouptype in dbcontext.F_CAB_CD_MAIN_GROUP_TYPE on grouptype.CDGT_FK_MAIN_GROUP equals maingrouptype.CDMG_SEQUENCE
join caca in dbcontext.F_CAB_CAB_CASK_MATRIX on ofdt.OFDT_FK_CACA_SEQ equals caca.CACA_SEQUENCE
join cask in dbcontext.F_CAB_CD_CASK on caca.CACA_FK_CASK equals cask.CDCA_SEQUENCE
join vbncode in dbcontext.F_CAB_CAB_VBN_MATRIX on cabcode.CABC_SEQUENCE equals vbncode.CVMA_FK_CAB_CODE
join grel in dbcontext.F_CAB_GENERAL_RELATIONS on ofdt.OFDT_FK_GREL_SEQ equals grel.GREL_SEQUENCE into greltable
from g_loj in greltable.DefaultIfEmpty()
where
ofdt.OFDT_FK_OFFER_HEADER == ofhd.OFHD_SEQUENCE
&& ofdt.OFDT_NUM_OF_ITEMS > 0
&& props.PROP_FK_CDLA_SEQ == userinfo.lang.CDLA_SEQUENCE
orderby props.PROP_CAB_DESC ascending
select new {
Desc = props.PROP_CAB_DESC,
Group = new { cdgr_seq = cabgroup.CDGR_SEQUENCE, Desc = cabgroup.CDGR_DESC },
Grouptype = new { grouptype.CDGT_SEQUENCE, Desc = grouptype.CDGT_DESC },
Properties = new CABProperties { props = props },
Price = ofdt.OFDT_ITEM_PRICE,
PIC_SEQ = ofdt.OFDT_FK_PICTURE ?? ((cabcode.CABC_FK_PICTURE ?? cabcode.CABC_SEQUENCE)),
PIC_URL = ofdt.OFDT_EXT_PICTURE_REF ?? "",
Seq = ofdt.OFDT_SEQUENCE,
Available = ofdt.OFDT_NUM_OF_ITEMS,
CabCode = ofdt.F_CAB_CD_CAB_CODE.CABC_CAB_CODE,
VBNCode = vbncode.CVMA_FK_VBN_CODE,
Remark = ofdt.OFDT_REMARK,
IsSpecial = ofdt.OFDT_SPECIAL_YN,
Arrived = inTransit ? ofdt.OFDT_ARRIVAL_DATE < DateTime.Now : true,
Cask = new CABCask { cask = cask, caca = caca },
Supplier = g_loj == null ? (ofdt.OFDT_SUPPLIER ?? "") : g_loj.GREL_NAME,
SupplierWeb = g_loj == null ? "" : g_loj.GREL_WEBSITE_URL,
SupplierLogo = g_loj == null ? ofhd.F_CAB_GROWER.F_CAB_ORGANISATION.ORGA_FK_PICTURE : g_loj.GREL_FK_PICT_SEQ,
SupplierSeq = g_loj == null ? -1 : g_loj.GREL_SEQUENCE,
}
}
};
userinfo.mainofferlist = offerlist.ToList();

As Daniel Kelly also mentioned the ToList function is where your query is executed, because these LinqToEntities queries are executed at the point where they are first enumerated, and ToList does that to be able to create a list.
Basically the reason why your querying takes so much time can be separated into two different reasons:
you are using too much projections and I thine (the parts with new {
})
your query has an incredible amount of join clauses
I would recommend to separate your query into subqueries, and run them separately like the first part in
...
select o
use
...
select o).ToList()
by breaking down the main query where you have a lot of subqueries it will be faster and much more readable, so you have less "messiness".
And last but not least you should create mapping for the anonymous objects, and use those classes other than projection that should speed up your query.

Related

Trying to add item to linq select statement in c#

I have a long result set in linq which is put in ratestest, I am mapping the result to ChargeElementsNullable. The issue is this doesnt include a fee record which is
var fees = (from r in _dbContext.Rates
where r.RateTypeFK == RateType.Fee && (r.LocaleBW & localeid) > 0 && (r.PolicyBW & policy.BitWise) > 0 && r.RateExclude == 0
select r);
So I want to add fees to ratestest and I could do that mapping again so I can use the add method, but I dont want to do that long winded mapping just for one record.. I am trying to add it to ratestest directly instead.. but no joy... I tried using DefaultIfEmpty expecting a left join.. but fee still wasnt in there..
var ratestest = (from qi in quoteInputs
join r in _dbContext.Rates on qi.RatePK equals r.RatePK
join fee in fees on r.RatePK equals fee.RatePK into feecontainer
from fee in feecontainer.DefaultIfEmpty()
join c in _dbContext.Covers on r.CoverCalcFK equals c.CoverPK into covers
from c in covers.DefaultIfEmpty()
join rt in _dbContext.RateTypes on qi.RateTypeFK equals rt.RateTypePK
where rt.Ratable == 1 ||
rt.RateTypePK == RateType.PostCode ||
rt.RateTypePK == RateType.Fee// employersliab.Contains(r.InputFK)
select new ChargeElementsNullable
{
PolicyFK = quote.PolicyFK,
InputFK = r.InputFK,
LongRate = r.LongRate,
RateLabel = r.RateLabel,
CoverName = c.CoverName,
CoverFK = r.CoverCalcFK,
CoverBW = c.BitWise,
ListRatePK = r.ListRatePK,
RatePK = r.RatePK,
RateName = r.RateName,
Rate = r.Rate,
Threshold = r.Threshold,
Excess = r.Excess,
DivBy = r.DivBy,
DiscountFirstRate = r.DiscountFirstRate,
DiscountSubsequentRate = r.DiscountSubsequentRate,
HazardRating = r.HazardRating,
TableFirstColumn = r.TableFirstColumn,
TableChildren = r.TableChildren,
RateTypeFK = r.RateTypeFK,
PageNo = r.PageNo,
SumInsured = qi.SumInsured,
NoItems = qi.NoItems,
RateValue = qi.RateValue,
TriggerCode = rt.TriggerCode,
Territory = territory
}).ToList();
You have to create a model with two properties one for quoteInputs and another for fees. Then you just need to select both of them.
Eg:
class model1
{
public QuoteInputs quoteInputs {get;set;}
public Fees fees{get;set;}
}
Then you call use this model in the select clause and assign this model tables directly.
Now let's take your code and change select like this :
var ratestest = (from qi in quoteInputs
join r in _dbContext.Rates on qi.RatePK equals r.RatePK
join fee in fees on r.RatePK equals fee.RatePK into feecontainer
from fee in feecontainer.DefaultIfEmpty()
join c in _dbContext.Covers on r.CoverCalcFK equals c.CoverPK into covers
from c in covers.DefaultIfEmpty()
join rt in _dbContext.RateTypes on qi.RateTypeFK equals rt.RateTypePK
where rt.Ratable == 1 ||
rt.RateTypePK == RateType.PostCode ||
rt.RateTypePK == RateType.Fee// employersliab.Contains(r.InputFK)
select new model1{
quoteInputs = qi,
fees = fee
}).ToList();
As you mentioned in comments, you are seeking for Concat method (MSDN). This can be done like this:
var fees = from ...
select new ChargeElementsNullable {
Prop1 = (some value),
Prop2 = (other value)
}
var ratestest = from ...
select new ChargeElementsNullable {
Prop1 = (some value),
Prop2 = (other value)
}
var bothtogether = fees.Concat(ratestest);
Be sure, that you have all properties in exactly same order and all properties are in both selects. Otherwise Linq2Sql will fail when obtaining results. (I assume from your code that you are using it)
As #Cris correctly pointed out in comments, same order is not necessary.

LINQ: Multiple Join With Max Revision Filter

So I have this line:
var transrevmax = db.TRANSACTs
.Where(x => x.SITE == "1" &&
x.DATE_IN >= dateinchoice &&
x.DATE_OUT <= dateoutchoice)
.GroupBy(x => x.TICKET_NO)
.Select(x => x.OrderByDescending(y => y.REV_NO).FirstOrDefault())
.ToList();
and it returns the exact list of transactions I want the join below to go through to obtain values from...
var exportrecovery = (from trans in transrevmax
join detail in db.DETAILs on new { TICKET_NO = trans.TICKET_NO, REV_NO = trans.REV_NO } equals new { TICKET_NO = detail.TICKET_NO, REV_NO = detail.REV_NO }
join job in db.JOBs on new { JOB_CODE = trans.JOB_CODE, CUST_CODE = trans.CUST_CODE } equals new { JOB_CODE = job.CODE, CUST_CODE = job.CUST_CODE }
join customer in db.CUSTOMERs on trans.CUST_CODE equals customer.CODE
join invoiced in db.INVOICEDs on trans.TICKET_NO equals invoiced.TICKET_NO
where trans.DATE_IN >= dateinchoice && trans.DATE_OUT <= dateoutchoice && trans.STATUS.ToString().Trim() != "V" && trans.STATUS.ToString().Trim() != "E"
select new { ADDRESS = customer.ADDRESS, CITY = customer.CITY, STATE = customer.STATE, ZIP = customer.ZIP, FREIGHT = detail.HAUL_CHGE + detail.FUEL_CHGE, JOB_NAME = job.NAME, HAUL_TAX = detail.HAUL_TAX, INVOICE_NO = invoiced.INVOICE_NO, CUST_NAME = customer.NAME, TAX_CODE = customer.TAX_CODE, MAT_CHGE = detail.MAT_CHGE, MAT_TAX = detail.MAT_TAX, MAT_CODE = detail.MAT_CODE, QTY = detail.QTY, PRICE = detail.PRICE, DATE_MOD = trans.DATE_OUT, REV_NO = trans.REV_NO, SITE = trans.SITE, TICKET_NO = trans.TICKET_NO, CUST_CODE = trans.CUST_CODE, JOB_CODE = trans.JOB_CODE }
).ToList();
... if I run the first line I get the transactions limited to only those with the max revision (what I want), and if I run the join (replacing the "transrevmax" with the table "db.TRANSACTs") I get the right range of ticket numbers, but it includes all revisions. I am stumped as to how I can use the joined tables and get only unique rows according to their (maximum) revision number. When these two are used in conjunction I receive zero rows. Please advise.
Remove the ToList on the transrevmax creation.

How to get the group sum in my complex linq query

below I have listed a linq query that works properly in my asp.net.mvc web app.
In addition I would like to group over 'allowance.ParameterId' in order to get the group sum for 'allowance.Freight (instead of multiple records for the given key).
var query = from ara in aras
join company in companies on ara.Id equals company.ARAId
join wasteWater in wasteWaters on company.Id equals wasteWater.CompanyId
join allowance in allowances on wasteWater.Id equals allowance.WasteWaterID
join parameter in parameters on allowance.ParameterId equals parameter.Id into JoinedParameterAllowance
from parameter in JoinedParameterAllowance.DefaultIfEmpty()
where company.Activ == true && company.End == null && company.Template == false
&& wasteWater.End == null
select new FreightSummaryViewModel
{
AraName = ara.Name,
AraId = ara.Id,
AllowedParameter = parameter.Name,
AllowedFreight = allowance.Freight
};
I have tried to insert 'group ...' but failed to get it right.
Could someone help me please to set up the proper syntax?
Thank you in advance, Manu
I have little idea about relations in your database, so I improvised...
// Some dummy data to play with
var aras = Enumerable.Range(0, 5).Select(i => new { Id = i, Name = "Ara" + i });
var companies = Enumerable.Range(0, 15).Select(i => new { Id = i, ARAId = i % 5, Activ = true, End = (DateTime?)null, Template = false });
var wasteWaters = Enumerable.Range(0, 35).Select(i => new { Id = i, CompanyId = i / 15, End = (DateTime?)null });
var allowances = Enumerable.Range(0, 70).Select(i => new { Id = i, WasteWaterID = i, ParameterId = i % 4, Freight = i * 1000 });
var parameters = Enumerable.Range(0, 4).Select(i => new { Id = i, Name = "Parameter" + i });
And this is what I believe you looked for:
var query =
from ara in aras
join company in companies on ara.Id equals company.ARAId
join wasteWater in wasteWaters on company.Id equals wasteWater.CompanyId
join allowance in allowances on wasteWater.Id equals allowance.WasteWaterID
join parameter in parameters on allowance.ParameterId equals parameter.Id
into JoinedParameterAllowance
// from parameter in JoinedParameterAllowance.DefaultIfEmpty()
where true
&& company.Activ == true
&& company.End == null
&& company.Template == false
&& wasteWater.End == null
group allowance by new
{
AraName = ara.Name,
AraId = ara.Id,
ParameterId = allowance.ParameterId
} into myGroup
select new //FreightSummaryViewModel
{
AraName = myGroup.Key.AraName,
AraId = myGroup.Key.AraId,
AllowedParameter = myGroup.Key.ParameterId,
AllowedFreight = myGroup.Sum(g => g.Freight)
};

How to write Outer Join using LINQ

I am using the below Inner Join to retrive the Data between the two tables, but all data is not getting populated. I tried implementing Outer join by connecting using CCY1== CCY1 and PCODE == PCODE, but no luck.
var q = from g1 in TableCCY1.AsEnumerable()
join g2 in TableCCY2.AsEnumerable()
on g1.Field<string>("CCY1") equals g2.Field<string>("CCY1")
where g1.Field<string>("PCODE") == g2.Field<string>("PCODE")
select new
{
g1currency = g1.Field<string>("CCY1"),
g2currency = g2.Field<string>("CCY1"),
g1code = g1.Field<string>("PCODE"),
g2code = g2.Field<string>("PCODE"),
g1Amt1 = g1.Field<string>("AMT1"),
g2Amt2 = g2.Field<string>("AMT2")
};
Thanks for your help.
For left join you can use this approuch: http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx
The code should be:
var q = from g1 in TableCCY1
join g2 in TableCCY2 on g1.CCY1 equals g2.CCY1 && g1.PCODE equals g2.PCODE into TableCCY3
from g3 in TableCCY3.DefaultIfEmpty()
select new
{
g1currency = g1.CCY1,
g2currency = (g3 == null ? String.Empty : g3.CCY1),
g1code = g1.PCODE,
g2code = (g3 == null ? String.Empty : g3.PCODE),
g1Amt1 = g1.AMT1,
g2Amt2 = (g3 == null ? 0 : g3.AMT2)
};
It looks like you just want to union/concat the two tables into one and then just group on those two columns. You're not logically joining the two tables. That actually makes it much easier.
var q = from row in TableCCY1.AsEnumerable().Concat(TableCCY2.AsEnumerable())
group row by new
{
CCY1 = row.Field<string>("CCY1"),
PCode = row.Field<string>("PCODE")
} into matches
select new
{
CCY1 = matches.Key.CCY1,
PCODE = matches.Key.PCode,
Sum = matches.Sum(match => match.Field<decimal?>("AMT2")),
};

Linq to SQL With Left Outer Join and Group By With Sum - How To

I'm trying to transform the SQL Query below into Linq to SQL
select Categorias.IdCategoria, Categorias.Nome, SUM(lancamentos.valor)
from lancamentos
left outer join Categorias on Lancamentos.IdCategoria = Categorias.IdCategoria
where Month(DataLancamento) = 11
and Credito = 1
and Lancamentos.Ocultar = 0
group by Categorias.IdCategoria, Categorias.Nome
This is what I've done
from lancamento in Lancamentos
where lancamento.Credito == true
&& lancamento.Ocultar == false
&& lancamento.DataLancamento.Month == 10
join categoria in Categorias on lancamento.IdCategoria equals categoria.IdCategoria into temp
from lancamentoJoinCategoria in temp.DefaultIfEmpty()
group lancamentoJoinCategoria by new { lancamentoJoinCategoria.IdCategoria, lancamentoJoinCategoria.Nome } into x
select new {
IdCategoria = (int?)x.Key.IdCategoria
, Nome = x.Key.Nome
}
How do I add the SUM(lancamentos.valor) to the linq to sql above ?
It will be:
(from lancamento in Lancamentos
join categoria in Categorias on lancamento.IdCategoria equals categoria.IdCategoria into temp
from lancamentoJoinCategoria in temp.DefaultIfEmpty()
where lancamento.Credito == true
&& lancamento.Ocultar == false
&& lancamento.DataLancamento.Month == 10
group lancamento by new { lancamentoJoinCategoria.IdCategoria, lancamentoJoinCategoria.Nome } into x
select new
{
IdCategoria = (int?)x.Key.IdCategoria,
Nome = x.Key.Nome,
sumValor = x.Sum(a=>a.valor)
});
You use the .Sum() method.
Eg;
Public Sub LinqToSqlCount03()
Dim q = (From o In db.Orders _
Select o.Freight).Sum()
Console.WriteLine(q)
End Sub
according to MSDN there is no query expression equivalent to the Sum() operation.
I provided a little sample how you could use the Method Syntax of Sum() in a query.
Some query operations, such as Count
or Max, have no equivalent query
expression clause and must therefore
be expressed as a method call. Method
syntax can be combined with query
syntax in various ways. For more
information, see LINQ Query Syntax versus Method Syntax (C#).
var example = new[]
{
new { Count = 1, Name = "a" }, new { Count = 2, Name = "b" },
new { Count = 2, Name = "c" }, new { Count = 2, Name = "c" }
};
var result = from x in example
select new
{
x.Name,
Sum = (from y in example
where y.Count.Equals(2)
&& y.Name==x.Name
select y.Count).Sum()
};
var distinct = result.Distinct().ToList();

Categories

Resources