Lambda not in like SQL - asp.net-mvc

I’m working with MVC 5 and I’m quite new on it. How can convert the below sql code to lambda expression. Basically what I’m trying to do is to show all records that ProductID not exist in Scrap Table
Select * from Product
where ProductID not in (Select ProductID from Scrap where ref = '123')
and active = 1

I believe your query is equivalent to:
select p.*
from Product p
join Scrap s on p.ProductID = s.ProductID
where
s.ref <> '123'
and p.active = 1
If so, try this:
from p in db.Products
join s in db.Scraps on p.ProductID equals s.ProductID
where s.Ref != "123" && p.Active == 1
select p

Try
var query =
from p in db.Products
where !(from s in db.Scrap
where s.ref == '123'
select s.ProductId)
.Contains(p.ProductId)
&& p.Active = 1

Try something like:
var products = (from p in db.Products //db is an instance of my datacontext
where !db.Scrap.Any(s => s.ProductId == p.ProductId && s.ref == "123")
&& p.active == 1 // p.active == true if active is of type bit in sql
select p);

Related

Converting Sql to Linq (to Entities)

I have a query like that and it works normally. If a student gets more than one course, it list all of them:
SELECT ks.KullaniciKodu as username, ks.Sifre as password, k.adi as firstname, k.soyadi as lastname, k.Email as email,
MAX(CASE WHEN c.DERSKODU = 'ENF100' THEN 'ENF100' ELSE '' END)As course1,
MAX(CASE WHEN c.DERSKODU = 'ATA101' THEN 'ATA101' ELSE '' END) As course2,
MAX(CASE WHEN c.DERSKODU = 'TDB101' THEN 'TDB101' ELSE '' END) As course3,
MAX(CASE WHEN c.DERSKODU = 'İNG101' THEN 'İNG101' ELSE '' END) As course4
FROM Kayit k
JOIN DersNotu dn ON dn.KayitNo = k.KayitNo
JOIN Ders c ON c.DersKayitNo = dn.DersKayitNo AND c.DERSKODU IN ('ENF100','ATA101','TDB101', 'İNG101') AND c.DONEM = '201512'
JOIN KullaniciSifre ks ON ks.KullaniciKodu = k.KullaniciKodu
GROUP BY ks.KullaniciKodu, ks.Sifre, k.adi, k.soyadi, k.Email
But when i convert it to Linq (to Entity), it only gets one course or all courses are null. Where is the difference and problem in this code?
from k in db.Kayit
join dn in db.DersNotu on k.KayitNo equals dn.KayitNo
join c in db.Ders on dn.DersKayitNo equals c.DersKayitNo
join ks in db.KullaniciSifre on k.KullaniciKodu equals ks.KullaniciKodu
where
(new string[] { "ENF100", "ATA101", "TDB101", "İNG101" }).Contains(c.DersKodu) &&
c.Donem == 201512
group new { ks, k, c } by new
{
ks.KullaniciKodu,
ks.Sifre,
k.Adi,
k.Soyadi,
k.Email
} into g
select new
{
KullaniciKodu = g.Key.KullaniciKodu,
Sifre = g.Key.Sifre,
Adi = g.Key.Adi,
Soyadi = g.Key.Soyadi,
Email = g.Max(p => (
p.k.Email == string.Empty ? "NULL" : p.k.Email)),
Course1 = g.Max(p => (
p.c.DersKodu == "ENF100" ? "ENF100,1" : "NULL,1")),
Course2 = g.Max(p => (
p.c.DersKodu == "ATA101" ? "ATA101,1" : "NULL,1")),
Course3 = g.Max(p => (
p.c.DersKodu == "TDB101" ? "TDB101,1" : "NULL,1")),
Course4 = g.Max(p => (
p.c.DersKodu == "İNG101" ? "İNG101,1" : "NULL,1"))
}
Using MAX in SQL is just a trick to work-around the problem. You should not convert that explicitly to LINQToEntity. The problem here is you use Max against strings. In your case the NULL,1 seems be always selected as max except for the TDB101,1.
The actual logic here is such as for ENF100, if any one found, project for "ENF100", otherwise project for "NULL,1". The same logic for others. So it should be like this:
Course1 = g.Any(p => p.c.DersKodu == "ENF100") ? "ENF100,1" : "NULL,1",
Course2 = g.Any(p => p.c.DersKodu == "ATA101") ? "ATA101,1" : "NULL,1",
Course3 = g.Any(p => p.c.DersKodu == "TDB101") ? "TDB101,1" : "NULL,1",
Course4 = g.Any(p => p.c.DersKodu == "İNG101") ? "İNG101,1" : "NULL,1"
Also the Email is projected wrong, it is already included in the group key, so you can just project it via the group key:
Email = g.Key.Email

How to get distinct value from the list

