Optimize EF query using Any instead of contains - c#

That was my query with Contains:
db.NavFilters.Where(finalExpression)
.Count(x => x.Attribute1 == e.Attribute && x.Link == link && x.SubLink == subLink && db.NavItemsFilters
.Where(n=> !(n.Promo == string.Empty || n.Promo == null))
.Select(n=>n.ItemID)
.Contains(x.ItemID) )
But, as far as I know, contains is a hard operation, and I need to optimize it. Is such query will give the same result?
db.NavFilters.Where(x=> db.NavItemsFilters.Any(n=>n.Promo != string.Empty && n.ItemID == x.ItemID))
.Where(finalExpression)
.Count(x => x.Attribute1 == e.Attribute && x.Link == link && x.SubLink == subLink)
I know, that the best solution is to add navigation properties. But I can't do that for many reasons.

You can optimize you query like this:
var navFilters = db.NavFilters.Where(finalExpression);
var thereIsAny = (from x in navFilters
join n in db.NavItemsFilters on x.ItemID equals n.ItemID
where n.Promo != string.Empty && x.Attribute1 == e.Attribute && x.Link == link && x.SubLink == subLink
).Any();

IQueryable.Contains does exist, so your query should get converted to SQL.

Related

EF Core with '&&' and '||' inside lambda expression

The following query is not returning the proper results, it will return properly for company, but not the other two parameters. For clarification this is inside a post method of a page taking the user's input for company, name, and/or state
var transporters = await _db.TransporterProfiles
.Include(x => x.TransportState)
.Where(x => x.Company == company || company == null &&
x => x.LastName == name || name == null &&
x => x.TransportState.Name == state || state == null)
.ToListAsync();
I've tried adding parentheses around each part of the query such as
.Where((x => x.Company == company || company == null) &&
(x => x.LastName == name || name == null) &&
(x => x.TransportState.Name == state || state == null))
but this produces an error
Operator '&&' cannot be applied to operands of type 'lambda expression'
There's no reason to include company == null in the query. If you don't want a search term, don't include it at all. You can build AND conditions by adding Where clauses to a query as needed, eg :
if(value1 != null)
{
query=query.Where(x=>x.Property1 == value1);
}
if(value2 != null)
{
query=query.Where(x=>x.Property2 == value2);
}
In the question's case you can write something like this:
var query=_db.TransporterProfiles.Include(x => x.TransportState).AsQueryable();
if(company!=null)
{
query=query.Where(x => x.Company == company);
}
if(name!=null)
{
query=query.Where(x => x.LastName == name);
}
if(state!=null)
{
query=query.Where(x => x.TransportState.Name == state);
}
var transporters=await query.ToListAsync();
You don't need to include TransportState to use x.TransportState.Name in the Where clause. Include is used to eagerly load related data, not tell EF to JOIN between related tables.
If you don't want Include you can start the query with :
var query=_db.TransporterProfiles.AsQueryable();
The issue with your syntax is you have multiple lambdas that should be one.
.Where(x => (x.Company == company || company == null) &&
(x.LastName == name || name == null) &&
(x.TransportState.Name == state || state == null))
That said the actual solution is to do what #PanagiotisKanavos posted as an answer, generate the query dynamically based on the input values.

EFCore 2.2 No mapping to a relational type can be found for the CLR type 'Expression[]'

