conditional linq query behavior - c#

I have a feeling the title is misleading, please edit if you choose to. when I do this:
var q = (condition)?(from ...Select(..)): (from.. Select(..));
I get a error at ":" saying
Type of conditional expression could not be determined because
there is no implicit conversion between anonymous types.
But if I do:
var b = some base linq query;
var q = (condition)?(use b here one way):(use b here differently);
no complaints. Why? How is second way different?
Edit:
Everywhere, my final projections are the same. (Final .Select(....) everywhere has same fields)
Edit2:
I apologize.. typo on my part. Select()s everywhere were not the same. Method 1 works fine too if the projections 'match'

It think the error message explains this:
Type of conditional expression could not be determined because
there is no implicit conversion between anonymous types.
This
var x = 0 < 2 ? new { a = 1 } : new { a = 2 }
would compile. but this
var x = 0 < 2 ? new { a = 1 } : new { b = 2 };
would give the error above because {a=1} and {b=2} are not the same anonymous types.

If you did a cast after your LINQ query everything would be fine.
Its sort of the same problem that you get then you do something like
int? val = true ? 1 : null;
That won't work, but if you cast the null like this:
int? val = true ? 1 : (int?)null;
It does.

A conditional expression needs the two operands it's evaluating to be the same type. So if you do
var a = (condition)? "A" : "B";
... both "A" and "B" are of the same type (string), so the result, a, will be of type string.
What your error message is telling you is that your two operands (the two from ... Select statements) evaluate to anonymous types, but not the same anonymous type, and it can't convert one anonymous type into another. Thus it doesn't know what type the result, q, should be.
While I am not 100% sure, I believe that even if your two expressions are exactly the same, they will be different anonymous types. At any rate, the fact that you got that error indicates that they are not the same anonymous type.
With your second set of statements, you first set b to be equal to the result of a linq query. Hence it has a type. Although your statement does say so, the fact that the code compiles implies that (use b here one way) and (use b here differently) return results of the same type. If they both return an instance of the same type as b, for example, they will be of the same type.

Related

The cast to value type 'Boolean' failed because the materialized value is null

