linq select field type unknown - c#

I have the following code:
var query =
from product in products.AsEnumerable()
where product.Field<string>("Product") == "Phone"
select new
{
Name = product.Field<string>("Name"),
ProductNumber = product.Field<string>("ProductNumber"),
ListPrice = product.Field<Decimal>("Price")
};
But I am getting the following error:
Unable to cast object of type 'System.Int32' to type 'System.String'.
I assume that's because in the ProductNumber column I don't always have doesn't always have strings, and in the first row the ProductNumber is actually an int. I've tried converting and casting them to string, but didn't work.
How can I fix this?

From the documentation of Field<T>:
InvalidCastException [is thrown when] the value type of the underlying column could not be cast to the type specified by the generic parameter, T.
It looks like in your case the column type is a nullable integer, so you need to do something like this:
var query =
from product in products.AsEnumerable()
where product.Field<string>("Product") == "Phone"
select new
{
Name = product.Field<string>("Name"),
// Below, I am using ""+... idiom for a null-safe ToString
ProductNumber = ""+product.Field<int?>("ProductNumber"),
ListPrice = product.Field<Decimal>("Price")
};
The idea is to retrieve the value as an int, but accept data rows where the data is missing.
If you do not mind ProductNumber of your anonymous class being nullable ints instead of strings, remove the ""+ part of the expression.

First you fetch the records as enumerable with ProductNumber as string and then later on Enumerable have a conversion from string to int.

Related

Assigning values from dynamic List to model properties. Conversion Errors

