EF6 query that behaves strangely using contains - sql-server

I have an EF6 SQL Server query that behaves strangely when it is supplied with a List<int> of IDs to use. If bookGenieCategory = a value it works. If selectedAges is empty (count = 0) all is well. If the selectedAges contains values that exist in the ProductCategory.CategoryId column, the contains fails and NO rows are returned.
Note: AllocationCandidates is a view, which works properly on its own.
CREATE VIEW dbo.AllocationCandidate
AS
SELECT
p.ProductID, p.SKU as ISBN, p.Name as Title,
pv.MSRP, pv.Price, pv.VariantID, pv.Inventory,
ISNULL(plt.DateLastTouched, GETDATE()) AS DateLastTouched,
JSON_VALUE(p.MiscText, '$.AgeId') AS AgeId,
JSON_VALUE(p.MiscText, '$.AgeName') AS AgeName
FROM
dbo.Product AS p WITH (NOLOCK)
INNER JOIN
dbo.ProductVariant AS pv WITH (NOLOCK) ON pv.ProductID = p.ProductID
LEFT OUTER JOIN
dbo.KBOProductLastTouched AS plt WITH (NOLOCK) ON plt.ProductID = p.ProductID
WHERE
(ISJSON(p.MiscText) = 1)
AND (p.Deleted = 0)
AND (p.Published = 1)
AND (pv.IsDefault = 1)
GO
Do I have a typo here or a misplaced parenthesis in the following query?
var returnList = (from ac in _db.AllocationCandidates
join pc in _db.ProductCategories on ac.ProductID equals pc.ProductID
where (bookGenieCategory == 0
|| bookGenieCategory == pc.CategoryID)
&&
(selectedAges.Count == 0 ||
selectedAges.Contains(pc.CategoryID))
orderby ac.AgeId, ac.DateLastTouched descending
select ac).ToList();