I have a project that used to use DBML as the ORM for getting data out of our database. We are in the process of replacing this DBML with Entity Framework Core 2.2.6 (unfortunately the most recent version we can use as we must support old .Net 4 framework code).
I have a block of LINQ that worked using the old DBML code but does not work in the EF Core framework 2.2.6.
The error message I'm getting is:
No mapping to a relational type can be found for the CLR type
'Expression[]'.
List<datadictproperty> dprop = (from dd in dBConnection.datadictproperties
where
dd.entityid == ent.id &&
dd.clientid == clientid &&
((((dd.suppress <= emp.seclevel &&
dd.suppress != Constants.g_everyone_seclevel) ||
dd.suppress == null)) || ignoreSecLevel) ||
(!
(from d in dBConnection.datadictproperties
where
d.entityid == ent.id &&
d.clientid == clientid &&
d.name == dd.name
select new
{
d.name
}).Contains(new { dd.name }) &&
dd.entityid == ent.id &&
dd.clientid == null &&
((((dd.suppress <= emp.seclevel &&
dd.suppress != Constants.g_everyone_seclevel) ||
dd.suppress == null)) || ignoreSecLevel))
orderby
dd.name
select dd).ToList();
One thing I can add which may make this easier is that the following statement should return zero or one row, so FirstOrDefault() may work. At the moment it is returning an Anonymous List<T> type.
(from d in dBConnection.datadictproperties where
d.entityid == ent.id &&
d.clientid == clientid &&
d.name == dd.name
select new
{
d.name
})
The piece of the statement that I think does not work is the following:
d.name == dd.name
select new
{
d.name
}).Contains(new { dd.name })
My issue is that I can't figure out how to change this code to correctly get the same data using EF Core 2.2.6 technology.
Is there anyone who could give me some ideas as to how I can change this code to produce the same data and thereby not generate a runtime error mentioned above?
Any help would be greatly appreciated :)
I have figured out how to fix this code. The end result was to use the following code:
List<datadictproperty> dprop = dBConnection.datadictproperties.Where(
dd =>
dd.entityid == ent.id &&
dd.clientid == clientid &&
((((dd.suppress <= emp.seclevel &&
dd.suppress != Constants.g_everyone_seclevel) ||
dd.suppress == null)) || ignoreSecLevel) ||
(!(dBConnection.datadictproperties.Where(d =>
d.entityid == ent.id &&
d.clientid == clientid &&
d.name == dd.name).Select(d => d.name)).Contains(dd.name) &&
dd.entityid == ent.id &&
dd.clientid == null &&
(((dd.suppress <= emp.seclevel &&
dd.suppress != Constants.g_everyone_seclevel) ||
dd.suppress == null) || ignoreSecLevel))
).OrderBy(dd => dd.name).ToList();
It seems that changing it to the following it worked. I assume because it is not an anonymous type once being selected.
(dBConnection.datadictproperties.Where(d =>
d.entityid == ent.id &&
d.clientid == clientid &&
d.name == dd.name).Select(d => d.name))

Simplify LINQ query

I have the following query:
db.ObjectTags.Where(c =>
c.TagID == tagID &&
(!db.DeletedObjects.Any(d=> d.ForObjectTypeID == c.ForObjectTypeID && d.ForObjectID == c.ForObjectID)
|| !db.DeletedObjects.SingleOrDefault(d => d.ForObjectTypeID == c.ForObjectTypeID && d.ForObjectID == c.ForObjectID).Deleted)
)
Its goal is to return objects that are not in a deleted state.
The table DeletedObjects has two states:
A record doesn't exist (not deleted)
A record exists with a deleted (bool) value
I need to query where either the record doesn't exist, or if it does the deleted value is false.
Is there any way to condense that statement eg with SingleOrDefault()?
You only need one !db.DeletedObjects.Any(...) and no SingleOrDefault
var q = db.ObjectTags
.Where(c=> c.TagID == tagID && !db.DeletedObjects
.Any(d => d.Deleted && d.ForObjectTypeID == c.ForObjectTypeID && d.ForObjectID == c.ForObjectID));
Can you please try this linq query
db.ObjectTags.Where(c =>
c.TagID == tagID &&
(db.DeletedObjects.Any(d=> d.ForObjectTypeID == c.ForObjectTypeID && d.ForObjectID == c.ForObjectID && !c.Deleted))
)
I believe you need to left join between ObjectTags and DeletedObjects. A LINQ query like this:
from objectTag in db.ObjectTags
from deletedObject in db.DeletedObjects
.Where(deletedObject => deletedObject.ForObjectTypeID == objectTag.ForObjectTypeID && deletedObject.ForObjectID == objectTag.ForObjectID)
.DefaultIfEmpty()
where deletedObject == null || !deletedObject.Deleted

Rewrite sql query to LINQ. Can't find an error