public ActionResult Votation(int id=0)
{
var events = db.Events_Info_tbl.Where(x => x.is_active == true).FirstOrDefault();
//query the first category
List<CampaignManager_tbl> candidates = new List<CampaignManager_tbl>();
candidates = (from cat in db.Events_Category_tbl
join can in db.Candidates_Info_tbl
on cat.events_category_id equals can.events_category_id
where cat.events_info_id == events.events_info_id
select new CampaignManager_tbl {
events_category_name = cat.events_category_name,
candidates_fullname = can.candidates_fullname,
candidates_info_id = can.candidates_info_id,
vote_no = cat.vote_no.Value,
isSelected = can.isSelected.Value,
events_category_id = cat.events_category_id
}).ToList();
return View(candidates);
}
This code was working before but now I've got this error: The cast to value type 'Boolean' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
What's wrong with this? I didn't change any of my codes before. It just this time I've got an error.
I ran into this error because of a different problem. In my query I am selecting specific columns from an account member and its associated account:
DBContext.Set<AccountMember>()
.Include(am => am.Account)
.Select(am => new
{
am.ID,
am.IsPrimary,
Account = new { am.Account.ID, am.Account.DoNotEmail }
});
Both of my boolean properties, IsPrimary and DoNotEmail are non-nullable so the resulting property type of their properties in the anonymous type is bool. The exception occurs when an AccountMember does not have an Account. Since am.Account is null, the expression am.Account.DoNotEmail returns null. But the anonymous type's DoNotEmail property inferred it was of type bool which cannot be set to null. The solution is to give it a hint that this should be a nullable bool by casting the property to a bool?:
DBContext.Set<AccountMember>()
.Include(am => am.Account)
.Select(am => new
{
am.ID,
am.IsPrimary,
Account = new { am.Account.ID, DoNotEmail = (bool?)am.Account.DoNotEmail }
});
Hapily EF is smart enough to ignore this cast and not blow up trying to convert it to SQL.
I'm going out on a limb here, but I'd guess it has to do with this line:
isSelected = can.isSelected.Value,
If when the two tables are joined, one of them does not return a value for that particular field, it will be null. It appears the type here is a nullable of some type - probably bool?, based on the naming - and that would match your error.
Try to replace it with the following:
isSelected = can.isSelected.HasValue ? can.isSelected.Value : false,
Of course, you can replace the default false here with true, if that makes more sense in your case.
Edit: Note that you should probably do the same for the other column you use in a similar way (I'm assuming this is returning an int?):
vote_no = cat.vote_no.HasValue ? cat.vote_no.Value : 0
Is db.Candidates_Info_tbl.IsSelected a bit field?
If so, the query could likely result in
isSelected = can.isSelected.Value,
with isSelected being null, whereas you probably declared isSelected as bool in CampaignManager_tbl class.
Change the isSelected to bool in the CampaignManager_tbl class and try again.
if your field type is int while some value is null, then you can use case-when and convert to bit like below
SELECT CASE WHEN FieldName IS NOT NULL THEN CONVERT(BIT,FieldName) ELSE CONVERT(BIT,0) END AS NewFieldName

SelectMany cannot be inferred from the usage [duplicate]

This question already has an answer here:
SelectMany() Cannot Infer Type Argument -- Why Not?
(1 answer)
Closed 7 years ago.
I get the following error when I try to compile my code:
The type arguments for method
'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable,
System.Func>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
List<RowEntry> entries = ...
List<RowArgument> arguments = ...
var argumentsVal = entries.SelectMany((RowEntry entry) =>
(IEnumerable<RowArgumentVal>)arguments.SelectMany((RowArgument arg) =>
new RowArgumentVal()
{
Action = "X"
, EntryID = entry.ID
, ArgID = arg.ID
, Val_BIT = true
, Val_DATE = new DateTime(2014, 01, 04)
, Val_DATETIME = new DateTime(2014, 01, 04)
, Val_DECIMAL = 4.6M
, Val_INT = 88
, Val_TIME = new TimeSpan(6, 0, 0)
}
).Cast<RowArgumentVal>()).Cast<RowArgumentVal>().ToList();
I don't get how I can "type" this even further...
The problem is that the inner SelectMany isn't applicable there, and you probably meant Select.
var argumentsVal = entries.SelectMany(entry =>
arguments.Select(arg => new RowArgumentVal())).ToList();
Each entry will be mapped into an IEnumerable<RowArgumentVal> according to the arguments.
Imagine the outer SelectMany was a simple Select, and it would generate List<IEnumerable<RowArgumentVal>>. But because it is SelectMany, it will "flatten" the result into a simple List<RowArgumentVal>.
The SelectMany method expects a mapping to IEnumerable<T> - not T. Your original code would be valid if RowArgumentVal just happened to implement the IEnumerable<T> interface, which I suppose isn't the case.
That seems to be a cartesian product from both lists since there is no relation between them. You may want to join them but it's not exactly clear how.
Here is a more readable and compiling way to produce a cartesian product:
var query = from entry in entries
from argument in arguments
select new RowArgumentVal
{
Action = "X", EntryID = entry.ID, ArgID = argument.ID, // ...
};
List<RowArgumentVal> argumentsVal = query.ToList();
If you want a cartesian product try doing the following
var argumentsVal = from e in entries
from a in arguments
select new RowArgumentVal(...)
As an aside, the "further typing" you could have done would be to give the type arguments to the generic method calls. In particular, if you had changed the second SelectMany to SelectMany<RowArgument, RowArgumentVal> you would have got the errors
Cannot implicitly convert type RowArgumentVal to System.Collections.Generic.IEnumerable<RowArgumentVal>. An explicit conversion exists (are you missing a cast?)
Cannot convert lambda expression to delegate type System.Func<RowArgument,int,System.Collections.Generic.IEnumerable<RowArgumentVal>> because some of the return types in the block are not implicitly convertible to the delegate return type
which would maybe have led you to the other answers here - that you were trying to call a method that expected a sequence, but you were giving it a single object.
(Or in trying to decide which type arguments to add, you would have realised what was wrong sooner.)

Confusion with Enumerable.Select in C#

I am new to the C# and .NET world. I am trying to understand the following statement.
var xyz = Enumerable.Repeat(obj, 1).ToList();
var abc =
xyz.Select(xyzobj => new res {
foo = bar,
xyzs = new [] {xyzobj},
}).ToList();
I understand that Select takes in an object and a transformer function and returns a new form of the object. But here, it takes in a lambda expression with an enum value and another object.
I am little confused. Is the statement above similar to
var abc = xyz.Select(xyzobj => {
//some work with xyzobj
//and return object.
}).ToList();
Can somebody explain the above statement actually does, my head just spins around with these statements all around in my new work location.
Can somebody direct me to good resources to understand lambda expressions and Enumeration.
There are two main types of lambda expressions in C#.
Expression lambdas like this:
x => foo(x);
This takes a parameter x, and performs some transformation on it, foo, returning the result of foo(x) (though technically it may not return a value of the result type of foo(x) is void).
Statement lambdas look like this:
x => {
// code block
}
This takes a parameter, x and performs some action on it, (optionally returning a value if an explicit return is provided). The code block may be composed of multiple statements, meaning you can declare variables, execute loops, etc. This is not supported in the simpler expression lambda syntax. But it's just another type of lambda.
If it helps you can think of the following as being equivalent (although they are not perfectly equivalent if you are trying to parse this as an Expression):
x => foo(x);
x => { return foo(x); }
Further Reading
Lambda Expressions (C# Programming Guide)
// Creates a IEnumerable<T> of the type of obj with 1 element and converts it to a List
var xyz = Enumerable.Repeat(obj, 1).ToList();
// Select takes each element in the List xyz
// and passes it to the lambda as the parameter xyzobj.
// Each element is used to create a new res object with object initialization.
// The res object has two properties, foo and xyzs. foo is given the value bar
// (which was defined elsewhere).
// The xyzs property is created using the element from xyz passed into the lambda
var abc = xyz.Select(
xyzobj => new res {
foo = bar,
xyzs = new [] {xyzobj},
}).ToList();

How to Assign a Query Result to an Integer Array

public List<Workflow> GetMyWorkflows(int[] MyRoles)
{
int[] myWorkflowIDs = new int[] { };
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID;
var distinctWorkflows = query.Distinct();
myWorkflowIDs = distinctWorkflows.toArray();
return myWorkflowIDs;
}
In this method I want to retrieve an array of workflows that a user can
access.
I get the following error : Cannot implicitly convert type 'int?[]' to 'int[]'
I want to retrieve an array of workflows
But your method must return a List<Workflow> or a List<int>.
So you should skip the array idea. The other issue is between int and int?. You can solve that in the select clause with select w.WorkflowID.Value or select w.WorkflowID ?? 0. Or simply select w for a List<Workflow>.
Also it is a good idea to dispose a context when it becomes unreachable.
public List<int> GetMyWorkflows(int[] MyRoles)
{
using (RapidWorkflowDataContext context = new RapidWorkflowDataContext())
{
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID ?? 0;
// select w; to return a List<WorkFlow>
var distinctWorkflows = query.Distinct();
return distinctWorkflows.ToList(); // ToList because we are closing the Context
}
}
I'm going to guess that WorkflowID is of type int?. If you are certain that it cannot be null, change your central query to:
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID.Value;
This will ensure that query is now of type IEnumerable<int> instead of IEnumerable<int?>, with the int following on throuhh the Distinct() and ToArray() functions.
This seems like a pretty good error to me
Cannot convert type 'int?[]' to 'int[]'
You must have an array of type int? and be trying to implicitly convert it to int.
Therefore you have two options - stop trying to implicitly convert, and allow the result to be int?[], like this:
int?[] myWorkflowIDs = new int?[] { };
or force the convert to take place, like this:
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select (int)w.WorkflowID;
// or w.WorkflowID ?? 0; as necessary
So int? can also be written Nullable<int> which is basically an int that can take null values. For example:
int? nullableNumber = 5; // Set to a value
nullableNumber = null? // Set to null (which is possible because int? is nullable).
As you can imagine, Nullable<int> is useful for databases because sometimes you might have a column that has null values, and so this type gives a useful means of mapping to this sort of value. The problem, though is that in your code you have to deal with two different types, int vs. int?. You can cast between the two values by using:
// If the nullable-integer is not-null then use it's value, else default to `0`.
int nonNullable = nullableNumber ?? 0;
which will replace nulls with 0 if the value is null. Or you can just store your myWorkflowIDs in a nullable value (Nullable<int>[] or int?[]), which semantically better reflects what the column value in the database actually is.

Casting List<x> to List<y>

The following code works:
List<JsonStock> stock = new List<JsonStock>();
foreach(tblStock item in repository.Single(id).tblStocks)
stock.Add((JsonStock) item);
So naturally you'd think that this code would work too:
List<JsonStock> stock = repository.Single(id).tblStocks.Cast<JsonStock>().ToList()
But I get the error Invalid cast operation - does anybody know why that might happen?
UPDATE
tblStocks is a list of LINQ to SQL object, tblStock.
JsonStock is a simplified version of the tblStock class and gets returned to a webpage as a JSON object.
The following operator was built to do the casting:
public partial class tblStock{
public static explicit operator JsonStock(tblStock stock){
JsonStock item = new JsonStock
{
boxes = stock.boxes,
boxtype = stock.tblBoxType.name,
boxtype_id = stock.boxtype_id,
grade = stock.grade,
packrate = stock.packrate,
weight = stock.weight
};
return item;
}
}
Cast is used to change a non-generic collection into a generic one, i.e. it performs an unboxing operation. It can't be used the way you want.
When you have a look at the implementation of Cast and the CastIterator it uses, you see, that it takes an object and casts it to the specified type:
foreach (object current in source)
{
yield return (TResult)current;
}
This only works if current really is a TResult. No custom conversions are applied in this case.
This is the default behavior, you can test it yourself:
double d = 0.0;
object tmp = d;
int i = (int)tmp; // throws the same exception you are getting
What you want is best achieved with a simple Select if tblStocks is a generic enumerable:
List<JsonStock> stock = repository.Single(id).tblStocks
.Select(x => (JsonStock)x).ToList();
Or, if tblStocks is a non-generic enumerable, you need to combine Cast and Select:
List<JsonStock> stock = repository.Single(id).tblStocks.Cast<tblStock>()
.Select(x => (JsonStock)x).ToList();
This will first unbox the objects in tblStocks to their real type (tblStock) and then cast it to the type you want (JsonStocks).
implicit and explicit conversion operators are ignored by Cast. In your case that means that
public static explicit operator JsonStock(tblStock stock)
is ignored by Cast they are however not ignored in the foreach case
Instead of using Cast, consider using OfType. In Cast, if the item you are processing isn't the destired type, you will get the InvalidCastException. With OfType, it will trap for invalid cast and only return items that actually are the type that you are looking for.
List<JsonStock> stock = repository.Single(id).tblStocks.OfType<JsonStock>().ToList()
If you return empty lists however, I would suspect that your tblStocks actually is not returning JsonStocks and you are trying to project some other type (tblStock?) into a DTO (JsonStock). If the later is the case, you need to use Select to project into the new type from the underlying type.
List<JsonStock> stock = repository.Single(id).tblStocks
.Select(stock => new JsonStock
{
Id = stock.Id,
Val1 = stock.Val1,
Val2 = stock.Val2,
...
}
.ToList();
Ahhh, the wonders of explicit operator overloads.
So to fix your problem you might want to call a Select beforehand.
List<JsonStock> stock = repository.Single(id).tblStocks.Select(x => (JsonStock)x).ToList()
However i'd say that this operator overload is something you should get rid of. Consider replacing it with a copy constructor like implementation
class JsonStock
{
public JsonStock(tblStock other)
{
// copy values here
}
}
tblStocks.Cast<JsonStock>() performs a cast.
(JsonStock) item performs a cast or applies a custom-defined conversion.
Since tblStock is a LINQ to SQL class and JsonStock is a custom class created by you, none is a subtype of the other. Thus, you cannot cast between the two.
To fix this, you can use the Select clause of LINQ and manually convert the elements:
List<JsonStock> stock = repository.Single(id).tblStocks
.Select(item => (JsonStock) item).ToList();

Categories