i have written below query in LINQ to perform left join but its throwing error:
var qry = from c in dc.category_feature_Name_trans_SelectAll_Active()
join p in dc.product_category_feature_trans_SelectAll()
on c.cft_id equals p.cft_id into cp
from p in cp.DefaultIfEmpty()
select new
{
c.cft_id,
c.feature_id,
c.feature_name,
p.product_id ,
p.value
};
Error:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the
current web request. Please review the stack trace for more information about
the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 57: on c.cft_id equals p.cft_id into cp
Line 58: from p in cp.DefaultIfEmpty()
error Line 59: select new
Line 60: {
Line 61: c.cft_id,
Please help me.
cp.DefaultIfEmpty() returns a sequence which will have a single null value in if cp was empty.
That means you have to account for the fact that the p in
from p in cp.DefaultIfEmpty()
may be null. Now, you haven't really said what you want to happen in that case. You might want something like this:
var qry = from c in dc.category_feature_Name_trans_SelectAll_Active()
join p in dc.product_category_feature_trans_SelectAll()
on c.cft_id equals p.cft_id into cp
from p in cp.DefaultIfEmpty()
select new
{
c.cft_id,
c.feature_id,
c.feature_name,
product_id = p == null ? null : p.product_id,
value = p == null ? null : p.value
};
... or you may want some different handling. We don't know the types of p.product_id or p.value, which doesn't help. (For example, you'll need a bit more work with the above code if product_id is a value type.)
You are making left join, so p can be null. You need to account for that.
Here is the query that should work, although I don't know for sure what kind of value is p.value. The query will work if value is a reference type. If value is value type, than use the cast similar to product_id cast.
var qry = from c in dc.category_feature_Name_trans_SelectAll_Active()
join p in dc.product_category_feature_trans_SelectAll()
on c.cft_id equals p.cft_id into cp
from p in cp.DefaultIfEmpty()
select new
{
c.cft_id,
c.feature_id,
c.feature_name,
product_id = p == null ? (int?)null : p.product_id,
value = p == null ? null : p.value,
};
I am facing the same issue while using the LEFT JOIN to multiple tables in LINQ query and I was facing null reference exception. I used the trick to check the null value using the "?" Please correct me if my approach is incorrect.
var deviceResultDetails = from pa in programAliasrecords
join pgm in Newprogramrecords on pa.program_id equals pgm.id into pgm_join
from pgm2 in pgm_join.DefaultIfEmpty()
join latest_fw in firmwareWithIdrecords on pgm2?.latest_firmware_id equals latest_fw?.id into latest_fw_join
from latest_fw2 in latest_fw_join.DefaultIfEmpty()
join dev_fw in firmwareWithIdrecords on pgm2?.firmware_group_id equals dev_fw?.firmware_group_id into dev_fw_join
from dev_fw2 in dev_fw_join.DefaultIfEmpty()
join vv in vulnerabilityrecords on pgm2?.id equals vv?.program_id into vv_join
from vv2 in vv_join.DefaultIfEmpty()
where
dev_fw2?.version == row.firmware
&& pa?.keyword == row.model
select new _deviceResults
{
model = row.model,
serial = row.serial,
firmware = row.firmware,
firmware_date = dev_fw2.br_date == null ? null: dev_fw2.br_date,
latest_firmware = latest_fw2.version == null ? null : latest_fw2.version,
latest_firmware_date = latest_fw2.br_date == null ? null : latest_fw2.br_date,
status = Convert.ToInt32(vulnerability_count) > 0 && pgm2.out_of_support == "TRUE" ? "Vulnerable (End of Suport)" :
Convert.ToInt32(vulnerability_count) > 0 && pgm2.out_of_support == " " ? "Vulnerable (Upgradeable)" :
pgm2.out_of_support == "TRUE" ? "Out-of-support" :
Convert.ToInt32(dev_fw2.revs_out_of_date) > 1 ? "Out-of-date" :
pgm2.id == "NonHP" ? "NonHP" :
pgm2.id == "NoFirmware" ? "No Firmware" :
pgm2.id == "HPink" || pgm2?.id == "HPOther" ? "Not evaluated (model not included yet)" :
pgm2.id == " " ? "Not evaluated (model not recognized)" :
dev_fw2.version == " " ? "Not evaluated (firmware version missing)" :
dev_fw2.id == " " ? "Not evaluated (firmware version mismatch)" : // && dev_fw.id in (select version from firmware)
dev_fw2.id == " " ? "Not evaluated (firmware version unrecognized)" :
dev_fw2.br_date == " " || pgm2.id == " " || dev_fw2.revs_out_of_date == " " ? "Not evaluated" : "OK",
out_of_support = pgm2.out_of_support == "" ? "false" : pgm2.out_of_support,
revs_out_of_date = dev_fw2.revs_out_of_date == null ? null : dev_fw2.revs_out_of_date,
program_id = pgm2.id == null ? null : pgm2.id,
firmware_id = dev_fw2.id == null ? null : latest_fw2.br_date
};
Use your Model Class as a parameter for DefaultIfEmpty() function.
from p in cp.DefaultIfEmpty(new yourModelClass())
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
};
I'm trying to select nullable datetime by adding days.
As instance
COMPLETE_TIME = (x.VISIT_DATE.HasValue ? x.VISIT_DATE.Value.AddDays(1) : (DateTime?)null)
If datetime has value i want to add 1 days by selecting query as above.
If datetime does not have value i want to set nullable datetime by selecting query as above.
However if i try above code i get Error (please check bottom side for error)
All Query:
var result=
(from s in context.SURVEYs
join x in context.SURVEY_X on s.SURVEY_ID equals x.SURVEY_ID
join sas in context.SURVEY_ANSWER_SELECTION on s.SURVEY_ID equals sas.SURVEY_ID
join o in context.REP_OPTION on sas.OPTION_ID equals o.OPTION_ID
from PCO in context.REP_PARENT_CHILD_OPTIONS.Where(w => w.CHILD_OPTION_ID == sas.OPTION_ID).DefaultIfEmpty()
where
(s.SURVEY_ID == 5 || s.PARENT_SURVEY_ID == 5) &&
o.SUGGESTION != null &&
PCO.PARENT_OPTION_ID == null
select new
{
SUGGESTION = o.SUGGESTION,
DISPLAY_ORDER = "0",
SUGGESTION_TYPE = o.SUGGESTION_TYPE,
o.EXAMPLE_IMAGE_ID,
COMPLETE_TIME = (x.VISIT_DATE.HasValue ? x.VISIT_DATE.Value.AddDays(1) : (DateTime?)null) // Problem in this part
}).ToList();
Error:
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'System.DateTime AddDays(Double)' method, and this method cannot be translated into a store expression.
Question:
How can i select added datetime or null datetime in select part ?
Any help will be appreciated.
Thanks.
If you are using EF 6 you could use on of the DbFunctions
var result=
(from s in context.SURVEYs
join x in context.SURVEY_X on s.SURVEY_ID equals x.SURVEY_ID
join sas in context.SURVEY_ANSWER_SELECTION on s.SURVEY_ID equals sas.SURVEY_ID
join o in context.REP_OPTION on sas.OPTION_ID equals o.OPTION_ID
from PCO in context.REP_PARENT_CHILD_OPTIONS.Where(w => w.CHILD_OPTION_ID == sas.OPTION_ID).DefaultIfEmpty()
where
(s.SURVEY_ID == 5 || s.PARENT_SURVEY_ID == 5) &&
o.SUGGESTION != null &&
PCO.PARENT_OPTION_ID == null
select new
{
SUGGESTION = o.SUGGESTION,
DISPLAY_ORDER = "0",
SUGGESTION_TYPE = o.SUGGESTION_TYPE,
o.EXAMPLE_IMAGE_ID,
COMPLETE_TIME = (x.VISIT_DATE.HasValue ? Dbfunctions.AddDays(x.VISIT_DATE.value, 1) : (DateTime?)null) // Problem in this part
}).ToList();
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.
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.
I'm having a problem with a LINQ to SQL query, the data being returned from the server could be null so I've set up an if statement within the query but the query is still throwing an exception.
This is a shortened down version of the query code
var a = from b in db.branches
where (b.Location != null) ?
(
(Query.Location == null) ?
true :
//The following line causes the exception to be thrown
object.Equals(Query.Location.ToLower() , b.Location.ToLower())
) :
(
(Query.Location == null) ?
true :
false
)
select b;
If the search term "Location" is null then I don't want to filter by location, but if it isn't null then I have to check if the value in the row is null or not as some of entries have a null location.
The code works fine until I add in the compare line. In order to get to the compare line, both Query.Location and b.Location cannot be null therefore the code shouldn't fail.
Any idea what the problem could be?
Thanks.
EDIT
If i remove the .toLower() from the object.equals call then the query runs correctly, it also manages to work no matter what case the query is in.
var a = from b in db.branches
where (b.Location != null) ?
(
(Query.Location == null) ?
true :
//The following line causes the exception to be thrown
object.Equals(Query.Location , b.Location)
) :
(
(Query.Location == null) ?
true :
false
)
select b;
I wouldn't like to say for sure what's going wrong here, but I think you could actually make your code significantly simpler by splitting it into two different queries:
public void Search(SearchTerms Query)
{
var queryWithLocation = db.branches.Where(b =>
Query.Location.Equals(b.Location, StringComparison.OrdinalIgnoreCase);
var query = Query.Location != null ? queryWithLocation : db.branches;
}
I've changed the way of doing the Equals - that's the way I prefer to perform case-insensitive searches; you'll have to see whether it works for LINQ to SQL.
Try this;
public void Search(SearchTerms Query)
{
var a = from b in db.branches
where (b.Location != null) ?
(
(Query.Location == null) ?
true
:
//The following line causes the exception to be thrown
Query.Location.ToLower() == b.Location.ToLower()
)
:
(
(Query.Location == null) ?
true
:
false
)
select b
}
I believe the object is probably causing the NullReference
from b in db.branches
let location = b.Location
where location != null ?
b.Location.Equals(b.Location, Query.Location ?? b.Location, StringComparison.OrdinalIgnoreCase) : // if Query.Location is null then select all
false // to select nothing in this case
select b;