UPDATE
Even doing a search on the contacts firstName or LastName causes issues:
var contacts =
(
from c in context.ContactSet
join m in context.py3_membershipSet on c.ContactId equals m.py3_Member.Id
where m.statuscode.Value == 1
&& ((c.FirstName != null && c.FirstName == searchTerm) || (c.LastName!=null && c.LastName == searchTerm) || (c.FullName != null && c.FullName == searchTerm))
orderby c.LastName
select new
{
ContactId = c.ContactId,
FirstName = c.FirstName,
LastName = c.LastName,
BranchCode = c.py3_BranchArea,
Branch = (c.FormattedValues != null && c.FormattedValues.Contains("py3_brancharea") ? c.FormattedValues["py3_brancharea"] : "N/a"),
JobTitle = c.JobTitle,
Organisation = (c.ParentCustomerId != null ? c.ParentCustomerId.Name : "N/a"),
joinedAsCode = c.py3_SOLACEMemberJoinedAs,
JoinedAs = (c.FormattedValues != null && c.FormattedValues.Contains("py3_solacememberjoinedas") ? c.FormattedValues["py3_solacememberjoinedas"] : "N/a"),
Expertise = (c.py3_SOLACEMemberAreasofExpertise != null && c.py3_SOLACEMemberAreasofExpertise.Trim() != String.Empty ? c.py3_SOLACEMemberAreasofExpertise : "N/a"),
Title = c.Salutation
}
);
Which screams back:
'py3_membership' entity doesn't contain attribute with Name = 'firstname'.
Ive got the following code that het some info from an Online CRM:
var context = new XrmServiceContext();
var contacts1 =
(
from c in context.ContactSet
join m in context.py3_membershipSet on c.ContactId equals m.py3_Member.Id
where m.statuscode.Value == 1
orderby c.LastName
select new
{
ContactId = c.ContactId,
FirstName = c.FirstName,
LastName = c.LastName,
BranchCode = c.py3_BranchArea,
Branch = (c.FormattedValues != null && c.FormattedValues.Contains("py3_brancharea") ? c.FormattedValues["py3_brancharea"] : "N/a"),
JobTitle = c.JobTitle,
Organisation = (c.ParentCustomerId != null ? c.ParentCustomerId.Name : "N/a"),
joinedAsCode = c.py3_SOLACEMemberJoinedAs,
JoinedAs = (c.FormattedValues != null && c.FormattedValues.Contains("py3_solacememberjoinedas") ? c.FormattedValues["py3_solacememberjoinedas"] : "N/a"),
Expertise = (c.py3_SOLACEMemberAreasofExpertise != null && c.py3_SOLACEMemberAreasofExpertise.Trim() != String.Empty ? c.py3_SOLACEMemberAreasofExpertise : "N/a")
}
);
I then bind this to a datalist as an array, which all works fine.
However I want to be able to limit the results to a value selected from a dropdownlist, and expected the following to work:
var context = new XrmServiceContext();
var contacts1 =
(
from c in context.ContactSet
join m in context.py3_membershipSet on c.ContactId equals m.py3_Member.Id
where m.statuscode.Value == 1 &&
c.FormattedValues["py3_brancharea"] == ddlBranchTags.SelectedItem.Value
orderby c.LastName
select new
{
ContactId = c.ContactId,
FirstName = c.FirstName,
LastName = c.LastName,
BranchCode = c.py3_BranchArea,
Branch = (c.FormattedValues != null && c.FormattedValues.Contains("py3_brancharea") ? c.FormattedValues["py3_brancharea"] : "N/a"),
JobTitle = c.JobTitle,
Organisation = (c.ParentCustomerId != null ? c.ParentCustomerId.Name : "N/a"),
joinedAsCode = c.py3_SOLACEMemberJoinedAs,
JoinedAs = (c.FormattedValues != null && c.FormattedValues.Contains("py3_solacememberjoinedas") ? c.FormattedValues["py3_solacememberjoinedas"] : "N/a"),
Expertise = (c.py3_SOLACEMemberAreasofExpertise != null && c.py3_SOLACEMemberAreasofExpertise.Trim() != String.Empty ? c.py3_SOLACEMemberAreasofExpertise : "N/a")
}
);
However, this throws the following error:
Invalid 'where' condition. An entity member is invoking an invalid property or method.
Its the same even if I hard code the branchtag criteria rather than going of the DDL value.
Ive also tried doing a select on the contacts1 set eg:
var results = contacts1.select(c=> c.BranchTag == ddlBranchTags.SelectedItem.Value
But that throws the same error.
If I remove the branchTag where clause it works as expected.
I think its fair to assume that Ive gone wayward with this, so any useful / constructive pointers (for a LINQ newb) would be really appreciated. Thanks.
LINQ-to-CRM is actually fairly limited in terms of the expression forms it can support. If you consider what it's doing behind the scenes – taking your entire LINQ expression and translating it into a CRM QueryExpression – this makes more sense.
Basically, it's not valid to expect to be able to embed arbitrary C# into your query (even if it compiles), as it's not possible to translate all code into a CRM query. The query provider could hypothetically be smart enough to determine what it can submit as a query to CRM, and what code it then has to execute client-side to get your final desired results, but that's generally a pretty hard problem – one which the LINQ provider does not attempt to solve.
In Where clauses specifically, the LINQ provider scans the expressions for basic forms of <attribute name accessor> <simple comparison> <value>. It understands early-bound codegen attribute accessors (entity.Attribute_name), indexer access (entity["attribute_name"]), and entity.GetAttributeValue<T>("attribute_name") access. Those are the only things you can use on the left side of a comparision in a Where clause (or in an OrderBy clause). In your second query, you're accessing FormattedValues, which the query provider doesn't understand/support.
An additional limitation is that a single Where clause can only address one "target" entity. So the case where you have filters on both "m" and "c" in a single predicate is not supported. I'm less clear on the reasons for this limitation, but that's how it is. You can still accomplish the equivalent filter by splitting the two conditions (joined by an && in your current query) into two separate Where clauses.
It's natural to want to write LINQ-to-CRM queries as though you are using LINQ-to-Objects, but doing so will generally be frustrating. LINQ-to-CRM is somewhat limited, but many of those limitations are inherited from the underlying CRM SDK query systems it's built upon. It's still a decent API for writing CRM queries, as long as you keep the limitations in mind.
If you're writing a query that is going to require you to execute some arbitrary code to filter/order/map your results, the general technique that should be used is to separate your query into two parts: the query that should be submitted to CRM, and your aribitrary code transformation. You write the most data-efficient query you can for getting your CRM data, force evaluation of the query using something like ToList or ToArray (as LINQ is otherwise lazily-evaluated), and then you proceed with your code on the results of that evaluation.
This looks to be a limitation of the CRM LINQ Provider.
where: The left side of the clause must be an attribute name and the right side of the clause must be a value. You cannot set the left side to a constant. Both the sides of the clause cannot be constants. Supports the String functions Contains, StartsWith, EndsWith, and Equals.
(Emphasis mine)
It probably has to do with indexing into the FormattedValues property of your Contact Set. You can try using a let clause and see if that helps.
EDIT: Here's an example of how to use let. I'm not sure if this will actually help - I can't test it at the moment.
from c in context.ContactSet
let formattedValue = c.FormattedValues["py3_brancharea"]
join m in context.py3_membershipSet on c.ContactId equals m.py3_Member.Id
where m.statuscode.Value == 1 &&
formattedValue == ddlBranchTags.SelectedItem.Value
Notice how I let formattedValue be the value from c.FormattedValue["py3_brancharea"] and then use that in the where clause. It might get you past the limitation.
I believe you can get around this issue by using method syntax for where instead of query syntax.
var context = new XrmServiceContext();
var contacts1 =
(
from c in context.ContactSet.Where(c => c.FormattedValues["py3_brancharea"] == ddlBranchTags.SelectedItem.Value)
join m in context.py3_membershipSet.Where(m => m.statuscode.Value == 1)
on c.ContactId equals m.py3_Member.Id
// ...
);
More insight on using method syntax. If you prefer a solution that lets you use query syntax, you can use LINQKit as explained here. As an aside, the LINQKit answer mentions that join gets performed on the client side, so you might want to change it to a where clause.
Based on #Peter Majeed's answer, I solved my problem by using the double where clause:
var contacts =
(
from c in context.ContactSet
join m in context.py3_membershipSet on c.ContactId equals m.py3_Member.Id
where m.statuscode.Value == 1
where ((c.FirstName != null && c.FirstName == searchTerm) || (c.LastName!=null && c.LastName == searchTerm) || (c.FullName != null && c.FullName == searchTerm))
orderby c.LastName
select new
{
ContactId = c.ContactId,
FirstName = c.FirstName,
LastName = c.LastName,
BranchCode = c.py3_BranchArea,
Branch = (c.FormattedValues != null && c.FormattedValues.Contains("py3_brancharea") ? c.FormattedValues["py3_brancharea"] : "N/a"),
JobTitle = c.JobTitle,
Organisation = (c.ParentCustomerId != null ? c.ParentCustomerId.Name : "N/a"),
joinedAsCode = c.py3_SOLACEMemberJoinedAs,
JoinedAs = (c.FormattedValues != null && c.FormattedValues.Contains("py3_solacememberjoinedas") ? c.FormattedValues["py3_solacememberjoinedas"] : "N/a"),
Expertise = (c.py3_SOLACEMemberAreasofExpertise != null && c.py3_SOLACEMemberAreasofExpertise.Trim() != String.Empty ? c.py3_SOLACEMemberAreasofExpertise : "N/a"),
Title = c.Salutation
}
);
Essentially the answer is to split each logical 'chunk' of the query with its own where clause. It would appear this allows LINQ to make one query based on the first 'where' clause and then do a secondary filter on that via the second 'where' clause.
Related
I'm trying to perform a left join with LINQ and I'm not finding the correct structure (my original query came from a query built in Access).
I'm trying to think with the method available in LINQ but I'm not getting any useful result. I really appreciate any suggestion related LINQ architecture and how to think complex queries that the ones below.
Original Query in Access:
(reformatted)
SELECT [Check People].ID, [Check People].MMS_USER__C, [Check People].EMPLOYMENT_STATUS__C, [Check People].LASTLOGINDATE, [Check People].ENTERPRISE_ID__C, [Check People].ISACTIVE, [ExtractPrivateReports].NAME, [ExtractPrivateReports].LASTRUNDATE, [ExtractPrivateReports].ID
FROM ExtractPrivateReports
LEFT JOIN [Check People] ON [ExtractPrivateReports].OWNERID = [Check People].MMS_USER__C
WHERE
(
(([Check People].ISACTIVE) = "true")
And
(([ExtractPrivateReports].LASTRUNDATE) < Date() - 180)
)
Or
(
(([Check People].ISACTIVE)="true")
And
(
(([ExtractPrivateReports].LASTRUNDATE) Is Null)
And
(([ExtractPrivateReports].CREATEDDATE) < Date() - 180)
)
);
Note: the tables are [Check People] and [ExtractPrivateReports]
Query that I made with LINQ (doesn't retrieve the same number of records that retrieves the query above)
DateTime daysCalculation = today.AddDays(-166);
var query_3 = from privateReports in privateReportList
join checkPeople in this.getListaCheckPeople(p, u)
on privateReports.OWNERID equals checkPeople.Mms_user_c
into jn
from j in jn.DefaultIfEmpty()
where ((j.IsActve.Equals("true")) && (privateReports.LASTRUNDATE.CompareTo(daysCalculation) > 0))
|| ((j.IsActve.Equals("true")) && (privateReports.LASTRUNDATE == null))
&& (privateReports.CREATEDDATE.CompareTo(daysCalculation) > 0)
&& (!string.IsNullOrEmpty(privateReports.OWNERID))
select new ActivePeopleWithPrivateReportsNotRunInMoreThan180Days
{
EMPLOYMENT_STATUS__C = j.EmploymentStatus == null ? "no value" : j.EmploymentStatus,
LASTRUNDATE = privateReports.LASTRUNDATE,
CheckPeople_ID = j.PeopleKey_c == null ? "no value" : j.PeopleKey_c,
ENTERPRISE_ID__C = j.EnterpriseID_c == null ? "no value" : j.EnterpriseID_c,
ISACTIVE = j.IsActve == null ? "no value" : j.IsActve,
NAME = privateReports.NAME,
LASTLOGINDATE = j.LastLoginDate == null ? DateTime.ParseExact("0001-01-01", "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture) : j.LastLoginDate,
ExtractPrivateReports_ID = privateReports.ID,
MMS_USER__C = j.Mms_user_c == null ? "no value" : j.Mms_user_c
};
The first code was used "join"
But in the second code is not used "join"
Note that the result is the same.
So I have several questions
Which is better?
Which is faster?
Code01:
(from Member in dContext.TB_FamilyCardMembers
select new
{
Member.FamilyCard_ID,
Member.TB_FamilyCard.NoFamilyCard,
CardType = Member.TB_FamilyCard.Is_Card == true ? "دفتر عائلة" : "بيان عائلي",
FirsN = Member.TB_Person.FirstName,
FatherN = Member.TB_Person.FatherName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Father_ID.ToString())).FirstName : Member.TB_Person.FatherName,
LastN = Member.TB_Person.LastName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Father_ID.ToString())).LastName : Member.TB_Person.LastName,
MotherN = Member.TB_Person.MotherName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Mother_ID.ToString())).FirstName : Member.TB_Person.MotherName,
MotherLN = Member.TB_Person.MotherLastName == null ? SelectPersonByID(int.Parse(Member.TB_Person.Mother_ID.ToString())).LastName : Member.TB_Person.MotherLastName
}).ToList();
______________________________________________
Code02:
(from Member in dContext.TB_FamilyCardMembers
join Card in dContext.TB_FamilyCards on Member.FamilyCard_ID equals Card.ID
join Person in dContext.TB_Persons on Member.Person_ID equals Person.ID
select new
{
Member.FamilyCard_ID,
Card.NoFamilyCard,
CardType = Card.Is_Card == true ? "دفتر عائلة" : "بيان عائلي",
FirsN = Person.FirstName,
FatherN = Person.FatherName == null ? SelectPersonByID(int.Parse(Person.Father_ID.ToString())).FirstName : Person.FatherName,
LastN = Person.LastName == null ? SelectPersonByID(int.Parse(Person.Father_ID.ToString())).LastName : Person.LastName,
MotherN = Person.MotherName == null ? SelectPersonByID(int.Parse(Person.Mother_ID.ToString())).FirstName : Person.MotherName,
MotherLN = Person.MotherLastName == null ? SelectPersonByID(int.Parse(Person.Mother_ID.ToString())).LastName : Person.MotherLastName
}).ToList();
All roads lead to Rome
Just because there is no join in the Linq code, does not mean there is no join in the final query.
Once you use
Member.TB_Person.FirstName
Linq-2-sql will post probably add a join to that generated SQL.
A lot of coders explicitly add the join because they are coding LINQ-2-SQL in a real SQL kind of way. Most of the times it is not needed (assuming the proper foreign keys are in place) because L2S will figure the join out for you.
To really answer your question you need to profile the actual SQL generated. Only then you will see the difference in the query that is actually sent to the database. Chances are it is identical. If not, select the most efficient of the two.
How to view the SQL:
How to view LINQ Generated SQL statements?
I am getting:
Non-static method requires a target.
The problem is that Status is null. I don't understand why, because there is a condition which clearly indicates if Status is null return 1.
var filterstatus = (from bq in basequery
let LastStatus = Status == null ? 1
: ((from sd in ems.SampleDatas
where sd.Reference_id == Status.id
&& sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
It's because it's converting the entire expression into SQL, and not doing the short-circuit in memory (the short circuit would be handled by the database).
You can write something like this, which will properly short-circuit in the database (but still generate the right hand side of the query).
var statusID = Status == null ? (int?)null : Status.id;
var filterstatus = (from bq in basequery
let LastStatus =
statusID == null ?
1 :
((from sd in ems.SampleDatas
where sd.Reference_id == statusID && sd.DateTimeUTC <= bq.DateTimeUTC
orderby sd.DateTimeUTC
select ((sd.Value >= StatusValue) ? 1 : 0)
).DefaultIfEmpty(1).FirstOrDefault())
select new { bq, LastStatus });
Ideally, though, you'd have two separate queries, depending on Status, as it's already known at that point whether the right hand side is required or not.
I have this SQL query, which works fine for my purposes :
SELECT [Documenti].[ID]
,[Documenti].[Data]
,[Documenti].[Descrizione]
,[Documenti].[DAL]
,[Documenti].[AL]
,[Documenti].[Stato]
,[Documenti].[NomeDoc]
,[Documenti].[Classe]
,[Documenti].[CTipo]
,[Documenti].[CSTIpo]
,[Documenti].[SiglaRed]
,[Documenti].[Stipula]
,[Documenti].[DataCmp]
,[Documenti].[Regione]
,[Documenti].[NumDoc]
,[Documenti].[Destinazione]
,[Documenti].[DataStampa]
,[Documenti].[Ext]
,[Tipi].GRP2
FROM [Documenti]
LEFT JOIN Tipi ON Tipi.C = [Documenti].Classe AND Tipi.C_Tipo = [Documenti].CTipo
LEFT JOIN STipi ON STipi.C = [Documenti].Classe AND STipi.C_STipo = [Documenti].CSTipo
where [Documenti].Stato IN (4,2,3)
In particular I need to realize the SELECT IN clause, because without it my LINQ code works fine. My LINQ query is the following :
IQueryable listaDoc = null;
string _fasi = "4;2;3";
var fasi = _fasi.Split(';').Select(n => short.Parse(n)).ToList();
listaDoc = (from DOC in db.Documenti
from subdoc in db.Tipi.Where(j => j.C_Tipo == DOC.CTipo && j.C == DOC.Classe.Value).DefaultIfEmpty()
from subdoc2 in db.Stipi.Where(j => j.C_STipo == DOC.CSTIpo && j.C == DOC.Classe.Value).DefaultIfEmpty()
from subdoc3 in db.PathClassi_Web.Where(j => j.cod_class == DOC.Classe).DefaultIfEmpty()
where fasi.Contains(DOC.Stato.Value)
select new
{
DOC.ID,
DOC.Data,
DOC.Descrizione,
DOC.DAL,
DOC.AL,
DOC.Stato,
DOC.NomeDoc,
DOC.Classe,
DOC.CTipo,
DOC.CSTIpo,
DOC.SiglaRed,
DOC.Stipula,
DOC.DataCmp,
DOC.Regione,
DOC.NumDoc,
DOC.Destinazione,
DOC.DataStampa,
DOC.Ext,
GRP2 = subdoc == null ? string.Empty : subdoc.GRP2,
DESCRIZ = subdoc2 == null ? string.Empty : subdoc2.Descriz,
DESCR_CLASS = subdoc3 == null ? string.Empty : subdoc3.descr_class
});
But it returns to me 0 results. What's wrong in my LINQ query in order to reach the same result of my pure SQL query described above?
I have used the tool Linqer (http://www.sqltolinq.com/) to help me convert complex SQL-statements to linq. It saved me many times.
I am working with the following Entity Framework query. I know there's a lot going on here but am hoping it's clear enough that someone might be able to spot the issue.
var lineItems = from li in Repository.Query<CostingLineItem>()
let cid = (li.ParentCostingPackage != null) ?
li.ParentCostingPackage.ParentCostingEvent.ProposalSection.Proposal.Costing.Id :
li.ParentCostingEvent.ProposalSection.Proposal.Costing.Id
where cid == costingId &&
li.OriginalProductId.HasValue &&
(li.Quantity.HasValue && li.Quantity.Value > 0) && // li.QuantityUnitMultiplier
Classifications.Contains(li.OriginalProduct.ClassificationEnumIndex)
let selectedChoiceId = li.OriginalPackageOptionId.HasValue ?
(from c in li.OriginalPackageOption.CostingLineItems
orderby (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id
select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault() :
0
where selectedChoiceId == 0 || (li.OriginalPackageOptionChoiceId.HasValue && li.OriginalPackageOptionId.Value == selectedChoiceId)
let hasProviderAvailable = li.OriginalProductItem.ProductItemVendors.Any(
piv => piv.ProductPricings.Any(pp => pp.ProductItemVendor.CompanyId != null || pp.ProductItemVendor.HotelId != null))
select new
{
LineItem = li,
ProductItem = li.OriginalProductItem,
Product = li.OriginalProduct,
Vendors = li.CostingLineItemVendors,
HasProviderAvailable = hasProviderAvailable
};
As is, this query generates the following run-time error:
The wait operation timed out
If I change the section that declares selectedChoiceId to the following, the error goes away:
let selectedChoiceId = 0
Can anyone see how that code is consistently causing a time-out error?
(Note: This code is part of a large application that has been running for several years. So I really don't think this has anything to do with the connection string or anything like that. If I make the change above, it works consistently.)
The query can be simplified in a number of ways, which should make it easier to optimize by the database engine.
Firstly, you can remove a number of null checks (HasValue), because they're not relevant in SQL, but they do bloat the generated SQL.
Secondly, I think this check involving selectedChoiceId can be greatly simplified. This is what I think the statement could look like:
from li in Repository.Query<CostingLineItem>()
let cid = (li.ParentCostingPackage != null) ?
li.ParentCostingPackage.ParentCostingEvent.ProposalSection.Proposal.Costing.Id :
li.ParentCostingEvent.ProposalSection.Proposal.Costing.Id
where cid == costingId &&
li.OriginalProductId.HasValue &&
li.Quantity > 0 && // no null check
Classifications.Contains(li.OriginalProduct.ClassificationEnumIndex)
let selectedChoiceId = (from c in li.OriginalPackageOption.CostingLineItems
orderby c.IsIncluded ? -2 : c.IsDefaultItem ? -1 : c.Id // no null checks
select (int)c.OriginalPackageOptionChoiceId).FirstOrDefault()
where !li.OriginalPackageOptionId.HasValue || li.OriginalPackageOptionId == selectedChoiceId
let hasProviderAvailable = li.OriginalProductItem.ProductItemVendors.Any(
piv => piv.ProductPricings.Any(pp => pp.ProductItemVendor.CompanyId != null || pp.ProductItemVendor.HotelId != null))
select new
{
LineItem = li,
ProductItem = li.OriginalProductItem,
Product = li.OriginalProduct,
Vendors = li.CostingLineItemVendors,
HasProviderAvailable = hasProviderAvailable
}
For the rest, of course there are the usual suspects. Better indexes may become more important as the database volume increases. Checking for (and fixing) database fragmentation can also have a significant impact.
I think this will give you a better performance but not sure if it'll fix the problem :
let selectedChoiceId = li.OriginalPackageOptionId.HasValue
? (from c in li.OriginalPackageOption.CostingLineItems
let cOrder = (c.IsIncluded ?? false) ? -2 : (c.IsDefaultItem ?? false) ? -1 : c.Id
orderby cOrder
select (int) c.OriginalPackageOptionChoiceId).FirstOrDefault()
: 0