Linq where clause finding for attribute on wrong entity - c#

Hi I have the following Linq query:
(from c in new_contactsubscriptionSet
join p in new_PaymentStatusSet
on c.new_PaymentStatusId.Id equals p.new_PaymentStatusId
where (c.new_EndDate > DateTime.Now &&
c.new_EndDate <= DateTime.Now.AddDays(14)) &&
p.new_IsPaidStatus == false
select c)
It throws the following FaultException which means its checking attribute new_ispaidstatus on wrong entity. It should be checking on new_PaymentStatus rather then new_contactsubscription
FaultException
'new_contactsubscription' entity doesn't contain attribute with Name = 'new_ispaidstatus'.
If I use the following queries its working fine:
(from c in new_contactsubscriptionSet
join p in new_PaymentStatusSet
on c.new_PaymentStatusId.Id equals p.new_PaymentStatusId
where p.new_IsPaidStatus == false
select c)
OR
(from c in new_contactsubscriptionSet
join p in new_PaymentStatusSet
on c.new_PaymentStatusId.Id equals p.new_PaymentStatusId
where (c.new_EndDate > DateTime.Now &&
c.new_EndDate <= DateTime.Now.AddDays(14))
select c)
Looks like something is wrong with the Where clause. Can anyone help me out to fix this query.
Thanks in Advance

You'll need a different where for each entity.
(from c in new_contactsubscriptionSet
join p in new_PaymentStatusSet
on c.new_PaymentStatusId.Id equals p.new_PaymentStatusId
where (c.new_EndDate > DateTime.Now && c.new_EndDate <= DateTime.Now.AddDays(14))
where p.new_IsPaidStatus == false
select c)
This is due to the way Microsoft maps the Linq query to a Query Expression. It attempts to map a where into it's only filter criteria, but these are applied on an entity to entity basis. So it is determining the names of all the attributes used, and creating a filter for it against the first expression it evaluates' entity.
With the multiple where's, it'll update the filter of the second Linked Entity rather than blindly adding it to the first.

Related

Sql to LINQ 'where in' query