I have a List generated from Linq to Entities query. In which, I need to get a unique records based on BibId. I have tried changing the query but no help to get the unique records based on BibId.
Query
aa.NewBibContentsModel = (from x in db.BibContents
where (x.TagNo == "245" && x.NormValue == aa.CurrentTitle) || (x.TagNo == "020" && x.NormValue == aa.CurrentISBN) || (x.TagNo == "022" && x.NormValue == aa.CurrentISBN)
select new
{
BibId = x.BibId,
Title = (from a in db.BibContents where a.BibId == x.BibId && a.TagNo == "245" orderby a.Id ascending select a.NormValue),
//Tit = (from a in db.BibContents where a.BibId == line.BibId && a.TagNo == "245" && a.Sfld == "a" select a.NormValue).FirstOrDefault(),
Author = (from a in db.BibContents where a.BibId == x.BibId && splitted.Contains(a.TagNo) && a.NormValue != null select a.TagNo).FirstOrDefault(),
ISBN = (from a in db.BibContents where a.BibId == x.BibId && a.NormValue != null && (a.TagNo == "020" || a.TagNo == "022") orderby a.Id ascending select a.NormValue)
}).AsEnumerable().Select(x => new BibContentsModel
{
BibId = x.BibId,
Title = string.Join(" ", x.Title),
Author = string.Join(" ", (from a in db.BibContents where a.BibId == x.BibId && a.TagNo == x.Author orderby a.Id select a.NormValue)),
ISBN = string.Join(" ", x.ISBN)
}).ToList();
Any help to this problem will be appreciated.
Thanks
What you're trying to achieve is know as Distinct By. MoreLinq has a function for it. The syntax would look like:
(from x in db.BibContentsNo == "022")
... // your query
}).AsEnumerable()
.DistinctBy(x => x.BibId) // <= MoreLinq
What is does is group the records by BibId and take the first element of each group.
You can download MoreLinq as a NuGet package.

Speeding up a linq query

I am trying to replicate the following SQL query with linq. On SQL Server it takes a fraction of a second to run:
select g.reference, count(*)
from isis.dbo.[group] as g inner join
isis.dbo.enrolment as e on g.groupid = e.groupid inner join
isis.dbo.student as s on e.studentid = s.studentid inner join
isis.dbo.progression as p on s.studentid = p.studentid
where p.academicyear = '12/13' and g.istutorgroup = 1
group by reference
In my MVC application I am passing a listing of "TutorGroups" to the view. For each tutor group in the view I need to display various information about them, one item being the number of "Progression" interviews they have had.
I have tried a couple of methods but they both take upwards of 30 secs to run in my MVC application:
<%TTMrequired = tg.Enrolments
.SelectMany(e => e.Student.Progressions
.Where(p => p.TTMChecked == false &&
p.TTMInterview == true &&
p.AcademicYear == year))
.Count(); %>
and
<%TTMrequired = tg.Enrolments
.Where(e => e.Student.Progressions
.Any(p => p.TTMChecked == false &&
p.TTMInterview == true &&
p.AcademicYear == year))
.Count(); %>
Anyone got any suggestions on how I can speed this up? I suspect the problem is me trying to do it a stupid way - it usually is!
You could try doing a Sum of counts instead of a SelectMany:
tg.Enrolments.Sum(e => e.Student.Progressions
.Count(p => p.TTMChecked == false &&
p.TTMInterview == true &&
p.AcademicYear == year)
);
Should be written using this syntax:
var TTMRequired = (from g in tg.Groups
join e in tg.Enrolment on g.groupid equals e.groupid
join s in tg.Students on e.studentid equals s.studentid
join p in tg.Progressions on s.studentid = p.studentid
where p.academicyear.Equals("12/13") && g.istutorgroup.Equals(1)
group g by g.reference into grp
select new {
grpRef = grp.Key,
grpCount = grp.Count()
});
Note: if g.istutorgroup is of type BIT instead of INT, consider using .Equals(true).

Return Original datatype after LINQ group by

