LINQ's WHERE clause where COLUMN may be null - c#

I have a problem with making WHERE clause with LINQ. I tried to google it but I've only got answers on what to do if the external variable is the type of nullable integer... well, I've tried these methods the other way around (I have 0...1 relations in my dataset):
e.g.
int oldId = oldQuestion.id;
IEnumerable<possible_answer> possibleAnswersQuery =
from x in Mentor11Entities.possible_answers
where object.Equals(x.question_id, oldId)
select x;
List<possible_answer> possibleAnswers =
possibleAnswersQuery.ToList<possible_answer>();
or
int oldId = oldQuestion.id;
IEnumerable<possible_answer> possibleAnswersQuery =
from x in Mentor11Entities.possible_answers
where Convert.ToInt32(x.question_id ?? 0).Equals(oldId)
select x;
List<possible_answer> possibleAnswers =
possibleAnswersQuery.ToList<possible_answer>();
but I'm always getting the error that LINQ doesn't support certain functions... is there any way to get around the problem?

just use
where x.question_id != null && x.question_id == oldId
so your query should be:
IEnumerable<possible_answer> possibleAnswersQuery =
from x in Mentor11Entities.possible_answers
where x.question_id != null && x.question_id == oldId
select x;

Related

Linq Query using navigation properties and Where clause

I am trying to compose a linq query using navigation properties. I am selecting properties from 3 entities:
Lockers
SectionColumn
Contracts
I require ALL rows from the Lockers table where all the following conditions are met: the LockerTypeId = "308", .OutOfOrder != true, x.SectionColumn.SectionId == "52").
The query below without the condition x.SectionColumn.SectionId == "52" works and returns exactly what I require except rows with Section id of any value are returned as I would expect.
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ?
false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
When I add the condition 'x.SectionColumn.SectionId == "52"' as below I get the error "The cast to value type 'System.Int32' failed because the materialized value is null". Either the result type's generic parameter or the query must use a nullable type" in linqpad. SectionId is a string (varchar in SQL Server).
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
I would be grateful for assistance in correctly writing this query.
First off, your code might be a little more straight forward if you stick to pure LINQ. In that case, your code should look something like the following.
var results = from l in Lockers
where l.LockerTypeId == "308" && l.OutOfOrder != true && l.SectionColumn.SectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
If l.SectionColumn.SectionId represents valid navigational properties and is of type string, then this should work correctly.
You really haven't done a thorough job of describing the issue (and it looks like you didn't stick around to field questions), but if l.SectionColumn is nullable, you should be able to update your code to something like this.
var results = from l in Lockers
let sectionId = (l.SectionColumn != null) ? l.SectionColumn.SectionId : null
where l.LockerTypeId == "308" && l.OutOfOrder != true && sectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}

LINQ: Set a default value if navigation property is null

This is my current LINQ statement:
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID = l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer).FirstOrDefault().ContactID
};
The trouble I'm having is that the last item is often null. So when I try to convert "results" to a List, I get
{"The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."}
I don't want to make SelectedProposalEngineerID a nullable int, for problems that would cause down stream. How would I give it a value of 0 when it's null?
I have seen a LOT of other threads about this, but I can't seem to adapt any of their answers to this case.
Use DefaultIfEmpty extension method.
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID =
l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer)
.Select(contact => contact.ContactID)
.DefaultIfEmpty(0)
.FirstOrDefault()
};
Nullable<int> ID;
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID = (ID = l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer).FirstOrDefault().ContactID).HasValue ? ID.Value : 0;
};
A ternary operator should do the job. Assign the result to a variable, then if it's not null, cast the variable to int and return it, else return 0.

Null value in the result of a left outer join linq causes error

