This question already has answers here:
Is this a bug in Visual Studio 2010 compiler?
(7 answers)
AppSettings value is returning null in ternary operator
(1 answer)
Closed 3 years ago.
I have the following objects:
class Parent
{
public Child Child { get; set; }
}
class Child
{
public string Type { get; set; }
public int Value { get; set; }
}
Why does the following code throw a NullReferenceException?
var value = parent.Child != null &&
parent.Child.Type == "test"
? parent.Child.Value == 1
: parent.Child.Value == 2;
I would think the null-check would block the second part of the conditional from executing.
However the following code works:
var value = (parent.Child != null) &&
(parent.Child.Type == "test"
? parent.Child.Value == 1
: parent.Child.Value == 2);
So I think some operator precedence is at fault here...
Why wouldn't it throw a NullReferenceException?
var value = parent.Child != null &&
parent.Child.Type == "test"
? parent.Child.Value == 1
: parent.Child.Value == 2;
Let's rewrite your code with an interim variable:
bool condition = parent.Child != null && parent.Child.Type == "test";
var value = condition ? parent.Child.Value == 1 : parent.child.Value == 2;
And then with an if statement to make it even clearer:
bool value = false;
bool condition = parent.Child != null && parent.Child.Type == "test";
if (condition)
{
value = parent.Child.Value == 1;
}
else
{
vaue = parent.Child.Value == 2;
}
Your condition requires two things to be true: parent.Child isn't null, and parent.Child.Type is equal to "test".
If your condition is false, you use the "else" part of the ternary operator: parent.Child.Value == 2.
If your condition is false because parent.Child == null then you will be trying to access a null object's property in your else section, thus triggering a NullReferenceException.
As for why it's interpreted in this order, see this documentation page about C# operators and their precedence. As you can see, a && b has higher precedence than a ? b : c. a && b will therefore be executed before a ? b : c.
Related
I am looking for a solution to concatenate two string values and get null as a result if both are null.
None of string1 + string2, string.Concat(string1, string2), string.Join(string1, string2) work. Research shows that is due to the fact, that these methods internally treat null as empty string.
How to solve this?
Since your actual formula is
(a + b) ?? (c + d) // assumes that null + null == null in (a + b)
I suggest rewriting it into
a == null && b == null ? c + d : a + b
which provides the expected result:
if both a and b are null we have c + d
a + b otherwise
If you want to have null (not empty string) when all a, b, c, d are null:
a == null && b == null ?
c == null && d == null
? null
: c + d
: a + b;
Something like this?
public class StringTest
{
public string CustomConcat(string one, string two) =>
one == null && two == null
? null
: string.Concat(one, two);
[Test]
public void ConcatTest()
{
Assert.IsNull(CustomConcat(null, null));
Assert.AreEqual("one", CustomConcat("one", null));
Assert.AreEqual("two", CustomConcat(null, "two"));
Assert.AreEqual("onetwo", CustomConcat("one", "two"));
// finally, a test for (a + b) ?? (c + d)
Assert.AreEqual("threefour", CustomConcat(null, null) ?? CustomConcat("three", "four"));
}
}
Yes, different method calls treat different preconditions, but you can always create your own function to handle "custom" preconditions.
As the most simple option you can try something like this:
String concatenateStrings(string s1, string s1) {
return (s1 == null && s2 == null)? null : String.concat(s1,s2);
}
You could do this:
var textResult = string.Empty;
if (string.IsNullOrWhiteSpace(text1) &&
string.IsNullOrWhiteSpace(text2))
{
textResult = null;
}
else
if (!string.IsNullOrWhiteSpace(text1) &&
string.IsNullOrWhiteSpace(text2))
{
textResult = text1;
}
else
if (string.IsNullOrWhiteSpace(text1) &&
!string.IsNullOrWhiteSpace(text2))
{
textResult = text2;
}
textResult = $"{text1} {text2}";
or in a better way:
textResult = (!string.IsNullOrWhiteSpace(text1) &&
string.IsNullOrWhiteSpace(text2)) ?
text1 :
(string.IsNullOrWhiteSpace(text1) &&
!string.IsNullOrWhiteSpace(text2)) ?
text2 :
(string.IsNullOrWhiteSpace(text1) &&
!string.IsNullOrWhiteSpace(text2)) ?
text2 :
$"{text1} {text2}";
I am wondering if it is possible to turn this statement of code into a ternary if statement.
var placeHolder = await Source.EntityOrDefaultAsync<_Item>(item => item.CompanyID == order.CompanyID && item.ItemID == od.ItemID);
if (placeHolder.TaxedAllCountryRegions = true || placeHolder.TaxedFed == 1 && placeHolder.TaxedState == 1)
{
decimal.TryParse(placeHolder.HandlingFee, out decimal trueFee);
od.HandlingFee = trueFee * od.Quantity;
}
I tried formatting it like this- but don't think it quite works.
var placeHolder = await Source.EntityOrDefaultAsync<_Item>(item => item.CompanyID == order.CompanyID && item.ItemID == od.ItemID);
return (placeHolder.TaxedAllCountryRegions = true || placeHolder.TaxedFed == 1 && placeHolder.TaxedState == 1) ?
decimal.TryParse(placeHolder.HandlingFee, out decimal trueFee)
: false;
od.HandlingFee = trueFee * od.Quantity;
As canton7 already commented, you might want to use == true instead of = true. Probably
also the part after || must be embedded in parenthesis.
Apart from that, you want also assign the HandlingFee, so a conditional operator alone doesn't help. But why dont you use a bool variable?
bool computeHandlingFee = placeHolder.TaxedAllCountryRegions || (placeHolder.TaxedFed == 1 && placeHolder.TaxedState == 1);
if(computeHandlingFee && decimal.TryParse(placeHolder.HandlingFee, out decimal trueFee))
{
od.HandlingFee = trueFee * od.Quantity;
}
return computeHandlingFee;
Maybe you want to include the decimal.TryParse in the computeHandlingFee-logic.
I have a table that I am filtering on.
There is a filter for values 'include' which can be true or false.
I have a filter that has 3 options: true, false, & all.
So, when the filter is true, it should return rows where include = 'true'; when the filter is 'false', return where include = false; and when 'all' return where include = true or false.
Here is my code, that is not working, but I think it should be.
private ICollection<AggregationEntityViewModel> getEntities(AggregationPracticeDetailsViewModel apdvm)
{
bool? filterInclude = Convert.ToBoolean(apdvm.Filter_IncludeValue);
var a = (from e in _repository.GetAll<Entity>()
where e.include == filterInclude != null ? (bool)filterInclude : (true || false)
select e
return a;
}
It is currently returning 0 rows when filter is set to 'All' or 'False', and returning all rows when set to 'Yes'.
FYI, I have ommitted lots of code for clarity's sake.
Please help...thanks!
*EDIT: I've displayed all the code, so you can see why I want to keep it all in linq query. Thanks for all the offered solutions. I see that most solutions involve using Linq Extension methods. Is there anyway to do it in inline linq query? *
bool? filterInclude = Convert.ToBoolean(apdvm.Filter_IncludeValue);
var a = (from e in _repository.GetAll<Entity>()
from u in e.Users
where (e.AuditQuestionGroupId != null ? e.AuditQuestionGroupId : 0) == this.LoggedInEntity.AuditQuestionGroupId
&& e.BatchNumber != null && e.BatchNumber.StartsWith(apdvm.Filter_BatchNumber == null ? "" : apdvm.Filter_BatchNumber)
&& e.Name != null && e.Name.ToLower().StartsWith(apdvm.Filter_EntityName.ToLower())
&& e.EntityState != null && e.EntityState.ToLower().Contains(apdvm.Filter_StateValue == null ? "" : apdvm.Filter_StateValue.ToLower())
&& u.NIAMembershipId != null && u.NIAMembershipId.Contains(apdvm.Filter_MemberNo == null ? "" : apdvm.Filter_MemberNo)
from p in e.PracticeProfiles.DefaultIfEmpty()
join ea in _repository.GetAll<EntityAggregate>() on e.EntityId equals ea.EntityId into eas
from ea in eas.DefaultIfEmpty()
where ea.include == filterInclude != null ? (bool)filterInclude : (true || false)
group e by new { entity = e, profile = p, ea = ea } into newGroup
orderby newGroup.Key.entity.Name
select new AggregationEntityViewModel()
{
Id = newGroup.Key.ea == null ? 0 : newGroup.Key.ea.Id,
EntityId = newGroup.Key.entity.EntityId,
Include = newGroup.Key.ea == null ? (true || false) : (bool)newGroup.Key.ea.include,
BHAddress = newGroup.Key.profile == null || newGroup.Key.profile.soloOffice == null ? false : (bool)newGroup.Key.profile.soloOffice,
Incorporated = newGroup.Key.profile == null || newGroup.Key.profile.company == null ? false : (bool)newGroup.Key.profile.company,
MajorityOwned = newGroup.Key.profile == null || newGroup.Key.profile.capital == null ? false : (bool)newGroup.Key.profile.capital,
MajorityVoting = newGroup.Key.profile == null || newGroup.Key.profile.votingRights == null ? false : (bool)newGroup.Key.profile.votingRights,
Name = newGroup.Key.entity.Name,
Partnership = newGroup.Key.profile == null || newGroup.Key.profile.partnership == null ? false : (bool)newGroup.Key.profile.partnership,
PublicAccountant = newGroup.Key.profile == null || newGroup.Key.profile.publicAccountant == null ? false : (bool)newGroup.Key.profile.publicAccountant,
Trust = newGroup.Key.profile == null || newGroup.Key.profile.operatingTrust == null ? false : (bool)newGroup.Key.profile.operatingTrust,
TrustDeed = newGroup.Key.profile == null || newGroup.Key.profile.deed == null ? false : (bool)newGroup.Key.profile.deed
}).ToList();
return a;
Convert.ToBoolean returns bool, not bool?, so there is no way filterInclude != null is true.
You should use following pattern instead of ternary operator within where clause:
var query = _repository.GetAll<Entity>();
if (apdvm.Filter_IncludeValue == "true")
query = query.Where(x => x.include == true);
else if (apdvm.Filter_IncludeValue == "false")
query = query.Where(x => x.include == false);
return query;
I assumed apdvm.Filter_IncludeValue is a string (and that's why you tried to call Convert.ToBoolean on it).
You could use
private ICollection<AggregationEntityViewModel> getEntities(
AggregationPracticeDetailsViewModel apdvm)
{
bool? filterInclude = apdvm.Filter_IncludeValue.ConvertToNullable<bool>();
var a = (from e in _repository.GetAll<Entity>()
where !filterInclude.HasValue || ea.include == filterInclude.Value
select new AggregationEntityViewModel()
{
Include = newGroup.Key.ea == null
? (true || false)
: (bool)newGroup.Key.ea.include,
}
return a;
}
just remove your (true||false) and add filterInclude == null in the where
For Nullable Value (taken from Convert string to nullable type (int, double, etc...))
public static T? ConvertToNullable<T>(this String s) where T : struct
{
try
{
return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
}
catch (Exception)
{
return null;
}
}
There is an other solution:
var query = from e in _repository.GetAll<Entity>();
if (filterInclude.HasValue)
{
// when filterInclude is null (it means **ALL**),
// do not filter otherwise - check the flag
query = query.Where(entity => entity.Include == filterInclude.Value);
}
// or one-line:
// query = query.Where(entity => filterInclude == null
// || entity.Include == filterInclude.Value);
var a = query.Select(entity => new AggregationEntityViewModel { .... });
return a;
Other problem is that Convert.ToBoolean never returns null. You should create own method to parse apdvm.Filter_IncludeValue.
In order to convert to nullable type, you colud use the generic method:
public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
Nullable<T> result = new Nullable<T>();
try
{
if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
result = (T)conv.ConvertFrom(s);
}
}
catch { }
return result;
}
Source.
Usage:
var filterInclude = apdvm.Filter_IncludeValue.ToNullable<bool>();
You can make it easier with fluent syntax like this:
private ICollection<AggregationEntityViewModel> getEntities(AggregationPracticeDetailsViewModel apdvm)
{
var query = _repository.GetAll<Entity>();
if(apdvm.Filter_IncludeValue != 'all')
{
var value = Convert.ToBoolean(apdvm.Filter_IncludeValue);
query = query.Where(q => q.include == value)
}
return query.Select(q => new AggregationEntityViewModel {...}).ToArray();
}
no need to evaluate string to nullable bool or smth. Same as no need to do strange boolean expressions.
This question already has answers here:
Only do Where condition if a value is passed in
(4 answers)
Closed 9 years ago.
public List<..> GetSomething(int column1Value, int column2Value, string column3Value)
{
from t1 in this.DataContext.Table1
where t1.column1 == column1Value &&
t1.column2 == column2Value &&
t1.column3 == column3Value
}
Now I want to re-use the above query i.e. don't want to duplicate it, but the ONLY difference is I want the t1.column3 == column3Value to be option so I call this like:
GetSomething(1,2,"HELLO");
and
GetSomething(1,2);
Is it possible to make this part of the where clause conditional? Meaning if you pass in "", then it ignores that clause?
Yes, just break your statement into two parts like this:
var query = this.DataContext.Table1
.Where( x => column1 == column1Value && x.column2 == column2Value);
if ( column3Value != "" )
query = query.Where( x => x.column3 == column3Value);
// Your existing processing of query
Try this:
public List<..> GetSomething(int column1Value, int column2Value, string column3Value = null) {
from t1 in this.DataContext.Table1
where t1.column1 == column1Value &&
t1.column2 == column2Value &&
(column3Value == null ? true : t1.column3 == column3Value)
}
I suggest ou to define OR operator in order to define such as optional
Make the parameter optional, and test for the lack of it:
public List<..> GetSomething(int column1Value, int column2Value,
string column3Value = null)
{
from t1 in this.DataContext.Table1
where t1.column1 == column1Value &&
t1.column2 == column2Value &&
(column3Value == null || t1.column3 == column3Value)
}
How Can I use Condition in Where Clause?
user can select section,product and model from list and see the result.
i add an item See All in all the filed,if user select See All,he can see all product in all section.so i want to write
a query that check for value if every property equal -1 dot bring in where condition.
//My Model
struct Model
{
public int SectionCode{get;set;}
public int ProductCode{get;set;}
public int ModelCode{get;set;}
}
var query=DBContext.Model.Where(data=>data.ModelCode==_ModelCode//if ModelCode!=-1
&& ProductCode==_ProductCode//if ProductCode!=-1
&& SectionCode==_SectionCode//if SectionCode!=-1 )
I know that i can write it with some if but i have to check a lot of condition.so i want to know, how can i write if in where Clause?
Just don't add a where clause if you don't need it, i.e:
IQueryable<Model> query = DBContext.Model;
if(_ModelCode != -1)
{
query = query.Where(data=>data.ModelCode==_ModelCode);
}
if(_ProductCode!= -1)
{
query = query.Where(data=>data.ProductCode==_ProductCode);
}
if(_SectionCode!= -1)
{
query = query.Where(data=>data.SectionCode==_SectionCode);
}
Try this :
var query=DBContext.Model.Where(data => (ModelCode == -1 || data.ModelCode == _ModelCode)
&& (ProductCode == -1 || ProductCode == _ProductCode)
&& (SectionCode == -1 || SectionCode == _SectionCode)
You could achieve this using logical operators:
var query = DBContext.Model.Where(data =>
(_ModelCode == -1 || data.ModelCode == _ModelCode)
&& (_ProductCode == -1 || data.ProductCode == _ProductCode)
&& (_SectionCode == -1 || data.SectionCode == _SectionCode))