I have an error with the method below where I am trying to add data from a combobox to a datagrid and then after I add the data I want to calculate the total amount of the 'ItemSellingPrice' and display that amount in a label.
using (TruckServiceClient TSC = new TruckServiceClient())
{
var item = cmbAddExtras.SelectedItem as ExtraDisplayItems;
if (item != null)
{
var displayItem = new List<ExtraDisplayItems>
{
new ExtraDisplayItems
{
displayItems = item.displayItems,
ItemId = item.ItemId,
ItemCode = item.ItemCode,
ItemDescription = item.ItemDescription,
ItemSellingPrice = item.ItemSellingPrice,
}
};
dgAddExtras.Items.Add(item);
var subTotalExtras = item.displayItems.Sum(x => x.ItemSellingPrice.GetValueOrDefault(0)); //Here
lblSubTotalExtrasAmount.Content = "R" + subTotalExtras;
}
}
The error that I get is:
Value cannot be null.
Does anyone have any ideas why this is happening?
EDIT: This is where I set the displayItems in my class
public class ExtraDisplayItems
{
public List<ExtraDisplayItems> displayItems;
public int ItemId { get; set; }
public string ItemCode { get; set; }
public string ItemDescription { get; set; }
public double? ItemSellingPrice { get; set; }
}
Thanks to Gert Arnold, the actual issue is that the item.displayItems is null (so not inside the lambda, since that works perfectly fine).
Some proof. This gets your exact error message:
double?[] x = null;
double? sum = x.Sum(y => y);
(The reason you don't get a NullReferenceException is that you actually call a static method (an extension method), so technically the reference isn't null but the argument is).
You should check where you set item.displayItems or prevent calling Sum on a item.displayItems with value null.
Related
Here is the GET REQUEST
var destname = textBox1.Text;
var client1 = new RestClient("https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=" + destname);
var request1 = new RestRequest(Method.GET);
request1.AddHeader("x-rapidapi-key", "");
request1.AddHeader("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com");
IRestResponse response1 = client1.Execute(request1);
var results = JsonConvert.DeserializeObject<DestinationName>(response1.Content);
And here is the classes
public partial class Place1
{
public string PlaceId { get; set; }
public string PlaceName { get; set; }
public string CountryId { get; set; }
public string RegionId { get; set; }
public string CityId { get; set; }
public string CountryName { get; set; }
}
public partial class DestinationName
{
public List<Place1> Places { get; set; }
}
When I do what is below I should be getting ES-sky which is the first element of the list but for some reason it gives me the last element of the list.
foreach (var a in results.Places)
{
label1.Text = a.PlaceId;
}
Here is the list
ES-sky
BCN-sky
ALC-sky
AGP-sky
MAD-sky
PMI-sky
IBZ-sky
TENE-sky
TFS-sky
TFN-sky
How would I adapt the code so that my output is ES-sky and not TFN-sky.
You are looping through the list, every time its writing value to "label1.Text". Use SingleOrDefault()/FirstOrDefault(). Dont use foreach loop.
Example:
var firstValue=results.Places.FirstOrDefault();
label1.Text = firstValue.PlaceId;
Instead of iterating through the loop here:
foreach (var a in results.Places)
{
label1.Text = a.PlaceId;
}
Just get the first value with appropriate validation:
if(results.Places != null && results.Places.Any())
{
var result = results.Places.First();
label1.Text = a.PlaceId;
}
You can bind only first place data in list from server side and get it on client side OR
bind FirstorDefault method to get first place data from list in client side.
I found this answer here at SO, Get nested property values through reflection C#, though when I run it in my case, it also tries to dump/recurse on e.g. a string's property, like Name, and when, it throws an exception.
My classes look like this
public class MyModels
{
public int Id { get; set; }
public DateTime EditDate { get; set; }
public string EditBy { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public class Organization
{
public Person Person { get; set; }
public Organization()
{
Person = new Person();
}
public string Name { get; set; }
}
public class Company : MyModels
{
public Organization Organization { get; set; }
public Company()
{
Organization = new Organization();
}
public string Description { get; set; }
}
And here's the code from the linked answer
var objtree = "";
void DumpObjectTree(object propValue, int level = 0)
{
if (propValue == null)
return;
var childProps = propValue.GetType().GetProperties();
foreach (var prop in childProps)
{
var name = prop.Name;
var value = prop.GetValue(propValue, null);
// add some left padding to make it look like a tree
objtree += ("".PadLeft(level * 4, ' ') + $"{name} = {value}") + Environment.NewLine;
// call again for the child property
DumpObjectTree(value, level + 1);
}
}
DumpObjectTree(itemData);
What I want is to iterate all the properties and check their value.
When I run the above code sample:
it first finds Organization, and recurse
at 1st level it finds Person, and recurse
at 2nd level if finds Name, and recurse
at 3rd level it throws an exception when it tries to GetValue for Name
If I remove my nested classes, and run it:
it first finds Description, and recurse
at 1st level it throws an exception when it tries to GetValue for Description
How do I make it to not try to dump/recurse on properties of type string, datetime, etc., like e.g. Name, Description?
The exception message says: "Parameter count mismatch."
As a note , the expected output/content in the objtree variable is e.g.
Organization = MyNameSpace.Models.Organization
Person = MyNameSpace.Models.Person
Name = TestName
Name = TestCompany
Description = Some info about the company...
Id = 1
EditDate = 31/08/2019
EditBy = user#domain.com
The reason for the exception is that string has a property named Chars. You normally don't see this property, because it's the indexer used when you do something like char c = myString[0];.
This property obviously needs a paramter (the index), and since you don't provide one, an exception is thrown.
To filter the types you don't want to recurse you need to extend the first line in the method. For example
if (propValue == null) return;
if (propValue.GetType().Assembly != Assembly.GetExecutingAssembly())
return;
This will only recurse through types declared in your assembly. If you want special filtering you need to adjust it.
Your current specification ("of type string, datetime etc") is not specific enough to give an exact solution, but I think the idea is clear.
Note that this won't prevent an exception to be raised if you declare an indexer in your own classes. So a better way might be to check for indexers directly:
foreach (var prop in childProps)
{
if (prop.GetIndexParameters().Any()) continue;
Second note: The current code has another flaw: You should keep track of which types you already dumped and abort the recursion when you come across a type the second time. That's possibly the reason for the exception at DateTime. A DateTime has a Date property, which is - hurray - of type DateTime. And so your objtree string grows infinitly until an OutOfMemoryException or StackOverflowException is thrown.
You need to skip recursion when:
Property is a value type
Property is a string
Property value contains reference to the object from the previous recursion level (ie, ParentObject) so that you don't get a stack overflow exception
Edit: Also when property is a collection type. If you want to get creative, you can have your recursor iterate through each object in the collection and then recurse through those
This PropertyInfo recursor seems to do the trick.
[Flags]
public enum PropertyRecursionOverflowProtectionType
{
SkipSameReference,
SkipSameType
}
public class PropertyRecursionBot
{
public object ParentObject { get; set; }
public object CurrentObject { get; set; }
public PropertyInfo PropertyInfo { get; set; }
public Type ParentType { get; set; }
public int Level { get; set; }
}
public static IEnumerable<PropertyRecursionBot> GetAllProperties(object entity,
PropertyRecursionOverflowProtectionType overflowProtectionType = PropertyRecursionOverflowProtectionType.SkipSameReference)
{
var type = entity.GetType();
var bot = new PropertyRecursionBot { CurrentObject = entity };
IEnumerable<PropertyRecursionBot> GetAllProperties(PropertyRecursionBot innerBot, PropertyInfo[] properties)
{
var currentParentObject = innerBot.ParentObject;
var currentObject = innerBot.CurrentObject;
foreach (var pi in properties)
{
innerBot.PropertyInfo = pi;
var obj = pi.GetValue(currentObject);
innerBot.CurrentObject = obj;
//Return the property and value only if it's a value type or string
if (pi.PropertyType == typeof(string) || !pi.PropertyType.IsClass)
{
yield return innerBot;
continue;
}
//This overflow protection check will prevent stack overflow if your object has bidirectional navigation
else if (innerBot.CurrentObject == null ||
(overflowProtectionType.HasFlag(PropertyRecursionOverflowProtectionType.SkipSameReference) && innerBot.CurrentObject == currentParentObject) ||
(overflowProtectionType.HasFlag(PropertyRecursionOverflowProtectionType.SkipSameType) && innerBot.CurrentObject.GetType() == currentParentObject?.GetType()))
{
continue;
}
innerBot.Level++;
innerBot.ParentObject = currentObject;
foreach (var innerPi in GetAllProperties(innerBot, pi.PropertyType.GetProperties()))
{
yield return innerPi;
}
innerBot.Level--;
innerBot.ParentObject = currentParentObject;
innerBot.CurrentObject = obj;
}
}
foreach (var pi in GetAllProperties(bot, type.GetProperties()))
{
yield return pi;
}
}
Use it like this:
public class RecursionTest
{
public string StringValue { get; set; }
public int IntValue { get; set; }
public RecursionTest Test { get; set; }
public RecursionTest ParentTest { get; set; }
}
var rec1 = new RecursionTest
{
IntValue = 20,
StringValue = Guid.NewGuid().ToString()
};
rec1.Test = new RecursionTest
{
IntValue = 30,
StringValue = Guid.NewGuid().ToString(),
ParentTest = rec1
};
rec1.Test.Test = new RecursionTest
{
IntValue = 40,
StringValue = Guid.NewGuid().ToString(),
ParentTest = rec1.Test
};
foreach (var bot in GetAllProperties(rec1, PropertyRecursionOverflowProtectionType.SkipSameReference))
{
Console.WriteLine($"{new string(' ', bot.Level * 2)}{bot.PropertyInfo.Name}: {bot.CurrentObject}");
}
I want to add multiple items to Database using entity framework from datagridview, but always get an error in the SaveChanges() method.
Here is my save button code
foreach (DataGridViewRow row in SaleGrid.Rows)
{
var saleProduct = new SaleProduct
{
SalesId = Convert.ToInt32(txtInvoice.Text),
CatId = Convert.ToInt32(row.Cells["CatId"].Value ?? DBNull.Value),
CatQuiltyId = Convert.ToInt32(row.Cells["QualityId"].Value ?? DBNull.Value),
SuitDesignId = Convert.ToInt32(row.Cells["DesignId"].Value ?? DBNull.Value),
SaleType = Convert.ToInt32(row.Cells["TypeId"].Value ?? DBNull.Value),
StockId = Convert.ToInt32(row.Cells["StockId"].Value ?? DBNull.Value),
Price = Convert.ToString(row.Cells["Price"].Value ?? DBNull.Value)
};
Db.SaleProducts.Add(saleProduct);
Db.SaveChanges();
}
public partial class SaleProduct
{
public int Id { get; set; }
public Nullable<int> SalesId { get; set; }
public Nullable<int> CatId { get; set; }
public Nullable<int> CatQuiltyId { get; set; }
public Nullable<int> SuitDesignId { get; set; }
public Nullable<int> CustomerId { get; set; }
public Nullable<int> SaleType { get; set; }
public Nullable<int> StockId { get; set; }
public string Price { get; set; }
An unhandled exception of type 'System.Data.Entity.Validation.DbEntityValidationException' occurred in EntityFramework.dll
Additional information: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
According to your error message it seems that some of your fields are possibly set as NOT NULL in database and you try to put NULL. You can check field EntityValidationErrors in your Exception in VS - there will be exact info which fields are incorrect
DbNull cannot be converted to Int32. If your value can be null then your Property is needed to be nullable.
public MyClass
{
public int? NullableProp {get; set;}
}
and your code
var saleProduct = new SaleProduct
{
NullableProp = String.IsNullOrEmpty(txtInvoice.Text) ? (int)null :
Convert.ToInt32(txtInvoice.Text),
....
}
Futhermore, It seems that you have ModelValidationError. In this case, you need to check EntityValidationErrors in order to understand what is exactly going wrong with your model.
You must first check null
row.Cells["Price"] is null ? DBNull.Value:Convert.ToString(row.Cells["Price"].Value)
Use This code and when the messages box show use
ctrl + c
To copy the message
Then post it here
try {
foreach (DataGridViewRow row in SaleGrid.Rows)
{
var saleProduct = new SaleProduct
{
SalesId = Convert.ToInt32(txtInvoice.Text),
CatId = Convert.ToInt32(row.Cells["CatId"].Value ?? DBNull.Value),
CatQuiltyId = Convert.ToInt32(row.Cells["QualityId"].Value ?? DBNull.Value),
SuitDesignId = Convert.ToInt32(row.Cells["DesignId"].Value ?? DBNull.Value),
SaleType = Convert.ToInt32(row.Cells["TypeId"].Value ?? DBNull.Value),
StockId = Convert.ToInt32(row.Cells["StockId"].Value ?? DBNull.Value),
Price = Convert.ToString(row.Cells["Price"].Value ?? DBNull.Value)
};
Db.SaleProducts.Add(saleProduct);
Db.SaveChanges();
}
}
catch (Exception e)
{
var messages = new List<string>();
do
{
messages.Add(e.Message);
e = e.InnerException;
}
while (e != null) ;
var message = string.Join(" - ", messages);
MessageBox.Show(message);
}
I'm trying to use reflection within a linq query to change a Decimal Null value to a string value. My code produce this error;
"Object of type System.String cannot be converted to type System.Nullable System.Decimal"
Thanks for your help.
public class ReportData
{
public IEnumerable<SASF> GetLongReportData(string commSubGp)
{
var context = new Entities();
string myDate = "2014-03-18";
DateTime date = Convert.ToDateTime(myDate);
var result = new List<SASF>();
if (commSubGp == "F00")
{
result = (from a in context.SASF
where a.RDate == date &&
a.COMM_SGP.CompareTo("F00") <= 0
orderby a.Conmkt, a.MKTTITL descending
select a).ToList();
//Here I'm trying to use reflection to loop through the object and set any value that's null to string value
result.ForEach(reflect =>
{
reflect.GetType().GetProperties().ToList().ForEach(p =>
{
var checkValue = p.GetValue(reflect, null);
if (checkValue == null)
{
p.SetValue(reflect, "non-reportable", null);
}
});
});
return result.ToList();
}
return results;
}
}
Since your property type is Decimal? the string "non-reportable" can not be converted to Decimal and the value fails to set. You could set it to zero however:
p.SetValue(reflect, Decimal.Zero, null)
Or any decimal value for that matter.
p.SetValue(reflect, Decimal.MinValue, null)
p.SetValue(reflect, Decimal.MaxValue, null)
Not knowing what the data is to be used for in the end I have no idea whether this would be appropriate or not.
Using reflection to do this probably isn't the best solution as it is quite an expensive process. Utilising the method below allows you to be specific and output the data in a way you see fit (although you could do this on the DB side too).
Without knowing the structure of the SASF class I have just created a pseudo class.
This of course requires you to specifically map each field to your stringified class. You might be able to use some tool like AutoMapper (https://github.com/AutoMapper/AutoMapper) to do this for you.
public class ReportData
{
public IEnumerable<SASFStringified> GetLongReportData(string commSubGp)
{
var context = new Entities();
string myDate = "2014-03-18";
DateTime date = Convert.ToDateTime(myDate);
var result = new List<SASF>();
if (commSubGp == "F00")
{
result = (from a in context.SASF
where a.RDate == date &&
a.COMM_SGP.CompareTo("F00") <= 0
orderby a.Conmkt, a.MKTTITL descending
select a).ToList();
var stringifiedResult = new List<SASFStringified>();
foreach (var sasf in result)
{
stringifiedResult.Add(new SASFStringified
{
ID = sasf.ID,
Field1 = sasf.Field1.HasValue ? sasf.Field1.Value.ToString() : "non-reportable",
Field2 = sasf.Field2.HasValue ? sasf.Field2.Value.ToString() : "non-reportable",
DateField = sasf.DateField.ToShortDateString()
});
}
return stringifiedResult;
}
return results;
}
}
public class SASF
{
public int ID { get; set; }
public decimal? Field1 { get; set; }
public decimal? Field2 { get; set; }
public DateTime DateField { get; set; }
}
public class SASFStringified
{
public int ID { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
public string DateField { get; set; }
}
Good day,
I have a class that is used to store a value of Type T that I don't know what the type will be until runtime. I want to unbox/cast, not sure what the correct term is, a specific type (in this case a nullable decimal) back to type object.
Please forgive my code layout:
The class snippet:
public abstract class Types
{
public class ValueField<T>
{
[XmlAttribute]
public int TemplateID { get; set; }
[XmlAttribute]
public int FieldID { get; set; }
[XmlIgnore]
[ScriptIgnore]
public TemplateApprovalField Field { get; set; }
[XmlIgnore]
[ScriptIgnore]
public InstanceTemplateActivityValues Values { get; set; }
[XmlAttribute]
public T Value { get; set; }
}
}
The function snippet:
I am stuck at the line "values.Add((Types.ValueField)field);", don't know how to cast it. At that moment, var field is a Types.ValueField.
Values = new Func<XmlDocument, List<Types.ValueField<object>>>(xml =>
{
List<Types.ValueField<object>> values = new List<Types.ValueField<object>>();
if (xml != null)
{
foreach (XmlNode node in xml.SelectNodes("//Field"))
{
if (node.Attributes["Type"].Value == "Numeric")
{
var field = new Types.ValueField<decimal?>()
{
Field = ApprovalFields.Find(f => f.FieldID == int.Parse(node.Attributes["ID"].Value)),
FieldID = int.Parse(node.Attributes["ID"].Value),
TemplateID = int.Parse(node.SelectSingleNode("../#ID").Value)
};
field.Value = new Func<string, decimal?>(val =>
{
if (string.IsNullOrEmpty(val))
return null;
else
{
decimal parsed = 0;
if (decimal.TryParse(val, out parsed))
return parsed;
}
return null;
})(node.InnerText);
values.Add((Types.ValueField<object>)field); //This is where my problem occurs...
}
}
}
return values;
})(row["Form_Values"] != DBNull.Value ?
new XmlDocument() { InnerXml = row["Form_Values"].ToString() } : null)
Any input will be greatly appreciated.
Regards
YP
You're creating a ValueField<decimal?>. That isn't a ValueField<object> - your ValueField<T> class isn't covariant in T, and couldn't be. (Classes can't be covariant, and your API includes T in "in" positions too.)
To demonstrate why this mustn't work:
Value<decimal?> foo = new Value<decimal?>();
Value<object> bar = foo; // Imagine this worked
bar.Value = "hello";
decimal? x = foo.Value;
What would you expect that to do? Everything other than the second line is above reproach, so we must make the second line fail, which it does.
The simple answer here is to create a Value<object> in the first place, instead of a Value<decimal?>.