I have linq query, that left outer join two tables. I found if a value of a field returns null,, then I will get an error message:
"The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."
I copied my linq below:
var SrvRef = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from r in r_join.DefaultIfEmpty()
where s.svhAccID == accId &&
s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
r.refID
};
bool FBeenPaid = SrvRef.FirstOrDefault().svhBalance == 0M; //this causes error
How can I fix this problem?
I'm slightly surprised at the kind of error you're getting, but there are two places you need to take account of the possibility of the result being null:
Within the query, where r can be null. (If you don't want to match when there are no elements in r_join matching s, you shouldn't be using a left outer join)
In the result itself: you're using FirstOrDefault() which will return null if SrvRef is empty.
So at first glance it should probably be something like:
var query = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from r in r_join.DefaultIfEmpty()
where s.svhAccID == accId && s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
refId = r == null ? 0 : r.refID // Adjust to the appropriate type of refID
};
var result = query.FirstOrDefault();
bool beenPaid = result != null && result.svhBalance == 0m;
With C# 6, you can change the bottom two lines to:
bool beenPaid = query.FirstOrDefault()?.svhBalance == 0m ?? false;
Having said that:
You're not currently using refId in the result anyway - why are you including it in the result?
Are you sure you want a left outer join at all?
Are you sure that taking the first result is really what you want? What if there are multiple results in the join?
Is there any reason you're not doing the whole thing in a query? Something like:
var paid = db.SrvHeads
.Where(s => s.svhAccID == accId && s.svhHeadCnt == HeadId)
.Any(s => db.Refererrants.Any(r => s.svhReferrer == r.refID
&& s.svhBalance == 0m);
.. but just for the precise semantics you want.
I had a similar issue.
Cause: You are using from "r" in r_join.DefaultIfEmpty(). You cannot use same alias name for left outer join.
Solution: Use different alias name if DefaultIfEmpty() cases. Eg: rEmpty
I modified the below query and its working.
var SrvRef = from s in db.SrvHeads
join r in db.Referrants on s.svhReferrer equals r.refID into r_join
from rEmpty in r_join.DefaultIfEmpty()
where s.svhAccID == accId &&
s.svhHeadCnt == HeadId
select new
{
s.svhBalance,
refID = rEmpty == null ? 0 : rEmpty.refID
};
what i think is causing an error is that svhBalance is an int32 value type and you are accessing a null value returned by SrvRef.FirstOrDefault().
please try the following line and let me know if it helped you.
if svhBalance is an int value type
var SrvRefObj= SrvRef.FirstOrDefault(); bool FBeenPaid = (((SrvRefObj!=null)&&(SrvRefObj.svhBalance
!=null))?(SrvRefObj.svhBalance == 0):(false))
else if it's a decimal value type
var SrvRefObj= SrvRef.FirstOrDefault(); bool FBeenPaid = (((SrvRefObj!=null)&&(SrvRefObj.svhBalance
!=null))?(SrvRefObj.svhBalance == 0M):(false))

Method has no supported translation to SQL

My project is using MVC 4 C# LINQ to SQL.
For some reason the method used to get data for one of my properties is giving a "has no supported translation to SQL" error. The method to fetch this data is nearly identical to the method of another property in the same query except the one that works grabs a string where the one that doesn't gets a decimal.
Exact error code:
Method 'System.Decimal GetModelDifficulty(System.String)' has no supported translation to SQL.
I've tried numerous variations on the below code but I always get the same error as above:
public List<ProductionSchedule> GetBaseProductionSchedule(DateTime selectedDate)
{
var spaList = (from x in db.WO010032s
join y in db.MOP1042s on x.MANUFACTUREORDER_I equals y.MANUFACTUREORDER_I into x_y
where x.STRTDATE == selectedDate && (x.MANUFACTUREORDERST_I == 2 || x.MANUFACTUREORDERST_I == 3)
from y in x_y.DefaultIfEmpty()
select new ProductionSchedule()
{
MO = x.MANUFACTUREORDER_I,
BOMNAME = x.BOMNAME_I,
SpaModel = x.ITEMNMBR,
MoldType = GetMoldType(x.ITEMNMBR.Trim()),
SerialNumber = y.SERLNMBR,
Difficulty = GetModelDifficulty(x.ITEMNMBR.Trim())
}).OrderByDescending(x => x.Difficulty).ToList();
return spaList;
}
public string GetMoldType(string model)
{
return db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.MoldType).FirstOrDefault();
}
public decimal GetModelDifficulty(string model)
{
return (decimal)db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.Difficulty).FirstOrDefault();
}
Well I've tweaked the code around enough times to where I've stumbled on a variation that works:
public List<ProductionSchedule> GetBaseProductionSchedule(DateTime selectedDate)
{
var spaList = (from x in db.WO010032s
join y in db.MOP1042s on x.MANUFACTUREORDER_I equals y.MANUFACTUREORDER_I into x_y
where x.STRTDATE == selectedDate && (x.MANUFACTUREORDERST_I == 2 || x.MANUFACTUREORDERST_I == 3)
from y in x_y.DefaultIfEmpty()
select new ProductionSchedule()
{
MO = x.MANUFACTUREORDER_I,
BOMNAME = x.BOMNAME_I,
SpaModel = x.ITEMNMBR,
MoldType = GetMoldType(x.ITEMNMBR.Trim()),
SerialNumber = y.SERLNMBR,
Difficulty = GetModelDifficulty(x.ITEMNMBR)
}).ToList();
return spaList.OrderByDescending(x => x.Difficulty).ToList();
}
public string GetMoldType(string model)
{
return db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.MoldType).FirstOrDefault();
}
public decimal GetModelDifficulty(string model)
{
decimal difficulty = (String.IsNullOrEmpty(model)) ? 0M : Convert.ToDecimal(db.SkuModelDatas.Where(x => x.Value == model.Trim()).Select(x => x.Difficulty).FirstOrDefault());
return difficulty;
}
Why it worked when trapping for null string for x.ITEMNMBR (model parameter) in one method and not the other and needing to OrderByDescending outside of the main LINQ query, I have no idea.
Thanks for all the suggestions and help with this.
The problem is your query is calling code that LINQ cannot translate into SQL.
First try this, it may help. There may be a problem with your (decimal) cast. Modify your method GetModelDifficulty to the following:
public decimal GetModelDifficulty(string model)
{
return Convert.ToDecimal(db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.Difficulty).FirstOrDefault());
}
If that doesn't work I'm afraid you'll have to break your query down further to narrow down the issue. Use the documentation provided here: Standard Query Operator Translation (LINQ to SQL) to be sure that any extension methods you are using have supported translations.
If you run into a piece of code that cannot be translated, you may need to declare a separate variable with the necessary value already stored inside of it, that you can then use inside of your query.
I think it's because your call to FirstOrDefault() can return a null value. When you assign to a typed object you can use ? operator to signify that it can be null:
decimal? myDec = <some code returning decimal value or null>
Then you can check if myDec is null or not by:
myDec.HasValue

Is there a way I can check for null object in AddRange() method linq statement?

I want to check in the below code if no match is found it should not throw object reference null error.
itm.lstCustomziation.AddRange(
(from xx in db.vw_DressingCustomization
where xx.CatID == itm.HeaderName.Value && xx.ProductID == itm.ProductID
select new itmCustomization()
{
catId = (int)xx.CatID,
custType = customizationType.Dressing,
isCustomziationDisplay = xx.IsDefaultDisplay,
isFixLimit = (bool)xx.isDefaultLimit,
maxLimit = (short)xx.DefaultFreeCount,
itmName = xx.Description,
isItemDefault = xx.IsDefaultDisplay,
price = (double)xx.MainPrice,
proId = (int)xx.ProductID
}).ToList<itmCustomization>());
You should break it into multiple steps. First execute the query and get the results. Then you should check for null/count and based on that check call the add range method. It would be more readable as well.
Add check whether your nullable itm.HeaderName really has value before accessing that value:
where itm.HeaderName.HasValue && // check if value exists
xx.CatID == itm.HeaderName.Value && // otherwise here is the problem
xx.ProductID == itm.ProductID

Categories