SQL to LINQ Expression - sql-server

I have specific SQL expression :
{
select * from courceMCPD.dbo.Contact c
where c.cID in ( select cId from courceMCPD.dbo.Friend f where f.cId=5)
}
i would like to get LINQ expression that gets the same result.
thank you in advance.

That sounds like it's equivalent to something like:
var friendIds = from friend in db.Friends
where friend.ContactId == 5
select friend.ContactId;
var query = from contact in db.Contacts
where friendIds.Contains(contact.Id)
select contact;
(There are lots of different ways of representing the query, but that's the simplest one I could think of.)
It's pretty odd to perform a join on a particular field and also mandate that that field has to have a particular value though... there's not very much difference between that and:
var query = db.Contacts.Where(c => c.Id == 5);
... the only difference is whether there are any friend entries for that particular contact.
EDIT: Smudge gave another option for the query in a comment, so I'm promoting it into this answer...
var query = db.Contacts.Where(c => c.Friends.Any(f => f.cId == 5))
This assumes you've got an appropriate Friends relationship defined in the Contacts entity.

Using labda expressions:
var query = dc.Contact
.Where(c => dc.Friend.Select(f => f.cId).Contains(i.cID))
.Where(c => c.cId == 5);
User "query" syntax:
var query = from c in dc.Contact
where (from f in dc.Friend select f.cID).Contains(c.cId)
where c.cId == 5
select c;

You haven't specified VB/C# so I'm going for VB =P
Dim results As IEnumerable(Of yourEntities.Contact) =
(From c In yourContextInstance.Contacts Where (From f In yourContextInstance.Friends
Where f.cId = 5 Select f.cId).Contains(c.cID))
Clearly, Jon's answer works, this query (I believe) just resembles your T-SQL Closer.

Related

EF6 query that behaves strangely using contains

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();

Can Any one help me to convert this sql query into linq

I need to convert this SQL Query to Link :
"Select * FROM [Register]
where RegisterId IN (SELECT MyId
FROM Friends
WHERE FriendId='" + Session["CurrentProfileId"] + "'
AND Status=1
UNION
SELECT FriendId
FROM Friends
WHERE MyId='" + Session["CurrentProfileId"] + "'
AND Status=1) ";
It may be look like this??? but this is incorrect and having errors
(from u in db.Register
where RegisterId).Contains
(from f in db.Freinds
where f.MyId == Id && m.Status == 1
select new { m.MyId })
.Union(from m in db.Freinds
where f.FreindId == Id && m.Status == 1
select new { m.CreateDate } ));
You have a few problems with the linq above and here are a few:
In the query in the Union you select the CreateDate whereas in the top on you select the MyId. I assume you meant to select FreindId.
In these 2 queries you create an anonymous class instance with the field but then compare it to the RegisterId which is probably a guid/string/int - but for sure not of the type you just created.
You are using the Contains method wrong. Linq syntax can be similar to sql but it is not the same. Check here for Contains
The correct Linq way of doing it is:
var idsCollection = ((from f in db.Freinds
where f.StatusId == 1 && f.MyId == Id
select f.MyId)
.Union(from m in db.Friends
where m.StatusId == 1 && f.FreindId == Id
select m.FriendId)).ToList();
var result = (from u in db.Register
where idsCollection.Contains(u.RegisterId)
select u).ToList();
Notice that the .ToList() is not a must and is here just to ease in debugging. For more information about this .ToList() and Linq in general check MSDN

using LINQ query to get max of a varchar column

I have a Code column of type varchar in database, which includes numbers only. I use the method below to get the next code, and it works fine. But I prefer all the calculations and type conversions to be done on SQL side, to have less overhead. Using the code below, I retrieve all data, which is not needed. Any ideas is really appreciated.
public string GetNextCode(int type)
{
var codeList = (from o in ObjectQuery
where o.Type == Type
select o.Code).ToList<string>();
List<int> list = codeList.Select(int.Parse).ToList();
int nextDL = list.Max() + 1;
return nextDL.ToString();
}
You could try something like the below, it orders the result set descending by the Code and then grabs the first result. Please note I haven't tested this but I think it should work.
var codeList = (from o in ObjectQuery
where o.Type == Type
select o.Code).OrderByDescending(
x => x.Code).First()
Instead of OrderByDescending().First() you could also use OrderBy().Last()
This is a solution I came up with:
var max = (from o in ObjectQuery
let num = ObjectQuery.Where(i => i.Type == Type).Select(i => i.Code).Cast<int>().Max()
select num).FirstOrDefault() + 1;
return max.ToString();

Dynamic column value with group by (sql server and Linq)

