How to create LINQ from complex SQL? - c#

I've this SQL query:
SELECT D.ID
FROM Documents AS D
INNER JOIN DocClasses AS DC WITH (NOLOCK)
ON D.DocClass = DC.ID
INNER JOIN DocSubClasses AS DSC WITH (NOLOCK)
ON D.DocSubClass = DSC.ID AND DSC.DocClassID = DC.ID
INNER JOIN DocPathFolders AS F WITH (NOLOCK)
ON D.DocPathFolderID = F.ID
WHERE
DC.ShortName = 'PAY' AND DSC.Name = 'xxxxx'
AND UPPER(F.Description) = 'READY TO SEND'
I'm trying to convert this query into LINQ. Here is what I've done so far:
from D in ctx.Documents
join DC in ctx.DocClasses on D.DocClass equals DC.ID
join DSC in ctx.DocSubClasses
on new { D.DocSubClass, DSC.DocClassID } equals new { DSC.ID, DC.ID }
join F in ctx.DocPathFolders
on D.DocPathFolderID equals F.ID
where
DC.ShortName == "PAY"
&& DSC.Name == "xxxxx"
&& (F.Description).ToUpper() == "READY TO SEND"
select D.ID;
I'm getting error in this line:
join DSC in ctx.DocSubClasses on new { D.DocSubClass, DSC.DocClassID } equals new { DSC.ID, DC.ID }
Here, I'm getting following error:
The name 'DSC' is not in scope on the left side of 'equals'
The name 'DC' is not in scope on the right side of 'equals'
I'm new in LINQ and that's why I can't solve these error. I would apperciate any suggestion on the above. Thanks.

You should rearrange the properties in the anonymous objects like this:
join DSC in ctx.DocSubClasses
on new { D.DocSubClass, DC.ID } equals new { DSC.DocClassID, DSC.ID }
Those on the right should be of the table you are joining (DSC) and those on the left can be of previous tables

You can try with Lambda Join instead of using LINQ.
Your JOIN statements look like below in Lambda.
C# Fiddle
var result = ctx.Documents.Join(ctx.DocClasses , d => new { Id = d.DocClass }, dc => new { Id = dc.ID }, (d, dc) => new { doc = d, docClass = dc }) //INNER JOIN DocClasses
.Join(ctx.DocSubClasses , d => new { sc = d.doc.DocSubClass, Id = d.docClass.ID }, dsc => new { sc = dsc.ID, Id = dsc.DocClassID }, (d, dsc) => new { doc = d, dsc = dsc } ) //INNER JOIN DocSubClasses
.Join(ctx.DocPathFolders, d => new { fId = d.doc.doc.DocPathFolderID }, f => new { fId = f.ID }, (d, f) => new { doc = d, f = f }) //INNER JOIN DocPathFolders
.Where(x => x.doc.doc.docClass.ShortName == "PAY" && x.doc.dsc.Name == "xxxxx" && x.f.Description.ToUpper() == "READY TO SEND")//Apply where clause
.Select(y => y.doc.doc.doc.ID);//Select whatever you want

Here is how I've solved my issue:
from D in ctx.Documents
join DC in ctx.DocClasses on D.DocClass equals DC.ID
join DSC in ctx.DocSubClasses on new { D.DocSubClass, DC.ID } equals new { DocSubClass = DSC.ID, ID = DSC.DocClassID }
join F in ctx.DocPathFolders on D.DocPathFolderID equals F.ID
where DC.ShortName == "PAY" && DSC.Name == "xxxx" && (F.Description).ToUpper() == "READY TO SEND"
select D.ID;

Just switch DSC into right side of equals and DC to the left, and it should work fine.
join DSC in ctx.DocSubClasses on new { D.DocSubClass, DC.ID } equals new { DSC.ID, DSC.DocClassID}