New programmer having trouble assigning model property values from a stored procedure returning a dynamic list.
Error: cant implicitly convert.
and if I try using something like Convert.ToDouble(Resultlist[0]); that fails too.
The results list is returning expected values from the DB.
I am prrobably missing something very simple.
Model properties in this case are all doubles.
CODE:
public PricingOptionsModel GetPricing(PricingOptionsModel model)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(GlobalConfig.CnnString("Database")))
{
List<dynamic> Resultlist;
Resultlist = connection.Query("dbo.GetCharges", commandType: CommandType.StoredProcedure).ToList();
model.item1 = Resultlist[0];
model.item2 = Resultlist[1];
model.item3 = Resultlist[2];
model.item4 = Resultlist[3];
model.item5 = Resultlist[4];
model.item6 = Resultlist[5];
model.item7 = Resultlist[6];
model.item8 = Resultlist[7];
}
return model;
}
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot
implicitly convert type 'object' to 'double'. An explicit conversion exists (are you missing a cast?)'
In case of your stored procedure, it provides a single row with multiple columns. ResultList has 1 item of type object and it has multiple properties (item1, item2, ...).
So you want to assign it like this: model.item1 = ResultList[0].item1;.
If types returned from Dapper don't match, then you can also apply Convert there like this:
model.Item1 = Convert.ToDouble(ResultList[0].item1;.
You should cast values to double.
Instead this:
model.item1 = Resultlist[0];
Do this:
model.item1 = (double) Resultlist[0];

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

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 problem from SqlDataReader

Suppose i have this sql statement and I have executed a sql command to get a datareader:
"select 1 union select 2"
//.....
var rdr = cmd.ExecuteReader();
and now i want to read the value in the first column of the first row:
var myInt = (int)rdr.GetValue(0); //great this works
var myLong = (long)rdr.GetValue(0); //throws cast exception, even though you can cast int to long
So it appears the type you cast to in C# needs to match exactly the SQL type. I.E. If the sql type is bigint, you can only cast to long. If the sql type is int, you can only cast to int. No mix and match...
I just want to get something that works regardless of the type of integer c# asks for and sql returns, as long as you could theoretically cast one to the other. So if SQL Server gives me a floating point type, and I'm asking for an int, I want the truncated int you get from doing that cast.
My goal is to make this work with generics, so I can have this function work when the generic parameter doesn't exactly match the datatype in sql server:
List<T> GetFirstColumn<T>(string sql) where T : struct
{
//get connection, execute reader
// loop:
// lst.Add( (T) rdr.GetValue(0));
}
I'd like this to work for both statments:
var sql1 = "Select 1"; //sql int
var sql2 = "Select cast(1 as bigint)"; //sql equivalent of a long
var lst1 = GetFirstColumn<int>(sql1);
var lst2 = GetFirstColumn<int>(sql2);
Does anyone have a relatively painless way of doing this?
Like Fredrik says, the value from SqlDataReader is boxed. You can convert a boxed value to an int with Convert.ToInt32, like:
int i = Convert.ToInt32(read[0]);
This will try to convert even if SQL Server returns a bigint or a decimal.
System.Convert will take care of the conversion.
T GetValue<T>(SqlDataReader rdr)
{
var dbVal = rdr.GetValue(0);
var csVal = (T)System.Convert.ChangeType(dbVal, typeof(T));
}
Caveat: if T == Nullable<S>, you need to do some extra work with reflection to get the underlying type and call ChangeType with typeof(S) as the type parameter. Apparently, MS didn't update the ChangeType function with .NET 2.0 and the introduction of nullables. And if it's a nullable, and dbVal is DBNull, you can just return null.
object dbValue = 5;
//this throws
Convert.ChangeType(dbValue, typeof(int?));
//this works
if(dbValue == DBNull.Value || dbValue == null)
{
if(typeof(int?).IsNullable) //or is a class, like string
{return null;}
dbValue = null;
}
var type = GetUnderlyingType<int?>(); //== typeof(int)
Convert.ChangeType(dbValue, type);
I think your problem is that GetValue returns an object. This means that in the case of an int, you will get an int boxed in an object. Then you cannot directly cast it to a long but must first unpack it as an int:
var myLong = (long)(int)rdr.GetValue(0);
This will be quite tricky using generics, I would say. Well, you could make generic methods with two type arguments; one specifying what type the field is, and one specifying the type you want. But I don't really see the need; SqlDataReader already has methods for the various data types, such as GetInt32, GetInt64 and so on, so the generic method would not really give any added value in that case.

LINQ DefaultIfEmpty(), generating inferred argument error

I am trying to get the below linq query to return -1 if there isn't any current value. I was looking at this article on MSDN, here, and it seemed that DefaultIfEmpty() was what I wanted.
Unfortunately, I am getting a The type arguments cannot be inferred from the usage. Try specifying the type arguments explicitly. error.
I guess I am not sure what that means or what it is telling me to do. Can someone explain, please.
public static int CheckForDRIID(int personID)
{
using (var context = ConnectDataContext.Create())
{
var masterIndex =
(from applicationAssociation in context.tblApplicationAssociations
where applicationAssociation.ApplicationID == 1 && applicationAssociation.PersonID == personID
select applicationAssociation.PersonApplicationID).DefaultIfEmpty(-1).Single();
return Convert.ToInt32(masterIndex);
}
}
-1 is an int and applicationAssociation.PersionApplicationID isn't, so it doesn't know what to return. You could replace -1 with the same type as applicationAssociation.PersionApplicationID, "-1". Or specify the type like DefaultIfEmpty<string>. And a third options is to do the Convert.ToInt32 in the select.
That code works fine for me:
public static int CheckForDRIID(int personID)
{
var someAssociaction = new { ApplicationID = 1, PersonID = 1, PersonApplicationID = 1 };
var associactions = (new[] { someAssociaction }).ToList();
associactions.Add(new { ApplicationID = 2, PersonID = 2, PersonApplicationID = 2 });
int masterIndex =
(from applicationAssociation in associactions
where applicationAssociation.ApplicationID == 1 && applicationAssociation.PersonID == personID
select applicationAssociation.PersonApplicationID).DefaultIfEmpty(-1).Single();
return masterIndex;
}
I just created a sample set of associactions, because I dont have your datacontext. By the way: you can just declare masterIndex an int, because you know the type the expression will return, so you dont need a var.
So the cause for your problem lies somewhere else: the PersonApplicationID field is not an int, therefore the compiler does not know which type it should infer for the generic function DefaultIfEmpty.
Update:
Just tried the above example with setting the PersonApplicationID property to:
new Nullable<int>(1): Throws "Cannot implicitly convert type 'int?' to 'int'"
1f: Throws "Cannot implicitly convert type 'float' to 'int'"
"": Throws "The type arguments for method DefaultIfEmpty<TSource> cannot be inferred from the usage. Try specifying the type arguments explicitly."
So, I assume you are storing strings in your database's PersonApplicationID field. If so, either change your database, or parse the string to an int in the Linq query:
public static int CheckForDRIID(int personID)
{
using (var context = ConnectDataContext.Create())
{
int masterIndex =
(from applicationAssociation in context.tblApplicationAssociations
where applicationAssociation.ApplicationID == 1 && applicationAssociation.PersonID == personID
select int.Parse(applicationAssociation.PersonApplicationID)).DefaultIfEmpty(-1).Single();
return masterIndex;
}
}

Categories