i have a table Plan with following sample data
i want to aggregate the result by PlanMonth and for PlanStatus i want that if any of its (in a group) values is Drafted i get drafted in the result and Under Approval otherwise. i have done it using following query
select PlanMonth, case when Flag=1 then 'Drafted' else 'Under Approval' end as PlanStatus
from
(select p.PlanMonth, Max(CASE WHEN p.PlanStatus = 'Drafted' THEN 1 ELSE 0 END) Flag
from Plans p
group by p.PlanMonth
) inquery
i have consulted this blog post. Is there something wrong with it? Moreover, if someone can help me translate it to linq i will be grateful
The query you have will work.
With the sample data you have provided it can be simplified a bit.
select p.PlanMonth,
min(p.PlanStatus) as PlanStatus
from Plans as p
group by p.PlanMonth
This will not work if you have values in PlanStatus that is alphabetically sorted before Drafted.
Following Linq query worked for me
return (from p in db.mktDrVisitPlans
group p by p.PlanMonth into grop
select
new VMVisitPlan
{
PlanMonth = grop.Key
PlanStatus = grop.Any(x=>x.p.PlanStatus == "Drafted")?"Hold":"Under Approval",
}).ToList();

LINQ to SQL Take w/o Skip Causes Multiple SQL Statements

I have a LINQ to SQL query:
from at in Context.Transaction
select new {
at.Amount,
at.PostingDate,
Details =
from tb in at.TransactionDetail
select new {
Amount = tb.Amount,
Description = tb.Desc
}
}
This results in one SQL statement being executed. All is good.
However, if I attempt to return known types from this query, even if they have the same structure as the anonymous types, I get one SQL statement executed for the top level and then an additional SQL statement for each "child" set.
Is there any way to get LINQ to SQL to issue one SQL statement and use known types?
EDIT: I must have another issue. When I plugged a very simplistic (but still hieararchical) version of my query into LINQPad and used freshly created known types with just 2 or 3 members, I did get one SQL statement. I will post and update when I know more.
EDIT 2: This appears to be due to a bug in Take. See my answer below for details.
First - some reasoning for the Take bug.
If you just Take, the query translator just uses top. Top10 will not give the right answer if cardinality is broken by joining in a child collection. So the query translator doesn't join in the child collection (instead it requeries for the children).
If you Skip and Take, then the query translator kicks in with some RowNumber logic over the parent rows... these rownumbers let it take 10 parents, even if that's really 50 records due to each parent having 5 children.
If you Skip(0) and Take, Skip is removed as a non-operation by the translator - it's just like you never said Skip.
This is going to be a hard conceptual leap to from where you are (calling Skip and Take) to a "simple workaround". What we need to do - is force the translation to occur at a point where the translator can't remove Skip(0) as a non-operation. We need to call Skip, and supply the skipped number at a later point.
DataClasses1DataContext myDC = new DataClasses1DataContext();
//setting up log so we can see what's going on
myDC.Log = Console.Out;
//hierarchical query - not important
var query = myDC.Options.Select(option => new{
ID = option.ParentID,
Others = myDC.Options.Select(option2 => new{
ID = option2.ParentID
})
});
//request translation of the query! Important!
var compQuery = System.Data.Linq.CompiledQuery
.Compile<DataClasses1DataContext, int, int, System.Collections.IEnumerable>
( (dc, skip, take) => query.Skip(skip).Take(take) );
//now run the query and specify that 0 rows are to be skipped.
compQuery.Invoke(myDC, 0, 10);
This produces the following query:
SELECT [t1].[ParentID], [t2].[ParentID] AS [ParentID2], (
SELECT COUNT(*)
FROM [dbo].[Option] AS [t3]
) AS [value]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ID]) AS [ROW_NUMBER], [t0].[ParentID]
FROM [dbo].[Option] AS [t0]
) AS [t1]
LEFT OUTER JOIN [dbo].[Option] AS [t2] ON 1=1
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p1 + #p2
ORDER BY [t1].[ROW_NUMBER], [t2].[ID]
-- #p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- #p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- #p2: Input Int (Size = 0; Prec = 0; Scale = 0) [10]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1
And here's where we win!
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p1 + #p2
I've now determined this is the result of a horrible bug. The anonymous versus known type turned out not to be the cause. The real cause is Take.
The following result in 1 SQL statement:
query.Skip(1).Take(10).ToList();
query.ToList();
However, the following exhibit the one sql statement per parent row problem.
query.Skip(0).Take(10).ToList();
query.Take(10).ToList();
Can anyone think of any simple workarounds for this?
EDIT: The only workaround I've come up with is to check to see if I'm on the first page (IE Skip(0)) and then make two calls, one with Take(1) and the other with Skip(1).Take(pageSize - 1) and addRange the lists together.
I've not had a chance to try this but given that the anonymous type isn't part of LINQ rather a C# construct I wonder if you could use:
from at in Context.Transaction
select new KnownType(
at.Amount,
at.PostingDate,
Details =
from tb in at.TransactionDetail
select KnownSubType(
Amount = tb.Amount,
Description = tb.Desc
)
}
Obviously Details would need to be an IEnumerable collection.
I could be miles wide on this but it might at least give you a new line of thought to pursue which can't hurt so please excuse my rambling.

Resources