I'm trying to convert this SQL query to LINQ
select * from fichepfe where valid = 1 and id_fiche in ( select fiche_id from enseignant_fiche where id_ens = '*variable*');
This query can return multiple rows.
This is what I tried but I keep getting this error
Operator '==' cannot be applied to operands of type 'method group' and 'IQueryable'
What I tried:
var fiches = (from fiche in _context.Fichepfes where fiche.Valid == true && fiche.IdFiche ==
(from fens in _context.enseignant_fiche where IdEns == *variable*
select fens.ficheId )
select fiche ).ToList();
Thanks in advance.
This should work:
var fiches =
(from fiche in _context.Fichepfes where
fiche.Valid == true &&
_context.enseignant_fiche.Any(fens => fens.IdEns == *variable* && fens.ficheId == fiche.IdFiche)
).ToList();
However this may cause part of the query to be executed by the client, since LINQ-to-SQL may be unable to convert the .Any() call to a raw query.
A better way would be to use a join:
var fiches =
(from fens in _context.enseignant_fiche where fens.IdEns == ens
join fiche in _context.Fichepfes on fens.ficheId equals fiche.IdFiche
where fiche.Valid
select fiche.IdFiche).ToList();
The join can be done the other way around, too, but it's around 5 to 6 times slower on my computer.
var fiches =
(from fiche in _context.Fichepfes where fiche.Valid
join fens in _context.enseignant_fiche on fiche.IdFiche equals fens.ficheId
where fens.IdEns == ens
select fiche.IdFiche).ToList();
So you have a table of Fiches (FichePfes), and a table of EnseignantFiches (enseignant_fiche).
There seems to be a relation between Fiches and EnseignantFiches: every Fiche has (owns?) exactly one EnseignantFiche, namely the EnseignantFichethat the foreign keyIdFiche` refers to.
Furthermore, every Fiche has a Boolean property Valid; every EnseignantFiche has a (string?) property IdEns.
Requirement: Give me all Valid Fiches, that owns an EnseignantFiche with a value of IdEns that equals "variable"
var validFiches = dbContext.Fiches.Where(fiche => fiche.Valid);
var variableEnseignantFiches = dbContext.EnseignantFiches
.Where(enseignantFiche => enseignantFiche.IdEns == "*variable*";
var requestedFiches = validFiches.Join(
variableEnseignantFiches,
fiche => validFiche.IdFiche, // from every Fiche take the foreign key
enseignantFiche => enseignantFiche.IdFiche, // from every EnseignatFiche take primary key
(fiche, enseignantFiche) => new // when they match, make one new object
{
// Select the fiche properties that yo plan to use
FicheId = fiche.Id,
FicheName = fiche.Name,
...
// Select the EnseignantFiche properties that you plan to use:
EnseignantName = enseignantFiche.Name,
...
});
In words:
From the table of Fiches, keep only the Valid ones.
From the table of EnseignantFiches, keep only those with an IdEns equal to "Variable"
Join these two tables on Primary key equals Foreign key, and Select the properties that you plan to use.
Of course you can do this in one big LINQ statement. Because the query is not executed yet, this won't enhance process speed. It will surely deteriorate readability, testability and reusability.

Is it safe to join a table twice in the same query?

I need to write some linq (linq-to-sql) for a search page that allows the user to search for cars and optionally include search criteria for the car's parts. The two tables are CAR and CAR_PARTS. Here is what I have so far:
var query = db.CAR;
//if the user provides a car name to search by, filter on car name (this works)
if(model.CarName != "")
{
query = from c in query
where c.Name == model.CarName
select c;
}
//if the user provides a car part name to filter on, join the CAR_PART table
if(model.CarPartName != "")
{
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartName == model.CarPartName
select c;
}
//if the user provides a car part code to filter on, join the CAR_PART table
if(model.CarPartCode != "")
{
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartCode == model.CarPartCode
select c;
}
If the user decides they want to search on both CarPartName and CarPartCode, this logic would result in the CAR_PART table being joined twice. This feels wrong to me, but is this the correct way to handle this?
How would you write this?
It's legal to do so, but whether it makes sense, depends on your datamodel and your desired outcome.
Generally your code does the following if partname and partcode are defined
Join the cars table with the parts table with partname as join condition
Join the result of the first join again with the parts table with partcode as join condition.
Thus, this is equal to a join with join condition car.partname = part.name and car.partcode = part.code. I don't know, whether this is your desired behaviour or not.
There are some cases to distinguish
Joining with AND condition
CASE 1.1: name and code of a part are keys in the parts table
In this case for each name and code are each unique in the parts table, thus for each name there is exactly one code. The double join is not necessary, and may even lead to wrong results, because
if selected name and code identify the same part, it's the first join will already get the desired results
if name and code identifiy different parts, your result will be empty because the condition cannot be fullfilled.
In that situation I would suggest to write is as follows
if (!string.IsNullOrEmpty(model.CarPartName)){
// your join on partname
} else if (!string.IsNullOrEmpty(model.CarPartCode)) {
// your join on partcode
}
CASE 1.2: name and code of a part are NOT keys in the parts table
In this case, neither name nor code may be unique, and for one name there may be different codes and vice versa. Here the double join is necessary and will only return results containing parts which match both, name and code
Joining with OR condition
If on the other hand you want your join condition to be like car.partname = part.name and car.partcode = part.code you have to consider the following cases
CASE 2.1 name and code are keys
Here applies the same as above in case 1.1
CASE 2.2 name and code are NOT keys
Here you can't use the stepwise approach, because the result of the first join will only contain cars, where the name matches. There may be parts where only the code condition matches, but they can never be included in the final result, if they are not contained in the result of the first match. So in this case, you will have to define your query something like this
if (!string.IsNullOrEmpty(model.CarPartName) && !string.IsNullOrEmpty(model.CarPartCode)) {
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartName == model.CarPartName || parts.PartCode == model.CarPartCode
select c;
} else if (!string.IsNullOrEmpty(model.CarPartName)) {
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartName == model.CarPartName
select c;
} else if (!string.IsNullOrEmpty(model.CarPartCode)) {
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartCode == model.CarPartCode
select c;
}
What is wrong in there is actually with proper relations you don't need the join at all. Add that the behavior of LinqToSQL you can write that as:
var query = db.CAR
.Where( c =>
( string.IsNullOrEmpty(model.CarName)
|| c.Name == model.CarName ) &&
( string.IsNullOrEmpty(model.CarPartName)
|| c.Parts.Any( p => p.PartName == model.CarPartName )) &&
( string.IsNullOrEmpty(model.CarPartCode)
|| c.Parts.Any( p => p.PartCode == model.CarPartCode )));
Yours would work provided query is IQueryable (db.CAR.AsQueryable()). The two Linq approaches are similar but not the same. Depending on your real necessity yours might be the correct one or the wrong one. Yours would produce two inner joins, while this one simply create 2 exists check. Assume you have:
Car, Id:5, Name: Volvo
And parts like:
CarID:5, PartName:HeadLights, PartCode:1 ... other details
CarID:5, PartName:HeadLights, PartCode:2 ... other details
CarID:5, PartName:HeadLights, PartCode:3 ... other details
If user asks with model.CarName = "Volvo" and model.PartName = "HeadLights", you would get back the same Volvo 3 times. In second approach, you get back a single Volvo.
HTH
I feel more comfortable with fluent syntax, but I'm sure something similar to the following will work for you. I would check the fields in your model as part of a Select statement and then conditionally join using one field or the other. If neither are set, leave it null.
var query = db.CAR;
if (!string.IsNullOrWhitespace(model.CarName))
{
query = query.Where(car => car.Name == model.CarName);
}
var items = query.Select(car => new
{
Car = car, // maybe better to split this up into different fields, but I don't know what the car object looks like
// I assume your Car entity model has a navigation property to parts:
CarPart = !string.IsNullOrWhitespace(model.CarPartName)
? car.Parts.FirstOrDefault(part => part.PartName == model.CarPartName)
: !string.IsNullOrWhitespace(model.CarPartCode)
? car.Parts.FirstOrDefault(part => part.PartCode == model.CarPartCode)
: null
})
.ToList();
This does mean that the Code will be ignored if the Name is filled in. Reverse it if it needs to be the other way around. Or if you want to use both fields, you can put the string null checks in the Where clause.

Invalid 'where' condition. An entity member is invoking an invalid property or method

I've tried several methods to serach a client who have a propritaire.id in membresEquipeDuClient list, but each time I'm getting the "Invalid 'where' condition. An entity member is invoking an invalid property or method."-error!
I got the error in the second request,
The methods I've used :
List<TeamMembership> membresEquipeDuClient = (from k in context.CreateQuery<TeamMembership>()
where k.teamid == equipeDuClient.Id
select k).ToList();
var pRechercheNumeroClient = (from p in context.CreateQuery<Client>()
where membresEquipeDuClient.Any(a => a.systemuserid == p.proprietaire.Id)
select p).FirstOrDefault();
Any idea how to fix this?
Thx for all the help!
As said in the comment, Any is not supported, but Contains is. So you can do:
var systemUserIds =
(
from k in context.CreateQuery<TeamMembership>()
where k.teamid == equipeDuClient.Id
select k.systemuserid // Select the Id only
).ToList();
var pRechercheNumeroClient =
(
from p in context.CreateQuery<Client>()
where systemUserIds.Contains(p.proprietaire.Id)
select p
).FirstOrDefault();
I'm not familiar with Dynamics CRM, but I think you can remove the ToList from the first statement, so it will be an IQueryable and both statements will be executed as one expression.
var pRechercheNumeroClient = (from p in context.CreateQuery<Client>()
where membresEquipeDuClient.Select(a => a.systemuserid).Contains(p.proprietaire.Id)
select p).FirstOrDefault();

Modularize (refactor) Linq queries

I have a few Linq queries. Semantically, they are
a join b join c join d where filter1(a) && filter2(c) && filter3(d)
a join b join c where filter1(a) && filter2(c)
a join b join c join e where filter1(a) && filter2(c) && filter4(e)
...
I want to be able to factor out the shared part:
a join b join c where filter1(a) && filter2(c)
and dynamically append join d and filter3(d)
Is there a way to do this? I am already using the Predicate Builder to dynamically build conditionals (filters).
EDIT: I am using Linq-to-SQL.
EDIT: The base query looks like:
from a in As.AsExpandable()
join b in Bs on a.Id equals b.PId
join c in Cs on b.Id equals c.PId
where filter1(a) && filter2(b) && filter3(c)
select new A { ... }
filters are predicates in Predicate Builder. The type of the query is IQueryable<A>.
Next, I'd like to join this with d
from a in BaseQuery()
join d in D on a.Id equals d.PId
Currently join d .. causes a compilation error:
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to Join
Your example is a bit vague, but it is easy to create a method that returns an IQueryable<T> and reuse that method, if that’s what you mean. Here is an example:
// Reusable method
public IQueryable<SomeObject> GetSomeObjectsByFilter(Context c)
{
return
from someObject in context.SomeObjects
where c.B.A.Amount < 1000
where c.Roles.Contains(r => r.Name == "Admin")
select someObject;
}
You can reuse this method in other places like this:
var q =
from c in GetSomeObjectsByFilter(context)
where !c.D.Contains(d => d.Items.Any(i => i.Value > 100))
select c;
Because the way IQueryable works, only the final query (the collection that you start iterating) will trigger a call to the database, which allows you to build a highly maintainable system by reusing business logic that gets effectively executed inside the database, whiteout the loss of any performance.
I do this all the time and it improves the maintainability of my code big time. It works no matter which O/RM tool you run, because there is no difference in Queryable<T> composition, between writing the query in one peace, or splitting it out to different methods.
Note that you do sometimes need some smart transformations to get the duplicate parts in a single method. Things that might help are returning grouped sets, and returning a set of a different type, than what you think you need. This sounds a bit vaque, but just post a question here at SO when you have problems splitting up a method. There are enough people here that can help you with that.
I can answer half your question easily. Linq makes it simple to append .where clauses to an existing query.
Example:
var x = db.Table1.where(w => w.field1 == nTestValue);
x = x.where(w => w.field2 == nTestValue2);
I believe you can do the joins as well but I have to go find an example in some old code. I'll look if nobody else jumps in with it soon.

Linq how to write a JOIN

Linq to EF, I'm using asp.net 4, EF 4 and C#.
Here are two ways I came up with to query my data. Ways A and C are working fine. B however needs to implement and additional WHERE statement (as "where c.ModeContent == "NA").
My question is:
Regarding this kind of join (outer join, I suppose) what is the best approach in term of performance?
Could you show me some code to implement additional WHERE statement in B?
Any way to improve this code?
Thanks for your time! :-)
// A
var queryContents = from c in context.CmsContents
where c.ModeContent == "NA" &&
!(from o in context.CmsContentsAssignedToes select o.ContentId)
.Contains(c.ContentId)
select c;
// B - I need to implent where c.ModeContent == "NA"
var result01 = from c in context.CmsContents
join d in context.CmsContentsAssignedToes on c.ContentId equals d.ContentId into g
where !g.Any()
select c;
// C
var result02 = context.CmsContents.Where(x => x.ModeContent == "NA").Where(item1 => context.CmsContentsAssignedToes.All(item2 => item1.ContentId != item2.ContentId));
Regarding query B you can apply the condition like this:
var result01 = from c in context.CmsContents where c.ModeContent == "NA"
join d in context.CmsContentsAssignedToes on c.ContentId equals d.ContentId into g
where !g.Any()
select c;
Your query will be far more readable and maintainable (and perform at least as well) if you use your association properties instead of join:
var result = from c in context.CmsContents
where c.ModeContent == "NA"
&& !c.AssignedToes.Any()
select c;
I'm guessing that the navigation on CmsContent to CmsContentsAssignedToes is called AssignedToes. Change the name in my query if it's actually called something else.
This query can be read out loud and you know exactly what it means. The join versions you have to think about.

Categories