Hint: Provided you have set your relations appropriately in your database you almost never need to use "join". It is easier to write in LINQ and relations are exposed as "navigational properties". You simply use "dot notation". ie:
var result = from d in ctx.Documents
where d.DocClasses.Any( dc => dc.ShortName == "PAY"
&& dc.DocSubClasses.Any( dsc => dsc.Name == "xxxxx" )
&& d.DocPathFolders.Any( dpf => dpf.Description.ToUpper() == "READY TO SEND" )
select d.ID;
Note: We don't have any idea on your model. I assumed the relations as 1-To-Many. If it were Many-To-1 then you would simply use notation like:
where d.DocClass.ShortName == "PAY"
&& d.DocClass.DocSubClass.Name == "xxxxx"
&& d.DocPathFolder.Description.ToUpper() == "READY TO SEND"
HINT2: Download and start using Linqpad from LinqPad.net. It is a great tool where you can not only test your LINQ queries but use as a .Net scratch pad.

Related

Pass parameters to linq queries

I have the following part of code that works perfectly.
public Task<List<RsvItemsViewModel>> GetReservations(int company_ID)
{
try
{
// we cannot pass parameter as company_id . we use hard coded values as 1 for CompanyId.Must find a way to overcome this
return ((from a in gTravelDbContext.Set<Frsvitem>()
join d in gTravelDbContext.Set<Freserv>()
on a.Rsinum equals d.Rsvnum into gd from d in gd.Where(p1 => p1.CompanyId == 1)
join c in gTravelDbContext.Set<Fsupplier>()
on a.SupplId equals c.SupplId into gc from c in gc.Where(p1=>p1.CompanyId == 1)
join i in gTravelDbContext.Set<Fzone>()
on a.Rsizone equals i.Zoneid into gi
from i in gi.DefaultIfEmpty() where i.CompanyId == 1
join g in gTravelDbContext.Set<Fservtype>()
on a.Rsisertype equals g.Stypecode
join b in gTravelDbContext.Set<Fcustomer>()
on d.CustId equals b.CustId
join e in gTravelDbContext.Set<Fpaykind>()
on d.Rsvpaymethod equals e.Payid into ge
from e in ge.DefaultIfEmpty()
join f in gTravelDbContext.Set<Fsalesman>()
on d.Rsvsalescode equals f.Salescode into gf
from f in gf.DefaultIfEmpty()
join h in gTravelDbContext.Set<Fpinake>()
on d.Rsvakirosi equals h.Tblid into gh
from h in gh.Where(p => p.Tblcd == "yesno")
select new RsvItemsViewModel
{
Rsinum = a.Rsinum,
Stypename = g.Stypename,
Cuname = b.Cuname,
Rsvcuname = d.Rsvcuname,
Sumame = c.Suname,
Rsvakirosi = d.Rsvakirosi,
Yesno = h.Tbltext ,
Paytext = e.Paytext,
Salesname = f.Salesname,
Stypegroup = g.Stypegroup,
Company_id =d.CompanyId.GetValueOrDefault(),
Zonename = i.Zonename,
Rsisertype = a.Rsisertype,
Suppl_id = a.SupplId,
Xrhsh = d.Xrhsh,
Bpar_id = a.BparId
}
).ToListAsync());
}
catch (Exception)
{
throw;
}
}
The problem is that I want to pass company_ID as a parameter in linq and I want to substitute the Where(p1 => p1.CompanyId == 1) with Where(p1 => p1.CompanyId == company_ID )
I would be grateful if someone could help me.
Thank you
You have used GroupJoin in way, which is not supported by EF Core. GroupJoin has a lot of limitations and use it only if you need LEFT JOIN.
I have rewritten your query to be translatable:
var query =
from a in gTravelDbContext.Set<Frsvitem>()
from d in gTravelDbContext.Set<Freserv>().Where(d => d.Rsvnum == a.Rsinum && d.CompanyId == company_ID)
from c in gTravelDbContext.Set<Fsupplier>().Where(c => a.SupplId == c.SupplId && c.CompanyId == company_ID)
from i in gTravelDbContext.Set<Fzone>().Where(i => a.Rsizone == i.Zoneid && c.CompanyId == company_ID).DefaultIfEmpty()
join g in gTravelDbContext.Set<Fservtype>()
on a.Rsisertype equals g.Stypecode
join b in gTravelDbContext.Set<Fcustomer>()
on d.CustId equals b.CustId
join e in gTravelDbContext.Set<Fpaykind>()
on d.Rsvpaymethod equals e.Payid into ge
from e in ge.DefaultIfEmpty()
join f in gTravelDbContext.Set<Fsalesman>()
on d.Rsvsalescode equals f.Salescode into gf
from f in gf.DefaultIfEmpty()
from h in gTravelDbContext.Set<Fpinake>().Where(h => d.Rsvakirosi == h.Tblid && h.Tblcd == "yesno")
select new RsvItemsViewModel
{
Rsinum = a.Rsinum,
Stypename = g.Stypename,
Cuname = b.Cuname,
Rsvcuname = d.Rsvcuname,
Sumame = c.Suname,
Rsvakirosi = d.Rsvakirosi,
Yesno = h.Tbltext ,
Paytext = e.Paytext,
Salesname = f.Salesname,
Stypegroup = g.Stypegroup,
Company_id =d.CompanyId.GetValueOrDefault(),
Zonename = i.Zonename,
Rsisertype = a.Rsisertype,
Suppl_id = a.SupplId,
Xrhsh = d.Xrhsh,
Bpar_id = a.BparId
}

Linq select statement not working after grouping

I am getting data from multiple tables by joining and I want to group data on basis of date but after group by the statement, I'm getting an error to select all entities against a date.
var query = from record in _entityRepository.GetAll().Where(x => x.DateRecord > DateTime.UtcNow.Date)
join job in _jobRepository.GetAll() on record.Id equals job.Id
into g1
from job in g1.DefaultIfEmpty()
join punchList in _punchListRepository.GetAll() on record.Id equals punchList.Id
into g2 from punchList in g2.DefaultIfEmpty()
join punchJob in _jobRepository.GetAll() on punchList != null ? -1 : punchList.JobId equals punchJob.Id
into g4 from punchJob in g4.DefaultIfEmpty()
group new {record, job, punchList, punchJob} by new{ record.DateRecord}
into g3
select new
{
Date = g3.Key,
job= g3.Select(x=>x.job),
punchList= g3.Select(x=>x.punchList)
};
And I also have tried ToList() in select statement but it did not work.
Try this:
var entityRepository=_entityRepository.GetAll().Where(x => x.DateRecord > DateTime.UtcNow.Date).ToList();
var jobRepository=_jobRepository.GetAll().ToList();;
var punchListRepository=_punchListRepository.GetAll().ToList();;
var query = from record in entityRepository
join job in jobRepository on record.Id equals job.Id into g1
from job in g1.DefaultIfEmpty()
join punchList in punchListRepository on record.Id equals punchList.Id into g2
from punchList in g2.DefaultIfEmpty()
join punchJob in jobRepository on punchList != null ? -1 : punchList.JobId equals punchJob.Id into g4
from punchJob in g4.DefaultIfEmpty()
group new {record, job, punchList, punchJob} by new{ record.DateRecord} into g3
select new
{
Date = g3.Key,
job= g3.Select(x=>x.job).ToList(),
punchList= g3.Select(x=>x.punchList).ToList()
};
We need to split the query into two parts because group by clause is not fully converted into SQL so in my case I want one-month data so below is my code snipet.
var query = from record in _entityRepository.GetAll().Where(x =>
x.DateRecord > DateTime.UtcNow.Date && x.DateRecord <= DateTime.UtcNow.Date.AddMonths(1))
join job in _jobRepository.GetAll() on record.Id equals job.Id
into g1
from job in g1.DefaultIfEmpty()
join punchList in _punchListRepository.GetAll() on record.Id equals punchList.Id
into g2
from punchList in g2.DefaultIfEmpty()
join punchJob in _jobRepository.GetAll() on punchList != null ? -1 : punchList.JobId equals punchJob.Id
into g4
from punchJob in g4.DefaultIfEmpty()
select new {record, job, punchList,punchJob};
var queryResult = await query.ToListAsync();
var result = queryResult.GroupBy(x => x.record.DateRecord)
.Select(o => new
{
Date = o.Key,
job = o.Select(x => x.job ?? new Job()).ToList(),
punchList = o.Select(x => x.punchList ?? new PunchList()).ToList()
});

SQL to Linq: left join

I need to write linq query. I tried to write example in sql and it works great but I didn't manage to convert it to proper linq
This is my working sql query:
SELECT
pr.descr as product,
prm.Descr
, px.Value AS ParamValue
, prm.Unit AS Unit
, gx.TcPos
FROM [Product] pr
JOIN [PrGroup] prg ON pr.GroupId = prg.Id
LEFT JOIN [PrParamGroup] pg ON ISNULL(pr.PrParamGroupId, prg.PrParamGroupId) = pg.Id
CROSS JOIN [PrParam] prm
LEFT JOIN [PrParam2ProductX] px ON pr.Id = px.ProductId AND prm.Id = px.PrParamId
LEFT JOIN [PrParam2GroupX] gx ON prm.Id = gx.PrParamId AND pg.Id = gx.PrParamGroupId
WHERE
pr.Id = 123 AND
(px.PrParamId IS NOT NULL OR gx.PrParamId IS NOT NULL)
AND (gx.PrParamId <> -1 OR gx.PrParamId IS NULL)
AND (gx.PrParamId IS NOT NULL)
This is my linq attempt:
var productsDesc = (from pr in context.Product.Where(m => m.Id == 123)
join prg in context.PrGroup
on pr.GroupId equals prg.Id
join pg in context.PrParamGroup
on pr.PrParamGroupId equals pg.Id
from prm in context.PrParam
join px in context.PrParam2ProductX
on new { a = pr.Id, b = px.ProductId } equals new { a = prm.Id, b = px.PrParamId }
join gx in context.PrParam2GroupX
on new { prm.Id = gx.PrParamId } equals new { pg.Id = gx.PrParamGroupId }
select new
{
Name = prm.Descr,
Value = px.Value,
Unit = prm.Unit ?? ""
}).ToList();
Your linq dont implement left join and cross join
try this
var productsDesc = (from pr in context.Product.Where(m => m.Id == 123)
join prg in context.PrGroup
on pr.GroupId equals prg.Id
join pg in context.PrParamGroup
on pr.PrParamGroupId equals pg.Id into tmp1
from t1 in tmp1.DefaultIfEmpty()
from prm in context.PrParam
join px in context.PrParam2ProductX
on new { a = pr.Id, b = px.ProductId } equals new { a = prm.Id, b = px.PrParamId } into tmp2
from t2 in tmp2.DefaultIfEmpty()
join gx in context.PrParam2GroupX
on new { prm.Id = gx.PrParamId } equals new { pg.Id = gx.PrParamGroupId } into tmp3
from t3 in tmp3.DefaultIfEmpty()
select new
{
Name = prm.Descr,
Value = t2.Value,
Unit = prm.Unit ?? ""
}).ToList();

Why the following query results in an exception?

The following query results in the exception:
"Unable to cast object of type 'System.Linq.Expressions.TypedConstantExpression' to type 'SD.LLBLGen.Pro.LinqSupportClasses.ExpressionClasses.SetExpression'.".
What could be the problem?
return (from ubt in meta.TableUbt
join tc in meta.TableTc on ubt.TCId equals tc.Id
where ubt.Ar110aid == ar110AId && tc.IsPayment
group ubt by new { ubt.Ar110aid, ubt.TCId } into tempTrans
join pyd in meta.TablePyd on tempTrans.Key.Ar110aid equals pyd.Ar110Aid
join pm in meta.TablePm on pyd.Ar110Id equals pm.Id
join tly in TableTly on new { pyd.TyId, ChrgTransId = tempTrans.Key.TCId }
equals new { tly.TyId, tly.ChrgTransId }
join cr in meta.TableCr on
new
{
TyId = (int?)pyd.TyId,
TxLevId = (int?)tly.TxLevId,
Ar101Id = (int?)pm.Ar101Id
}
equals
new
{
cr.TyId,
cr.TxLevId,
cr.Ar101Id
}
join crd in meta.TableCrd on cr.Id equals crd.TableCrId
where crd.StartingLimit <= tempTrans.Sum(b => b.Amount) &&
tempTrans.Sum(b => b.Amount) <= crd.EndingLimit
select crd.Id).FirstOrDefault();
Probably problem is in joining on TableTly. It seems to be local data set. and it's prohibited to join Db data and in-memory objects.

Call Method from Linq query

I am using Linq query and call method Like..
oPwd = objDecryptor.DecryptIt((c.Password.ToString())
it will return null value.
Means this will not working.
how I Resolve this.
Thanks..
var q =
from s in db.User
join c in db.EmailAccount on s.UserId equals c.UserId
join d in db.POPSettings
on c.PopSettingId equals d.POPSettingsId
where s.UserId == UserId && c.EmailId == EmailId
select new
{
oUserId = s.UserId,
oUserName = s.Name,
oEmailId = c.EmailId,
oEmailAccId = c.EmailAccId,
oPwd = objDecryptor.DecryptIt(c.Password.ToString()),
oServerName = d.ServerName,
oServerAdd = d.ServerAddress,
oPOPSettingId = d.POPSettingsId,
};
If that is LINQ-to-SQL or Entity Framework. You'll need to break it into steps (as it can't execute that at the DB). For example:
var q = from s in db.User
join c in db.EmailAccount on s.UserId equals c.UserId
join d in db.POPSettings on c.PopSettingId equals d.POPSettingsId
where s.UserId == UserId && c.EmailId == EmailId
select new
{
oUserId = s.UserId,
oUserName = s.Name,
oEmailId = c.EmailId,
oEmailAccId = c.EmailAccId,
oPwd = c.Password,
oServerName = d.ServerName,
oServerAdd = d.ServerAddress,
oPOPSettingId = d.POPSettingsId,
};
then use AsEnumerable() to break "composition" against the back-end store:
var query2 = from row in q.AsEnumerable()
select new
{
row.oUserId,
row.oUserName,
row.oEmailId,
row.oEmailAccId,
oPwd = objDecryptor.DecryptIt(row.oPwd),
row.oServerName,
row.oServerAdd,
row.oPOPSettingId
};
var q = from s in db.User
join c in db.EmailAccount on s.UserId equals c.UserId
join d in db.POPSettings on c.PopSettingId equals d.POPSettingsId
where s.UserId == UserId && c.EmailId == EmailId
select new
{
oUserId = s.UserId,
oUserName = s.Name,
oEmailId = c.EmailId,
oEmailAccId = c.EmailAccId,
oPwd = c.Password,
oServerName = d.ServerName,
oServerAdd = d.ServerAddress,
oPOPSettingId = d.POPSettingsId,
};
foreach (var item in q)
{
item.oPwd = objDecryptor.DecryptIt(row.oPwd),
}
we can use a foreach loop also to update a single property. do not need to select all property in next query.
This has nothing about Linq query. you need to debug method objDecryptor.DecryptIt

Categories