I need my LINQ Query to return the Product Datatype, after being grouped. It seems to be encased into an anonymous psuedo family.
I have some properties in Product that I don't care about, just needing the p.ID and p.Name etc.
The error I'm getting at the moment with this is:
The entity or complex type 'DatabaseModel.Product' cannot be constructed in a LINQ to Entities query.
This is my Method:
public static List<Product> GetSellableSpecialOffers(DatabaseEntities db)
{
var query = from p in db.Products
where (p.Visible == true && p.Active == true)
&& p.ShowInSpecialOffers == true
group p by p.FamilyID == null ? 0 : p.FamilyID into f
select new Product {
ID = f.First().ID,
Name = f.First().Name,
Cost = f.First().Cost,
RRP = f.First().RRP
};
return query.ToList();
}
What is the problem? Is there a better way around this? SQL would always return 1 record instead of encasing the object in a secondary datatype, I don't get it.
Many thanks,
EDIT 1:
My apologies for extending the specification, but I need the returned product to be programatically generated e.g.
select new Product {
ID = f.First().ID,
Name = f.First().Name,
Cost = f.OrderBy(p => p.NowCost).FirstOrDefault(),
RRP = f.First().RRP
}
or if I could strongly type the family class:
public partial class ProductFamily
{
public Product GroupedProduct
{
get
{
return this.Products.OrderBy(p => p.NowCost).FirstOrDefault();
}
}
}
Then I would do:
var query = (from p in db.Products
where (p.Visible == true && p.Active == true)
&& p.ShowInSpecialOffers == true
group p by p.ProductFamily == null ? null : p.ProductFamily into f
select f.GroupedProduct).ToList<Product>();
But I can't get either solution to work with what I have.
You can try (boring, but not sure you have the choice)
var query = from p in db.Products
where (p.Visible == true && p.Active == true)
&& p.ShowInSpecialOffers == true
group p by p.FamilyID == null ? 0 : p.FamilyID into f
select new {
ID = f.First().ID,
Name = f.First().Name,
Cost = f.OrderBy(m => m.NowCost).First().Cost,
RRP = f.First().RRP
};
return query.ToList().Select(m =>
new Product {
ID = m.ID,
Name = m.Name,
Cost = m.Cost,
RRP = m.RRP
};
EDIT
Or as pointed by Master Skeet (not exactly the same as what you tried, but much easier)
var query = from p in db.Products
where (p.Visible == true && p.Active == true)
&& p.ShowInSpecialOffers == true
group p by p.FamilyID == null ? 0 : p.FamilyID into f
select f.OrderBy(m => m.NowCost).First();
You could simply select f.First() and return the resulting data, like this:
var query = from p in db.Products
where (p.Visible == true && p.Active == true)
&& p.ShowInSpecialOffers == true
group p by p.FamilyID == null ? 0 : p.FamilyID into f
select f.First();
Then outside the method use whatever properties you need, ignoring what you don't care about (you are handling Product objects, properties should be there even if you don't need them).
Remove the word 'Product' from
select new Product{}
'select new' itself will create an anonymous type whereas you are specifying it as a Product.

Problem with two Doctrine request