This is an SQL query:
SELECT Website,VendorID,Name,LinkProduct,
Link,Logo,Image,NameExtra as Industry,
(SELECT [Percent] FROM Web_Promotion
WHERE Web_Promotion.VendorID=Web_Vendor.VendorID)
AS PercentOff
FROM Web_Vendor WHERE Active='1' AND
(VendorID IN (Select VendorID FROM Web_Promotion
WHERE VendorID<>'' AND Static='True' AND [Percent] <> '0' AND
((Expires>=GETDATE()) OR (Expires IS NULL))) OR
VendorID IN (SELECT TOP 1 SC1 FROM NavItems
WHERE SC1=Web_Vendor.VendorID AND Promotion<>''
AND ((PromotionStart<=GETDATE() AND PromotionEnd>=GETDATE())
OR (PromotionStart<=GETDATE() AND PromotionEnd IS NULL))))
ORDER BY NameExtra,Sequence
I need to rewrite it to LINQ. So this is my LINQ:
return await _db.Web_Vendor.
Where(x => !(x.WebPromotion.VendorID == string.Empty || x.WebPromotion.VendorID == null)
&& x.WebPromotion.Static == true && x.WebPromotion.Percent != 0 &&
(x.WebPromotion.Expires >= DateTime.Now || x.WebPromotion.Expires == null)
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Take(1).Contains(x.WebPromotion.VendorID)))
.Include(x => x.WebPromotion).Where(x => x.Active == true).OrderBy(x => x.NameExtra)
.ThenBy(x => x.Sequence).ToListAsync();
I spent about three ours, but can't find an error. Original SQL query returns 16 rows, but my LINQ code returns only 13 of the. Unfortunately I have only one navigation property (Web_Vendor <-> Web_Promotion). I think that an error in the second part of my query:
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Take(1).Contains(x.WebPromotion.VendorID)))
Can any expert check my code and help me?
Correct data:
http://prntscr.com/9a5xwu
Linq data (not correct) contains the same data as correct instead of values where PercentOff is null.
The main problem is that LINQ generate inner join instead of left join in this place: http://prntscr.com/9a6stb
since you say that your linq data miss the case when PercentOff = null, i'd focus on that
I guess your "PercentOff" in Linq is Percent property, and i see that you have in your where: "x.WebPromotion.Percent != 0"
Is that a nullable value or you convert the null to the default property type, that is 0?
couldn't be that null is converted to 0 and then the query skip it?

Should I keep adding conditions to my LINQ query or add a condition upfront and use two different queries?

I have the following LINQ Query:
var contents = _contentsRepository.GetAll()
.Where(a => a.SubjectId == subjectId &&
a.ContentTypeId == contentTypeId &&
a.ContentStatusId == contentStatusId )
.ToList();
I would like this select to proceed normally unless the contentStatusId == 99. If that's
the case then I want it to retrieve a row from the database with ANY contentStatusId.
Would it be best to do a check of contentStatusId first and then break this down into
two LINQ selects or is there a way I could modify my LINQ query?
Note that I am using SQL Server 2012 and my repository:
public virtual IQueryable GetAll() { return DbSet; }
I believe you can modify your query by adding a contentStatusId == 99 component to your predicate that will short-circuit the evaluation of a.ContentStatusId == contentStatusId like so:
var contents = _contentsRepository.GetAll()
.Where(a => a.SubjectId == subjectId &&
a.ContentTypeId == contentTypeId &&
(contentStatusId == 99 ||
a.ContentStatusId == contentStatusId))
.ToList();
In the normal case everything will work just like before.
In the case when contentStatusId equals 99, there will be overhead of evaluating contentStatusId == 99 for every row, although I think depending on repository you're querying this part could be inlined as a true. You should see for yourself how this impacts performance in your setup.
Try this
var contents = _contentsRepository.GetAll()
.Where(a => contentStatusId == 99 ? (a.SubjectId == subjectId &&
a.ContentTypeId == contentTypeId &&
a.ContentStatusId == contentStatusId) : (a.SubjectId == subjectId &&
a.ContentTypeId == contentTypeId) )
.ToList();
var contents = _contentsRepository.GetAll()
.Where(a =>
{
return a.ContentTypeId == 99 ||
(a.SubjectId == subjectId &&
a.ContentTypeId == contentTypeId &&
a.ContentStatusId == contentStatusId)
}

Categories

Resources