I am not good at Linq expression, today I am running into one weird issue as the below of inner join statement,
var orders = (from q in dao.CurrentDBContext.New_OrderForm
join d in dao.CurrentDBContext.New_OrderGoodsDetail on q.billNum equals d.billNum
select new
{
q.billNum,
q.orderSource,
q.sourceOddNum
d.PPT
}
While I traced the linq statement, I am confused of that Entity Framework will convert the linq statement to the below sql statment
SELECT
[Extent1].[billNum] AS [billNum],
[Extent1].[orderSource] AS [orderSource],
[Extent1].[sourceOddNum] AS [sourceOddNum],
[Extent2].[PPT] AS [PPT]
FROM [dbo].[New_OrderForm] AS [Extent1]
INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2]
ON ([Extent1].[billNum] = [Extent2].[billNum]) OR
(([Extent1].[billNum] IS NULL) AND ([Extent2].[billNum] IS NULL))
Do you know why the below SQL segment did automatically append?
OR (([Extent1].[billNum] IS NULL) AND ([Extent2].[billNum] IS NULL)"
I don't expect that the above automatically append, since it did slow down SQL performance. Any suggestions?
Here is what you can do in case you cannot change the billNum columns to be non nullable.
First, set the option mentioned by #Giorgi
class CurrentDBContext
{
public CurrentDBContext()
{
Configuration.UseDatabaseNullSemantics = true;
// ...
}
}
Then change the LINQ query to not use join, but simple where like this
var orders = (from q in dao.CurrentDBContext.New_OrderForm
from d in dao.CurrentDBContext.New_OrderGoodsDetail
where q.billNum == d.billNum
select ...
The result will be the exact SQL query as the one you've shown (with JOIN!) without the OR part.
It seems that Linq translates q.billNum equals d.billNum is such a way that it also includes a valid match in case both q.billNum and d.billNum are NULL (in SQL NULL is never equal to NULL, hence the OR in your query).
Making both fields non-nullable would be the best solution, provided both fields can never be NULL.
If this is not the case, you could also try to add a where clause in your Linq statement to specifiy that both q.billNum and d.billNum cannot be NULL. With any luck, Linq will recognize that nullable values are not possible.
Note: If you are working with Oracle you should check for empty strings as well as NULL (empty string is equivalent to NULL). Empty strings should be fine as a valid value in SQL Server.
As the above did not help, you could try to write the query yourself. If I'm not mistaking it would be something along the following lines (assuming var is an List<Order> in your example code - the results of your query should match the class you are using):
StringBuilder query = new StringBuilder();
query.AppendLine("SELECT [Extent1].[billNum] AS [billNum],");
query.AppendLine(" [Extent1].[orderSource] AS [orderSource],");
query.AppendLine(" [Extent1].[sourceOddNum] AS [sourceOddNum],");
query.AppendLine(" [Extent2].[PPT] AS [PPT]");
query.AppendLine("FROM [dbo].[New_OrderForm] AS [Extent1]");
query.AppendLine("INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2] ON [Extent1].[billNum] = [Extent2].[billNum]");
List<Order> orders = DbContext.Database.SqlQuery<Order>(query.ToString()).ToList();
I have used similar workarounds to get around performance issues in the past.
If you are using EF6 try setting
context.Configuration.UseDatabaseNullSemantics = true;
and it will not generate NULL checks for those columns.
According to documentation
For example (operand1 == operand2) will be translated as: (operand1 = operand2) if UseDatabaseNullSemantics is true, respectively (((operand1 = operand2) AND (NOT (operand1 IS NULL OR operand2 IS NULL))) OR ((operand1 IS NULL) AND (operand2 IS NULL))) if UseDatabaseNullSemantics is false.
Following on from #Giorgi's answer, the UseDatabaseNullSemantics flag will not work with the equals keyword - only the == operand. Thus in order to get round this and ensure the join on billNum is not part of the OR clause this approach should work (in conjunction with the UseDatabaseNullSemantics flag):
var orders = (from q in dao.CurrentDBContext.New_OrderForm
from d in dao.CurrentDBContext.New_OrderGoodsDetail
where q.billNum == d.billNum
select new
{
q.billNum,
q.orderSource,
q.sourceOddNum
d.PPT
}
This will generate the JOIN without the OR.
Related
Here is the problematic line:
var originalSummaryCandidates =
(from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId &&
variantGenotypeIds.Contains(new int[] {a.VariantId, a.GenotypeId})
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
variantGeotpeIds is of type List<int[]>. Both a.VariantId and a.GenotypeId are of type int.
I cannot figure out why it why it will not do the comparison. Is this a deferred execution issue? It doesn't seem like it should be...
Thanks in advance.
List<T>.Contains only takes a single parameter of type T. In your case, T is Int32 but you're passing in a Int32[].
If you want to check that both values are in the list, you have to break the calls apart:
where d.DrugId == drugId &&
variantGenotypeIds.Contains(a.VariantId) &&
variantGenotypeIds.Contains(a.GenotypeId)
EDIT
If variantGenotypeIds is actually a List<Int32[]>, then there's another issue. LINQ to SQL will try to convert your query into its SQL equivalent. In this case, there's no way to translate your query into SQL so LINQ to SQL will throw an Exception.
If you really need to query this way, you'll have to read the records into memory first and then query using LINQ to Objects (which may or may not be a big deal depending on how many rows you are reading):
var query =
from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants
on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId
select new { a, d }
var originalSummaryCandidates =
(from q in query.AsEnumerable()
where variantGenotypeIds.Contains(new [] { q.a.VariantId, q.a.GenotypeId})
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
Array comparison uses reference equality by default. It's possible that linq-to-sql just tries to translate that into SQL that compares the values, but you'd have to look at the generated SQL to be sure. Another option would be to use Any instead:
where d.DrugId == drugId &&
variantGenotypeIds.Any(v => v[0] == a.VariantId && v[1] == a.GenotypeId)
but I'm not sure if Linq-to-Sql will be able to translate that to the correct SQL either. Another option would be to project the List` to a > and then do a string comparison:
variantGenotypeStrings = variantGenotypeIds.Select(v => string.Format("{0}|{1}", v[0],v[1]);
var originalSummaryCandidates =
(from a in masterDB.tbl_thirty_second_summaries_multi_variant_associations
join d in masterDB.tbl_thirty_second_summaries_multi_variants on a.ThirtySecSummaryId equals d.ThirtySecondSummaryId_this
where d.DrugId == drugId &&
variantGenotypeStrings.Contains(string.Format("{0}|{1}", a.VariantId, a.GenotypeId))
select d.ThirtySecondSummaryId_this)
.Distinct()
.ToList();
I have the following query
var listOfFollowers = (from a in db.UserLinks
where a.TargetUserID == TargetUserID && a.LinkStatusTypeID == 2
join b in db.UserNotifications on (int)a.OriginUserID equals b.TargetUserID
select b);
then I want to update once column on each row ( or object) returned
foreach (var a in listOfFollowers)
{
a.UserNotifications += HappeningID.ToString() + "|";
}
db.SubmitChanges();
The query seems to work , and when I put the generated SQL into SSMS it works fine , but when I run the entire code I get exception for trying to cast to int, don't make too much sense.
Is this ok , to do a query using a join , but only returning one table , change one property, then submitchanges?
The reason you are getting can't cast exception is that in the LINQ statement it is invalid to do cast. Other things which we can't do is to use regular method calls such as toString()
(int)a.OriginUserID is not allowed in
var listOfFollowers = (from a in db.UserLinks
where a.TargetUserID == TargetUserID && a.LinkStatusTypeID == 2
join b in db.UserNotifications on (int)a.OriginUserID equals b.TargetUserID
select b);
This problem will occur because parser tries to convert it into a equivalent SQL but doesn't find any equivalent. Other such cases are when you try to invoke toString(). Several Good responses here:
linq-to-entities-does-not-recognize-the-method
For your current scenario, I believe you have to
1. get the results from first query
2. cast the value
3. Make the second LINQ query
Hope this helps!
Most likely, problem occures in this piece of code (int)a.OriginUserID.
Try remove casting or use SqlFunctions.StringConvert() to compare strings.
I try to convert the following SQL Select statement in Linq2SQL:
SELECT stoptimes.stopid,
trips.tripid,
stoptimes.sequence
FROM trips
INNER JOIN stoptimes
ON stoptimes.tripid = trips.tripid
WHERE ( trips.routeid = '3' )
AND ( trips.endplace = 'END001' )
ORDER BY stoptimes.sequence DESC
It works well but with linq2sql, I get an exception with this following statement:
var first = (from tableTrip in db.Trips
join tableStopTimes in db.StopTimes on tableTrip.TripId equals tableStopTimes.TripId
where tableTrip.RouteId == 3 && tableTrip.EndPlace == "TAEND"
select new
{
tableStopTimes.StopId,
tableStopTimes.Radius,
tableStopTimes.PlaceName,
tableStopTimes.Place,
tableStopTimes.Sequence
}).OrderByDescending(X => X.Sequence).First();
Thanks
The error "Sequence contains no elements" is a result of the collection that you are calling First() on not having anything in it.
You can call FirstOrDefault instead to avoid this issue.
EDIT:
I believe in the case of the anonymous type that you are creating, the default will be null.
I have a members table with columns
member_Id,
member_Lastname,
member_Firstname,
member_Postcode,
member_Reference,
member_CardNum,
and i have another table mshipoptions with columns
mshipoption_id
mshiptype_id
and i have another table mshiptypes
mshiptype_id
mshiptype_name
another table memtomship
memtomship_id
mshipoption_id
member_id
and my entity name is eclipse
at the form load i am filling the datagrid view by using the below method....
private void reportmembers()
{
MemberControlHelper.Fillmembershiptypes(cbGEMembershiptype);
var membersreport = from tsgentity in eclipse.members
join memtomships in eclipse.membertomships on tsgentity.member_Id equals memtomships.member_Id
join mshipoptiions in eclipse.mshipoptions on memtomships.mshipOption_Id equals mshipoptiions.mshipOption_Id
join mshiptypes in eclipse.mshiptypes on mshipoptiions.mshipType_Id equals mshiptypes.mshipType_Id
select
new {
tsgentity.member_Id,
tsgentity.member_Lastname,
tsgentity.member_Firstname,
tsgentity.member_Postcode,
tsgentity.member_Reference,
tsgentity.member_CardNum,
mshiptypes.mshipType_Name,
};
if (txtfirstname.Text != "")
{
dgvmembersrep.DataSource = membersreport.Where(t => t.member_Firstname == txtlastname.Text).ToList();
}
if (txtcardnum.Text != "")
{
dgvmembersrep.DataSource = membersreport.Where(a => a.member_CardNum == txtcardnum.Text).ToList();
}
}
that was fine,...
My problem is here , i have one comboboxsay (cbgemembershiptype)......
when ever the user select the membership type in (cbgemembershiptype) i want retrieve the details of members those who have that membership type....
(Your question is unclear in terms of whether the query you've got already does what you want. I'm assuming it does.)
It's unclear why you'd want to convert this query into one using lambda expressions. It's certainly possible, but each join would introduce a new range variable - by the end, the strictly literal translation would end up with a select involving something like
member_Id = a.b.c.member_Id
... it wouldn't be very readable at all.
There are ways of improving it if you're writing it out by hand, but it still wouldn't be as clear as the query expression.
You should definitely know both forms, and write using whichever form is clearest for the query in question - which in this case is definitely the query expression form.
For more information about how query expressions are translated, see my Edulinq post on that topic.
I have a query which is fully translatable to SQL. For unknown reasons LINQ decides the last Select() to execute in .NET (not in the database), which causes to run a lot of additional SQL queries (per each item) against database.
Actually, I found a 'strange' way to force the full translation to SQL:
I have a query (this is a really simplified version, which still does not work as expected):
MainCategories.Select(e => new
{
PlacementId = e.CatalogPlacementId,
Translation = Translations.Select(t => new
{
Name = t.Name,
// ...
}).FirstOrDefault()
})
It will generates a lot of SQL queries:
SELECT [t0].[CatalogPlacementId] AS [PlacementId]
FROM [dbo].[MainCategories] AS [t0]
SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]
SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]
...
However, if I append another Select() which just copies all members:
.Select(e => new
{
PlacementId = e.PlacementId,
Translation = new
{
Name = e.Translation.Name,
// ...
}
})
It will compile it into a single SQL statement:
SELECT [t0].[CatalogPlacementId] AS [PlacementId], (
SELECT [t2].[Name]
FROM (
SELECT TOP (1) [t1].[Name]
FROM [dbo].[Translations] AS [t1]
) AS [t2]
) AS [Name]
FROM [dbo].[MainCategories] AS [t0]
Any clues why? How to force the LINQ to SQL to generate a single query more generically (without the second copying Select())?
NOTE: I've updated to query to make it really simple.
PS: Only, idea I get is to post-process/transform queries with similar patterns (to add the another Select()).
When you call SingleOrDefault in MyQuery, you are executing the query at that point which is loading the results into the client.
SingleOrDefault returns IEnumerable<T> which is no longer an IQueryable<T>. You have coerced it at this point which will do all further processing on the client - it can no longer perform SQL composition.
Not entirely sure what is going on, but I find the way you wrote this query pretty 'strange'. I would write it like this, and suspect this will work:
var q = from e in MainCategories
let t = Translations.Where(t => t.Name == "MainCategory"
&& t.RowKey == e.Id
&& t.Language.Code == "en-US").SingleOrDefault()
select new TranslatedEntity<Category>
{
Entity = e,
Translation = new TranslationDef
{
Language = t.Language.Code,
Name = t.Name,
Xml = t.Xml
}
};
I always try to separate the from part (selection of the datasources) from the select part (projection to your target type. I find it also easier to read/understand, and it generally also works better with most linq providers.
You can write the query as follows to get the desired result:
MainCategories.Select(e => new
{
PlacementId = e.CatalogPlacementId,
TranslationName = Translations.FirstOrDefault().Name,
})
As far as i'm aware, it's due to how LINQ projects the query. I think when it see's the nested Select, it will not project that into multiple sub-queries, as essentially that would be what would be needed, as IIRC you cannot use multiple return columns from a sub-query in SQL, so LINQ changes this to a query-per-row. FirstOrDefault with a column accessor seems to be a direct translation to what would happen in SQL and therefore LINQ-SQL knows it can write a sub-query.
The second Select must project the query similar to how I have written it above. It would be hard to confirm without digging into a reflector. Generally, if I need to select many columns, I would use a let statement like below:
from e in MainCategories
let translation = Translations.FirstOrDefault()
select new
{
PlacementId = e.CatalogPlacementId,
Translation = new {
translation.Name,
}
})