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; }
}
Related
I am using lambda expression to access values with data type, but the problem I have data type for Time as Time(7) on my local database and using Entity Framework. On my model this data type is define as DateTime.
How do I now access this data type to be time?
This is my code:
public List GetIncident_Details()
{
Entities incident = new Entities();
List result = new List();
var c_incident = incident.Incident_Template.Select(c => c).ToList();
if (c_incident != null && c_incident.Count() > 0)
{
foreach (var cData in c_incident)
{
Incident_DropDown model = new Incident_DropDown();
model.Title = cData.Title;
model.Description = cData.Description;
model.Date_Occurred = cData.Date_Occurred;
// How do I change this to have access?
// It's complaining about the data type object being set to a string?
model.Time = cData.Time;
model.Assignment_Group = cData.Assignment_Group;
model.Reported_CI = cData.Reported_CI;
result.Add(model);
}
}
return result;
}
public class Incident_DropDown
{
public string Title { get; set; }
public string Description { get; set; }
public string Date_Occurred { get; set; }
public DateTime Time { get; set; } // Time
public string Assignment_Group { get; set; }
public string Reported_CI { get; set; }
}
Took some advice from #alexey-rumyantsev, then had to test my code by interrogating model data type for Time it was Date Time, then change to Timespan. While testing this data type compare to my local database record and it was passing correct vales when debugging.
// Model name
public class Incident_DropDown
{
public string Title { get; set; }
public string Description { get; set; }
public string Date_Occured { get; set; }
public TimeSpan Time { get; set; } // had to change to work
public string Assignment_Group { get; set; }
public string Reported_CI { get; set; }
}
// Controller
public List<Incident_DropDown> GetIncident_Details()
{
Entities incident = new Entities();
List<Incident_DropDown> result = new List<Incident_DropDown>();
var c_incident = incident.Incident_Template.Select(c => c).ToList();
if (c_incident != null && c_incident.Count() > 0)
{
foreach (var cData in c_incident)
{
Incident_DropDown model = new Incident_DropDown();
model.Title = cData.Title;
model.Description = cData.Description;
model.Date_Occured = cData.Date_Occured;
model.Time = cData.Time; // This here enable to pass correct time as per database record
model.Assignment_Group = cData.Assignment_Group;
model.Reported_CI = cData.Reported_CI;
result.Add(model);
}
}
return result;
}
var todate = Filters.Where(it => it.Value == "ApplicationDateToSearch").Select(it =>
{
if (DateTime.TryParse(it.Description, out DateTime ConvertedToDate))
{
it.Description = ConvertedToDate.AddHours(23).AddMinutes(59).AddSeconds(59).ToString();
}
})?.FirstOrDefault();
Visual Studio doesn't seem to like this.
List<EmployeeRole> Filters
public class EmployeeRole
{
public String Description { get; set; }
public String Value { get; set; }
public int IntValue { get; set; }
}
The 'select' statement is used to select the properties you can't modify in that, so you have to use the 'ForEach' statement for that. But 'ForEach' statement doesn't have any return type so after that you can use FirstOrDefault
var x = Filters.Where(i => i.Value == "ApplicationDateToSearch").ToList();
x.ForEach(i => i.Description = DateTime.TryParse(i.Description, out DateTime ConvertedToDate) ? ConvertedToDate.AddHours(23).AddMinutes(59).AddSeconds(59).ToString() : string.Empty);
var todate = x.FirstOrDefault();
I have a class I want to populate from a Linq query, but I am using a sub select statement to slightly alter the properties of the list. I have a class it should fit into but it refuses to go in. I am wondering if there is a way I can get these results to fit into the list as I defined it rather than a generic anonymous type.
public class SCADA_DATA_Truncated
{
public string acode { get; set; }
public string LOCCODE { get; set; }
public Nullable<System.DateTime> COLDATE { get; set; }
public string RESULT { get; set; }
public string analyte { get; set; }
}
And here is where I am attempting to populate the data:
List<SCADA_DATA_Truncated> dataResults = (SCADA_DATA_Truncated)(from b in a2Entity.SCADA_DATA
where DbFunctions.TruncateTime(b.COLDATE) >= dateCheck1 && DbFunctions.TruncateTime(b.COLDATE) <= dateCheck2
&& whereInAcode.Contains(b.acode) && whereInLoc.Contains(b.LOCCODE)
select new
{
COLDATE = DbFunctions.TruncateTime(b.COLDATE),
acode = b.acode,
LOCCODE = b.LOCCODE,
RESULT = b.RESULT,
analyte = b.analyte
}
).ToList();
This is an anonymous type:
select new
{
COLDATE = DbFunctions.TruncateTime(b.COLDATE),
acode = b.acode,
LOCCODE = b.LOCCODE,
RESULT = b.RESULT,
analyte = b.analyte
}
The runtime has no idea how to convert it to your class
Why not changing it to
select new SCADA_DATA_Truncated
{
COLDATE = DbFunctions.TruncateTime(b.COLDATE),
acode = b.acode,
LOCCODE = b.LOCCODE,
RESULT = b.RESULT,
analyte = b.analyte
}
You can then remove the explicit cast altogether
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?>.
I want to return an anonymous type from a compiled query, which selects multiple columns from two tables.
I tried using:
public static Func < DBEntities, string>
but not able to compile it. I tried creating a new datatype BOMWorkOrder but could not make it work. May be ia m missing some syntax.
public static Func<DBEntities, string, IQueryable<BOMWorkOrder>> compiledWorkorderQuery =
CompiledQuery.Compile((DBEntities ctx, string bomNumber) =>
from items in ctx.BM10200
from orders in ctx.BM10300
where orders.Parent_Component_ID == -1 &&
orders.ITEMNMBR == bomNumber &&
orders.TRX_ID == items.TRX_ID
select new
{ bomWorkOrder =
items.TRXDATE,
orders.TRX_ID,
orders.ITEMNMBR,
orders.Assemble_Quantity
});
where work order will be:
public class BOMWorkOrder
{
public DateTime TransactionDate { get; set; }
public string TransactionId { get; set; }
public string ItemNumber { get; set; }
public int AssemblyQuantity { get; set; }
}
Since you've created the type BOMWorkOrder, use that type rather than an anonymous type:
... select new BOMWorkOrder
{
TransactionDate = items.TRXDATE,
TransactionId = orders.TRX_ID,
ItemNumber = orders.ITEMNMBR,
AssemblyQuantity = orders.Assemble_Quantity
};
If you return a list of anonymous objects, you will not be able to access the properties ( unless you use dynamic )
You're just missing the type name in your select:
...
select new BOMWorkOrder
{
TransactionData = items.TRXDATE,
TransactionId = orders.TRX_ID,
ItemNumber = orders.ITEMNBBR,
AssemblyQuantity = orders.Assemble_Queantity,
}
select new bomWorkOrder
{
TransactionDate =items.TRXDATE,
TransactionId =orders.TRX_ID,
ItemNumber =orders.ITEMNMBR,
AssemblyQuantity =orders.Assemble_Quantity
});