Firstly, I would recommend extracting the conditionals outside of the Linq expression. If you only want to filter data if a value is provided, move the condition check outside of the Linq rather than embedding it inside the condition. This is generally easier to do with the Fluent Linq than Linq QL. You should also aim to leverage navigation properties for relationships between entities. This way an AllocationCandidate should have a collection of ProductCategories:
var query = _db.AllocationCandidates.AsQueryable();
if (bookGenieCategory != 0)
query = query.Where(x => x.ProductCategories.Any(c => c.CategoryID == bookGenieCategory);
The next question is what does the selectedAges contains? There is an Age ID on the AllocationCandidate, but your original query is checking against the ProductCategory.CategoryId??
If the check should be against the AllocationCandidate.AgeId:
if (selectedAges.Any())
query = query.Where(x => selectedAges.Contains(x.AgeID));
If the check is as you wrote it against the ProductCategory.CategoryId:
if (selectedAges.Any())
query = query.Where(x => x.ProductCategories.Any(c => selectedAges.Contains(c.AgeID)));
Then add your order by and get your results:
var results = query.OrderBy(x => x.AgeId)
.ThenByDescending(x => x.DateLastTouched);
.ToList();

Related

EF6, Linq to entities queries all columns with select new clause

Not sure what is going on here, but my query is using every column even though I'm using a projection.
var students = (from x in db.vStudentProfiles
where x.strLegalFirstName == searchDTO.FirstName &&
x.strLegalMiddleName == searchDTO.MiddleName &&
x.strLegalLastName == searchDTO.LastName &&
x.datBirthDate == searchDTO.DateOfBirth &&
x.intStudentStatusCd != 3
orderby x.intStudentStatusCd ascending, x.datCreated descending
select new
{
x.strLegalFirstName,
x.strLegalLastName,
x.strLegalMiddleName,
x.datBirthDate,
x.intStudentStatusCd,
x.datCreated,
x.strNBEN
}
).ToList();
which results in all columns getting queried:
SELECT
[Project1].[C1] AS [C1],
[Project1].[strLegalFirstName] AS [strLegalFirstName],
[Project1].[strLegalLastName] AS [strLegalLastName],
[Project1].[strLegalMiddleName] AS [strLegalMiddleName],
[Project1].[datBirthDate] AS [datBirthDate],
[Project1].[intStudentStatusCd] AS [intStudentStatusCd],
[Project1].[datCreated] AS [datCreated],
[Project1].[strNBEN] AS [strNBEN]
FROM ( SELECT
[Extent1].[strNBEN] AS [strNBEN],
[Extent1].[strLegalFirstName] AS [strLegalFirstName],
[Extent1].[strLegalMiddleName] AS [strLegalMiddleName],
[Extent1].[strLegalLastName] AS [strLegalLastName],
[Extent1].[datBirthDate] AS [datBirthDate],
[Extent1].[datCreated] AS [datCreated],
[Extent1].[intStudentStatusCd] AS [intStudentStatusCd],
1 AS [C1]
FROM (SELECT
[vStudentProfile].[intStudentId] AS [intStudentId],
[vStudentProfile].[strStudentStatusDescE] AS [strStudentStatusDescE],
[vStudentProfile].[strSPID] AS [strSPID],
[vStudentProfile].[strSPIDPart1] AS [strSPIDPart1],
[vStudentProfile].[strSPIDPart3] AS [strSPIDPart3],
[vStudentProfile].[strSPIDPart4] AS [strSPIDPart4],
........ bunch of other columns
[vStudentProfile].[strStreetDirection] AS [strStreetDirection],
[vStudentProfile].[strProvince] AS [strProvince],
[vStudentProfile].[intStudentStatusCd] AS [intStudentStatusCd],
[vStudentProfile].[intSiteId] AS [intSiteId]
FROM [dbo].[vStudentProfile] AS [vStudentProfile]) AS [Extent1]
WHERE (([Extent1].[strLegalFirstName] = #p__linq__0) OR (([Extent1].[strLegalFirstName] IS NULL) AND (#p__linq__0 IS NULL))) AND (([Extent1].[strLegalMiddleName] = #p__linq__1) OR (([Extent1].[strLegalMiddleName] IS NULL) AND (#p__linq__1 IS NULL))) AND (([Extent1].[strLegalLastName] = #p__linq__2) OR (([Extent1].[strLegalLastName] IS NULL) AND (#p__linq__2 IS NULL))) AND ([Extent1].[datBirthDate] = #p__linq__3) AND (3 <> [Extent1].[intStudentStatusCd])
) AS [Project1]
ORDER BY [Project1].[intStudentStatusCd] ASC, [Project1].[datCreated] DESC
I've also tried
var students = (from x in db.vStudentProfiles
let Child = new
{
x.strLegalFirstName,
.....
orderby x.intStudentStatusCd ascending, x.datCreated descending
select Child
).ToList();
but as I expected didn't make a difference.
Use edmx with a single SQL Server View mapped.
Not sure what is going on here, but my query is using every column even though I'm using a projection.
That's just the way EF generates queries. EF expects the database engine to be smart enough to ignore the columns that are not used. SQL Server is smart enough.

How do I write a conditional (IF DATA EXISTS) LINQ statement without repetition?

I need to write a Linq query where the condition like this:
when data exists then the join will be INNER JOIN
when data is not exists then the join will be LEFT JOIN.
bool ifexists = db.Users.Any(u => u.id== 1);
IQueryable<modelname> query;
if(ifexists == true)
{
query=my inner join linq;
query = from s in query select s;
return query.ToList();
}
else
{
query=my left join linq;
query = from s in query select s;
return query.ToList();
}
Now how I can write this Linq? Is there any way to write this linq in a single Linq statement - I don't want to write the linq twice?
You can conditionally compose on an IQueryable before evaluating it against the database - this will allow you to at least partially DRY up the common actions on the different legs of your branching:
var query = db.SomeTable
.AsQueryable() // Not needed if `db.SomeTable` is already a Queryable
.Where(x => ** common predicates on x ** ));
// NB : Do not materialize here!
if(exists) // Don't need == true
{
query = query
.Where(x => ** branch 1 predicates **));
}
else
{
query = query
.Where(x => ** branch 2 predicates **);
}
return query
.Select(q => ** common fields to project **)
.OrderBy(q => ** common ordering **)
.ToList(); // Materialize right at the end
You Want like That?
bool ifexists = db.Users.Any(u => u.id== 1);
var query=ifexists==true ? (my inner join linq) : (my left join linq);
return query.ToList();
First create object of of database Context named **Objcontext**
now hold the variable in var the query would be like following
var Result = from a in Objcontext.tbl1 where !Objcontext.tbl2.Any(m=>(m.sid==a.aid)&&(m.mid==80))
select a;
If data is exists in the databse it will cheack using !Objcontext
I hope it will helpfull

Get data from a table after joining based on null value of joined table using LINQ

I have some tables like "Job" "AppliedJob" "JobOffer" "Contract" "Employeer"
Description: When employeer post job it is stored in "Job" table with his ID. If a freelancer apply to the job it is stored in "AppliedJob" table with his ID. Then Employeer sees the application and send offer to the freelancer and it is stored in "JobOffer" table. If freelancer accept the offer it is then stored in "Contract" table. At first in "Contract" ContractID, OfferID and StratDate are stored and CompletedDate is stored as null. When the contract is completed the CompletedDate field is modified with date.
Want: I want to return all jobs with no completed contract
I tried:
[HttpGet]
[Route("api/PrivateApi/GetEmployeerPostedJob/")]
public object GetEmployeerPostedJob(int id)
{
var data = (from j in db.Jobs
where j.EmployeerID == id
join apl in db.AppliedJobs
on j.JobID equals apl.JobID
join o in db.JobOffers
on apl.AppliedJobID equals o.AppliedJobID
join con in db.Contracts
on o.OfferID equals con.OfferID
where con.CompletedDate == null
select new
{
j.JobTitle,
j.JobID,
j.Budget,
j.Deadline,
j.Employeer,
j.JobDetails,
j.PublishDate,
j.ReqSkill,
j.NoOFFreelancer,
j.Preference,
Category1=j.Category,
totalAppliedFreelancer=(from aple in db.AppliedJobs where j.JobID ==aple.JobID select aple).Count(),
Category = (from gg in db.Categories where gg.CategoryID == j.Category select gg.CategoryName).FirstOrDefault()
}).ToList();
return data.AsEnumerable();
}
But it returns no jobs.
How can i get all jobs which are not completed yet(CompletedDate == null in Contract table)?
I want to show the employeer's posted jobs on his page but only those jobs that are not completed
The above (die to one to many relationships involved) can be paraphrased as
return all jobs with no completed contract
while the way you wrote the query, besides the possible data duplication, it answers the question
return all jobs with existing, but not completed contract
i.e. is missing the jobs w/o applied job, applied job w/o offer and offer w/o contract.
The correct query would be something like this:
from job in db.Jobs
where job.EmployeerID == id
join jobContract in (
from appliedJob in db.AppliedJobs
join offer in db.JobOffers on appliedJob.AppliedJobID equals offer.AppliedJobID
join contract in db.Contracts on offer.OfferID equals contract.OfferID
select new { appliedJob, offer, contract }
) on job.JobID equals jobContract.appliedJob.JobID into jobContracts
where !jobContracts.Any(jobContract => jobContract.contract.CompletedDate != null)
select ...
The query could further be simplified by using navigation properties, but since I don't see navigation properties between AppliedJob and JobOffer, I'm leaving that for you.
Update: Here is the same query with navigation properties (by simplified I meant no need for join operators):
from job in db.Jobs
let completedContracts =
from appliedJob in job.AppliedJobs
from offer in appliedJob.JobOffers
from contract in offer.Contracts
where contract.CompletedDate != null
select contract
where !completedContracts.Any()
select ...
This LINQ query doesn't work on live server (sql-2008) But works on my local VS 2013. Is there any mistake if my live database is empty at first time?
#Ivan Stoev
public object BrowseJobs()
{
var skills = db.Skills.ToDictionary(d => d.SkillID, n => n.SkillName);
var jobData = (from j in db.Jobs where j.Preference==2
//from cj in j.ClosedJobs.DefaultIfEmpty()
join cj in db.ClosedJobs.DefaultIfEmpty()
on j.JobID equals cj.JobID into closedJob
where !closedJob.Any()
join c in db.Categories on j.Category equals c.CategoryID
join jobContract in
(
from appliedJob in db.AppliedJobs.DefaultIfEmpty()
from offer in appliedJob.JobOffers.DefaultIfEmpty()
from contract in db.Contracts.DefaultIfEmpty()
select new { appliedJob, offer, contract }
).DefaultIfEmpty()
on j.JobID equals jobContract.appliedJob.JobID into jobContracts
where !jobContracts.Any(jobContract => jobContract.contract.CompletedDate != null)
select new
{
JobTitle = j.JobTitle,
JobID = j.JobID,
ReqSkillCommaSeperated = j.ReqSkill,
Category = c.CategoryName,
Budget=j.Budget,
Deadline=j.Deadline,
JobDetails=j.JobDetails,
PublishDate=j.PublishDate,
TotalApplied=(from ap in db.AppliedJobs where j.JobID == ap.JobID select ap.AppliedJobID).DefaultIfEmpty().Count()
}).AsEnumerable()
.Select(x => new
{
JobID = x.JobID,
JobTitle = x.JobTitle,
Category = x.Category,
Budget = x.Budget,
Deadline = x.Deadline,
JobDetails = x.JobDetails,
PublishDate = x.PublishDate,
SkillNames = GetSkillName(x.ReqSkillCommaSeperated, skills),
TotalApplied = (from ap in db.AppliedJobs where x.JobID == ap.JobID select ap.AppliedJobID).DefaultIfEmpty().Count()
}).ToList();
return jobData.AsEnumerable();
}

SQL Query Help - searching on multiple 'pairs' or data

I'm struggling to work out how to do a SQL query on a database that I have.
I have a view (which can be changed) which shows the relationships between the tables.
This creates a view as follows:
What I need to be able to do is search on one or more 'Attribute Pairs'
for example
I want to search for records with:
(
(AttributeName='FileExtension' AND AttributeValue='.pdf')
AND (AttributeName='AccountNumber' AND AttributeValue='ABB001'
)
As you can tell, this is not working as AttributeName cant be two things at once. I have this working with an OR filter, but I want it to find records that have all attribute pairs
SELECT
dbo.SiconDMSDocument.SiconDMSDocumentID,
dbo.SiconDMSAttribute.SiconDMSAttributeID,
dbo.SiconDMSAttribute.AttributeFriendlyName,
dbo.SiconDMSAttribute.AttributeName,
dbo.SiconDMSDocumentAttribute.AttributeValue,
dbo.SiconDMSAttribute.DataType,
dbo.SiconDMSDocumentType.SiconDMSDocumentTypeID,
dbo.SiconDMSDocumentType.DocumentTypeName,
dbo.SiconDMSDocumentType.DocumentTypeFriendlyName,
dbo.SiconDMSModule.SiconDMSModuleID,
dbo.SiconDMSModule.ModuleName,
dbo.SiconDMSModule.ModuleFriendlyName,
dbo.SiconDMSDocument.SiconDMSDocumentTypeModuleID
FROM dbo.SiconDMSDocument
INNER JOIN dbo.SiconDMSDocumentAttribute ON dbo.SiconDMSDocument.SiconDMSDocumentID = dbo.SiconDMSDocumentAttribute.SiconDMSDocumentID
INNER JOIN dbo.SiconDMSAttribute ON dbo.SiconDMSDocumentAttribute.SiconDMSAttributeID = dbo.SiconDMSAttribute.SiconDMSAttributeID
AND
(
(dbo.SiconDMSAttribute.AttributeName = 'Reference' AND dbo.SiconDMSDocumentAttribute.AttributeValue='12345')
OR (dbo.SiconDMSAttribute.AttributeName = 'AccountNumber' AND dbo.SiconDMSDocumentAttribute.AttributeValue='ABB001')
)
INNER JOIN dbo.SiconDMSDocumentTypeModule ON dbo.SiconDMSDocument.SiconDMSDocumentTypeModuleID = dbo.SiconDMSDocumentTypeModule.SiconDMSDocumentTypeModuleID
INNER JOIN dbo.SiconDMSDocumentType ON dbo.SiconDMSDocumentTypeModule.SiconDMSDocumentTypeID = dbo.SiconDMSDocumentType.SiconDMSDocumentTypeID
INNER JOIN dbo.SiconDMSModule ON dbo.SiconDMSDocumentTypeModule.SiconDMSModuleID = dbo.SiconDMSModule.SiconDMSModuleID
WHERE
(dbo.SiconDMSDocument.Deleted = 0)
AND (dbo.SiconDMSDocumentAttribute.Deleted = 0)
AND (dbo.SiconDMSAttribute.Deleted = 0)
AND (dbo.SiconDMSDocumentType.Deleted = 0)
AND (dbo.SiconDMSDocumentTypeModule.Deleted = 0)
AND (dbo.SiconDMSModule.Deleted = 0)
Are there any SQL functions that will allow me to do something like this?
I'm not sure what your complicated query has to do with the question of searching for attribute pairs.
Assuming you want the document ids that have both attributes:
select SiconDMSDocumentID
from yourview y
where (AttributeName = 'FileExtension' AND AttributeValue = '.pdf') or
(AttributeName = 'AccountNumber' AND AttributeValue = 'ABB001'
group by SiconDMSDocumentID
having count(*) = 2;
Or, if the attributes could have multiple values:
having count(distinct AttributeName) = 2

Zend DB Select nested Joins

I'm trying to realize the following query with zend db select:
SELECT `uac`.`uid`, `u`.`uid`, `g`.`groupid`, `g`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT JOIN (`user_in_group` AS `uig`
INNER JOIN `groups` AS `ag` ON (ag.groupid = uig.groupid) AND (ag.packageid = 2)
) AS `g` ON uac.uid = g.uid
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
GROUP BY `u`.`uid`
I got the following, but got stuck when trying to convert the nested join to zend structure:
$select = $db->select()->from(array('uac' => 'user_has_data'), array('uac.uid'))
->join(array('u' => 'users'), 'u.uid = uac.uid', array('uid', 'uname'))
->joinLeft(array('uig' => 'user_in_groups'), 'uig.uid = uid', array('agid' => 'uig.groupid'))
->join(array('ag' => 'groups'), '(ag.agid = uig.groupid) AND ( ag.packageid = '.$packageid.')', array('packageid'))
->where('uac.dataid = ?', $dataid)
->where('(u.uname LIKE ?)', $value)
->group('u.uid');
Is it possible to get the given sql query into a suitable structure for zend db select? I need a select object for further handling in a paginator, so if this is not possible I have to make a straight forward sql query.
I don't believe you can do a nested join like that with Zend_Db_Select. Your best bet is to create your own paginator adapter (it's easier than you might think, look at the Select one) and manage the LIMIT part of the SQL yourself.
I know the question is old, but i think people might still benefit from the right answer for this question.
That nested join is the same as
SELECT * FROM `user_in_group` AS `uig`
INNER JOIN `groups` AS `ag` ON (ag.groupid = uig.groupid) AND (ag.packageid = 2)
So this should work just fine.
$subquery = $db->select()
->from(array('uig' => 'user_in_groups'),'*')
->joinInner(array('ag'=>'groups'),'(ag.groupid = uig.groupid) and (ag.packegeid = 2)',array('*'));
$select = $db->select()->from(array('uac' => 'user_has_data'), array('uac.uid'))
->join(array('u' => 'users'), 'u.uid = uac.uid', array('uid', 'uname'))
->joinLeft('g'=>$subquery), 'uac.uid = g.uid', array('g.groupid', 'g.packageid'))
->where('uac.dataid = ?', $dataid)
->where('(u.uname LIKE ?)', $value)
->group('u.uid');

Resources