i have multi tables with one to many relation like chain
1- address has postal code id
2- postal table has area id
3- area table has city id
4- city table has county id
5- county table has country id
6- and at the last country table
from every table i need to get street name , full postal code, area name , city name, county name , country name
the query is as follow
var address = from add in _Database.Addresses
select add;
address.Select(x=>new AddressClass {
BuildingNameOrNumber=x.BuildingNameOrNumber,
MainStreet = x.Postcode ==null ? string.Empty: x.Postcode.StreetName,
FullPostCode = x.Postcode == null ? string.Empty :x.Postcode.FullPostcode,
AreaName = x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty: x.Postcode.Area.Name,
CityName = x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty : x.Postcode.Area.City == null ? string.Empty: x.Postcode.Area.City.Name,
CountyName = x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty : x.Postcode.Area.City == null ? string.Empty : x.Postcode.Area.City.County == null ?string.Empty: x.Postcode.Area.City.County.Name,
CountryName= x.Postcode == null ? string.Empty : x.Postcode.Area == null ? string.Empty : x.Postcode.Area.City == null ? string.Empty : x.Postcode.Area.City.County == null ? string.Empty : x.Postcode.Area.City.County.Country == null ? string.Empty:x.Postcode.Area.City.County.Country.CountryName
})
i need to replace this multi conditions with one condition for every property
This is too complex for me to wrap my head around sorry :)
But what you need is the ? and ?? operators. Second to last item in your really complex query will be like:
CountyName = x.Postcode?.Area?.City?.County?.Name ?? string.Empty
x?.y returns y if x is not null and returns null otherwise. If y is not nullable then result of x?.y will become Nullable<T> where T is typeof(y)
x ?? y returns x if x is not null and returns y if it is. Type of x and y should be the same here.
Hope it helps
Edit
In the second look I see that you are using LINQ with IQueryable which can't use the null propagating operator. So in this case you don't have this option you can either load all the data using a ToList() run your query on them if the data size is small. Or you can load each part in separate queries (which due to the number of sub element I don't recommend). Alternatively you can use tools like this
My advice is the first option however may not be memory friendly on large datasets.
Sorry for the initial hasty answer. 🙏
Related
I have the following LINQ query:
houses.Where(x =>
x.A && user.B
||
(x.Address == null || user.Address == null ? false : x.Address.CountryCode == user.Address.CountryCode
))
I get an error when user.Address is null in:
user.Address.CountryCode
To avoid that I tried to use:
x.Address == null || user.Address == null
But it seems user.Address.CountryCode is still evaluated.
How can I avoid that?
It's definitely EF Core 3.0 query translator defect.
For now (and in general) you can avoid it by not using conditional operator ?: for criteria expressions which need to be converted to SQL query parameters (like your user.Address.CountryCode), but the equivalent logical binary expressions.
e.g. instead of
(x.Address == null || user.Address == null ? false : x.Address.CountryCode == user.Address.CountryCode)
use the equivalent
(x.Address != null && user.Address != null && x.Address.CountryCode == user.Address.CountryCode)
Personally I find this logic confusing. You might try:
( (x?.Address?.CountryCode != null && user?.Address?.CountryCode != null) ? x.Address.CountryCode == user.Address.CountryCode : false
If that doesn't work then there is something very odd going on with the Address or CountryCode properties! Probably and entity framework issue.
List<OfferDTO> offers = dbContext.Offer.Where(x => x.IsDeleted)
.OrderBy(i => i.OfferID)
.Skip(start).Take((length))
.Select(y => new OfferDTO
{
OfferStageValue = y.OfferStage.Value ?? null,
PropertyAddressLine1 = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.AddressLine1 : string.Empty,
PropertyAddressLine2 = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.AddressLine2 : string.Empty,
PropertyCity = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.City : string.Empty,
PropertyZip = (y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null) ? y.PropertyAuction.Property.Address.PostalCode : string.Empty,
})
.ToList();
In the above example, I am casting directly to an object with a select statement.
Certain properties need to be checked for a null value before their values are used in the new object.
However, as you see, different properties check the same value for null over and over again. Specifically here, you see:
(y.PropertyAuction.Property != null && y.PropertyAuction.Property.Address != null)
is checked a few times in a row.
In the ensuing SQL, you are seeing that the query is checking for null each time:
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent13].[AddressID] IS NOT NULL)) THEN [Extent14].[AddressLine1] ELSE #p__linq__1 END AS [C5],
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent15].[AddressID] IS NOT NULL)) THEN [Extent16].[AddressLine2] ELSE #p__linq__2 END AS [C6],
CASE WHEN (([Extent12].[PropertyID] IS NOT NULL) AND ([Extent17].[AddressID] IS NOT NULL)) THEN [Extent18].[City] ELSE #p__linq__3 END AS [C7]
Is there a way to check once and carry that over, or is this the best way to safely use these values?
NOTE
y is the base table
PropertyAuction,
Property, and
Address are all separate tables as well and may not contain data.
You can get a bit more elegant in the LINQ itself by switching to Query syntax, and using the let keyword, but I think you're going to find your generated SQL is (has to be) pretty similar:
var offers = (from o in dbContext.Offers
where o.IsDeleted
let p = o.PropertyAuction.Property
let a = p != null ? p.Address : null
orderby o.OfferID
select new OfferDTO
{
OfferStageValue = o.OfferStage.Value,
PropertyAddressLine1 = a != null ? a.AddressLine1 : string.Empty,
PropertyAddressLine2 = a != null ? a.AddressLine2 : string.Empty,
PropertyCity = a != null ? a.City : string.Empty,
PropertyZip = a != null ? a.PostalCode : string.Empty,
})
.Skip(start).Take(length)
.ToList();
EDIT
Just got a chance to put the full model into the compiler and check the SQL - as expected, the generated SQL is identical to the SQL generated from your original query; (i.e., even though extracted into a single check in the LINQ, the generated SQL inlines these checks four times, resulting in identical SQL code).
If your primary goal was cleaner LINQ code, at least this answer accomplishes that.
Here is an example of an assignment to a linq path pulled from code first...
applicants = appRegistrations
.ToList()
.Select(c => new ApplicantList() {
PartnerType = c.Participant != null ? c.Participant.PartnerType != null ? c.Participant.PartnerType.PartnerTypeName : "" : ""
});
Notice the null checks - is there a more elegant way I can write this code considering Participant AND PartnerType could be null?
I just hate checking for nulls on each property.
You could check if one of both are null:
List<ApplicantList> applicants = appRegistrations
.Select(ar => c.Participant == null || c.Participant.PartnerType == null
? "" : c.Participant.PartnerType.PartnerTypeName)
.Select(str => new ApplicantList { PartnerType = str })
.ToList();
You can shorten it a little bit:
PartnerType = c.Participant != null && c.Participant.PartnerType != null
? c.Participant.PartnerType.PartnerTypeName
: ""
When you construct the objects such as Participant, make sure that the properties are never null. Think if null is a valid value for a Participant? If not then you should never allow it to be null. Take a constructor parameter and add a guard clause to check for nulls. Otherwise initialize them to a default value.
Also, see NULL Reference Pattern.
I'm coding a search for an MVC app we're building, the thing is I would like to search by various properties of an object. In this particular case this is my expected behavior:
If both parameters are null or empty, return all.
If any parameter has a value, select all filtered by that parameter using Contains.
This is what I'm doing:
var model = _svc.GetList(q => q.Name == (string.IsNullOrEmpty(entity.Name) ? q.Name : entity.Name) &&
q.Description == (string.IsNullOrEmpty(entity.Description) ? q.Description : entity.Description));
This returns either all elements if both fields are null or empty, or any element that matches exactly the Name AND/OR Description.
The thing here is I would like this to behave as a Contains.
I've managed to get this working with one field:
var model = _svc.GetList(q => (string.IsNullOrEmpty(entity.Name) ||
q.Name.ToLower().Contains(entity.Name.ToLower()))).ToList();
But when I add the Description field, it throws a NullReferenceException: Object reference not set to an instance of an object.
Just to check I've tried this and it didn't work either:
var model = (from q in _svc.GetList()
where (string.IsNullOrEmpty(module.Name) ||
q.Name.ToLower().Contains(module.Name.ToLower()))
select q).ToList();
model = (from q in model
where (string.IsNullOrEmpty(module.Description) ||
q.Description.ToLower().Contains(module.Description.ToLower()))
select q).ToList();
Well, for a multi-optional-criteria search, you can do (asserting "module" is your "search class").
Simple, and (I think) more readable. Add a null check for the description and entity property of your model.
//take all elements
var model = _svc.GetList();
if (!string.IsNullOrEmpty(module.Description))
model = model.Where(m =>
m.Description != null &&
m.Description.ToLower().Contains(module.Description.ToLower());
if (!string.IsNullOrEmpty(module.Name))
model = model.Where(m =>
m.Name != null &&
m.Name.ToLower().Contains(module.Name.ToLower());
return module.ToList();
Null check, because a ToLower() on a null will raise a NRE !
This is a little ugly but should do the trick, you are getting the null reference because of entries with empty Description. If what you are doing with the Name is any clue, you are probably doing something like this q.Description.ToLower().Contains(..) without checking that q.Description is not null
var model = _svc.GetList(q =>
(string.IsNullOrEmpty(entity.Name) || string.IsNullOrEmpty(q.Name)
|| q.Name.ToLower().Contains(entity.Name.ToLower()))
&& (string.IsNullOrEmpty(entity. Description) || string.IsNullOrEmpty(q. Description)
|| q.Description.ToLower().Contains(entity. Description.ToLower())))
I am trying to perform left outer join on 2 objects and getting an error : Object reference not set to an instance of an object.
The objects look like that
var deliverables = OCHART.GetACAPValues(organization, ReportingPeriod, FiscalYear, "(09-10.10a) Outreach Significant").ToList();
var references = (from rf in OCHART.References where rf.RefType.Equals("09-10.10a") && rf.Comments.Equals("2") select rf).ToList();
In which deliverables might often return 0 records. Unfortunately I cannot just go and join two tables from database so deliverables must be an object.
Can somebody please point me in the right direction
Thanks,
My code is
var items = (from rf in references
join pt in deliverables on rf.Description equals pt.b into prt
from x in prt.Where(prt2 => prt2.a.Equals(audience)).DefaultIfEmpty()
where rf.RefType.Equals("09-10.10a") && rf.Comments.Equals("2")
select new
{
audience = (string)(audience == null ? "" : audience),
RefType = (string)(rf.RefType == null ? "" : rf.RefType),
RefOrder = (int)(rf.RefOrder == null ? 0 : rf.RefOrder),
refName = (string)(rf.Description == null ? "" : rf.Description),
collumn_attr = (string)(x.b == null ? string.Empty : x.b),
value = (int)(x.ACAP == null ? (int?)null : x.ACAP)
})
.OrderBy(o => o.RefOrder)
.Take(9)
.ToList();
EDIT:
After some more debuging it appears that I get error on following lines in my code
collumn_attr = (string)(x.b == null ? string.Empty : x.b),
value = (int)(x.ACAP == null ? (int?)null : x.ACAP)
I noticed even when I have values (added for testing) in deliverables and when values are matching the query will execute properly, but when there is no match in deliverable that's when I get the error message.
The issue is probably with handling the null values.
I think x is null and is causing a NullReferenceException in the following lines:
collumn_attr = (string)(x.b == null ? string.Empty : x.b),
value = (int)(x.ACAP == null ? (int?)null : x.ACAP)
This judgment is based on the from x in line's DefaultIfEmpty() call, typical of left-outer-joins.
In database code, you would write something like x.ACAP == null to detect the case where there was no matching join element. If you change this replace the 'x.property == null' checks with "x == null" checks, I suspect your problem will clear up.
There's still the problem with the second line - you're going to get an exception at run-time if you try to cast the value (int?)null to an integer. Using a meaningful default int value such as 0 in the case that x == null will clear that up.
If you step through your code, before the query is executed do you actually see "deliverables" and "references" being populated with data ?
Investigate all child tables/properties you're using in your query. The reason you're getting that error is most likely because one of the properties you're using while comparing is null.
.RefType .Comment for example.
Maybe .RefType is null and it's having problems sorting at the end. Difficult to say without seeing what's in those two collections.
Added after your comment:
Note that it's better not to use .Equals() when your variable could be null. Use == instead. Reference: http://www.dotnetperls.com/string-equals
Also imagine that (x.ACAP == null ? (int?)null : x.ACAP) returns a null.
You're casting that whole thing as an int : value = (int)(x.ACAP == null ? (int?)null : x.ACAP). Casting null as n int will obviously fail
Just for the record, the new Null-conditional operators in C# 6.0 could be used like this:
collumn_attr = x?.b ?? string.Empty,
value = x?.ACAP