Hello I have a little problem with requests : In an action executeFiche, I have three requests
public function executeFiche(sfWebRequest $request){
// Récupération du logement correspondant à l'ID passé dans l'URL
$this->forward404Unless($this->logement = Doctrine::getTable('Logement')->find(array($request->getParameter('id'))), sprintf('Object logement does not exist (%s).', $request->getParameter('id')));
// Récupération du (ou des) locataire(s) actuel(s) du logement
$locataires = Doctrine::getTable('Logement')->createQuery('l')
->leftJoin('l.Bail b')
->leftJoin('b.Locataire')
->where('l.id = ?', $request->getParameter('id'))
->andWhere('(b.datefin >= ?', date('Y-m-d', time()))
->orWhere('b.datefin = 0000-00-00)')
->execute();
// Récupération du (ou des) locataire(s) précédent(s) du logement
$locatairesprec = Doctrine::getTable('Logement')->createQuery('l')
->leftJoin('l.Bail b')
->leftJoin('b.Locataire')
->where('l.id = ?', $request->getParameter('id'))
->andWhere('b.datefin < ?', date('Y-m-d', time()))
->andWhere('b.datefin != 0000-00-00')
->orderBy('datedeb')
->execute();
$this->locataires = $locataires;
$this->locatairesprec = $locatairesprec;
}
The problem is my two requests (the first is alright) hinder themselves and the result returned is wrong.
Edit : SQL request
SELECT l.id AS l__id, l.adresse AS l__adresse, l.montee AS l__montee, l.etage AS
l__etage, l.numetage AS l__numetage, l.numlogt AS l__numlogt, l.taille AS l__taille,
l.surfacehab AS l__surfacehab, l.typelog AS l__typelog, l.intergen AS l__intergen,
l.ascenseur AS l__ascenseur, l.ascenseuracc AS l__ascenseuracc, l.accessibl AS
l__accessibl, l.adaptable AS l__adaptable, l.adapte AS l__adapte, l.chauffage AS
l__chauffage, l.chargeschauf AS l__chargeschauf, l.chargeseauch AS l__chargeseauch,
l.chargeseaufr AS l__chargeseaufr, l.reservataire AS l__reservataire, l.loyer AS
l__loyer, l.loyercc AS l__loyercc, l.commentaires AS l__commentaires, l.created_at AS
l__created_at, l.updated_at AS l__updated_at, b.id AS b__id, b.locataire AS b__locataire,
b.logement AS b__logement, b.datedeb AS b__datedeb, b.datefin AS b__datefin, b.colloc AS
b__colloc, b.bailglissant AS b__bailglissant, l2.nud AS l2__nud, l2.titre AS l2__titre,
l2.nom AS l2__nom, l2.prenom AS l2__prenom, l2.nationalite AS l2__nationalite,
l2.datenaissance AS l2__datenaissance, l2.statutmatri AS l2__statutmatri, l2.statutpro AS
l2__statutpro, l2.nbenfants AS l2__nbenfants, l2.monoparental AS l2__monoparental,
l2.numprec AS l2__numprec, l2.rueprec AS l2__rueprec, l2.quartierprec AS l2__quartierprec,
l2.codepostalprec AS l2__codepostalprec, l2.villeprec AS l2__villeprec, l2.statutlogprec
AS l2__statutlogprec FROM logement l LEFT JOIN bail b ON l.id = b.logement LEFT JOIN
locataire l2 ON b.locataire = l2.nud WHERE (l.id = '1' AND (b.datefin >= '2010-07-01' OR
b.datefin = '0000-00-00'))
0.03s, "doctrine" connection
#
SELECT l.id AS l__id, l.adresse AS l__adresse, l.montee AS l__montee, l.etage AS
l__etage, l.numetage AS l__numetage, l.numlogt AS l__numlogt, l.taille AS l__taille,
l.surfacehab AS l__surfacehab, l.typelog AS l__typelog, l.intergen AS l__intergen,
l.ascenseur AS l__ascenseur, l.ascenseuracc AS l__ascenseuracc, l.accessibl AS
l__accessibl, l.adaptable AS l__adaptable, l.adapte AS l__adapte, l.chauffage AS
l__chauffage, l.chargeschauf AS l__chargeschauf, l.chargeseauch AS l__chargeseauch,
l.chargeseaufr AS l__chargeseaufr, l.reservataire AS l__reservataire, l.loyer AS l__loyer,
l.loyercc AS l__loyercc, l.commentaires AS l__commentaires, l.created_at AS l__created_at, l.updated_at AS l__updated_at, b.id AS b__id, b.locataire AS b__locataire,
b.logement AS b__logement, b.datedeb AS b__datedeb, b.datefin AS b__datefin, b.colloc AS
b__colloc, b.bailglissant AS b__bailglissant, l2.nud AS l2__nud, l2.titre AS l2__titre,
l2.nom AS l2__nom, l2.prenom AS l2__prenom, l2.nationalite AS l2__nationalite,
l2.datenaissance AS l2__datenaissance, l2.statutmatri AS l2__statutmatri, l2.statutpro AS
l2__statutpro, l2.nbenfants AS l2__nbenfants, l2.monoparental AS l2__monoparental,
l2.numprec AS l2__numprec, l2.rueprec AS l2__rueprec, l2.quartierprec AS l2__quartierprec,
l2.codepostalprec AS l2__codepostalprec, l2.villeprec AS l2__villeprec, l2.statutlogprec
AS l2__statutlogprec FROM logement l LEFT JOIN bail b ON l.id = b.logement LEFT JOIN
locataire l2 ON b.locataire = l2.nud WHERE (l.id = '1' AND b.datefin < '2010-07-01' AND
b.datefin != '0000-00-00') ORDER BY datedeb
EDIT
Thanks for this answer,
But when I want to put the queries in my model, I have others problems : I have an error, with '$request->getParameter('id')'. I exchange it to '$this->getId()' and Doctrine tell me I have an error.
For the parenthesis, I close them in the next. I don't know another way to generate SQL with order in the where. It is to have :
WHERE l.id = $request->getParameter('id') AND ( b.datefin >= date('Y-m-d', time()) OR b.datefin = 0000-00-00 )
Edit : I still have my problem. When the second request have something to return, the first doesn't return all the entries
As a good symfony practice, you could start by putting the queries in a model (something like LogementTable.class.php).
You have also some syntax problems in your queries.
Update : I didn't notice the parenthesis is closed on the line after
In the following line, you open the parenthesis but it isn't closed after :
->andWhere('(b.datefin >= ?', date('Y-m-d', time()))
Second error, in SQL dates must be surrounded by quotes :
->orWhere("b.datefin = '0000-00-00')")
// ...
->andWhere("b.datefin != '0000-00-00'")
Update 2 :
Try this as your second request :
$locatairesprec = Doctrine::getTable('Logement')->createQuery('l')
->leftJoin('l.Bail b')
->leftJoin('b.Locataire')
->where('l.id = ?', $request->getParameter('id'))
->andWhere('(b.datefin < ?', date('Y-m-d', time()))
->andWhere("b.datefin != '0000-00-00')")
->orderBy('datedeb')
->execute();

Resources