I have a LINQ query that queries a DataTable. In the DataTable, the field is a string and I need to compare that to an integer, basically:
if ((electrical >= 100 && electrical <= 135) || electrical == 19)
{
// The device passes
}
the problem is, I am trying to do this in LINQ like this:
var eGoodCountQuery =
from row in singulationOne.Table.AsEnumerable()
where (Int32.Parse(row.Field<String>("electrical")) >= 100 &&
Int32.Parse(row.Field<String>("electrical")) <= 135) &&
Int32.Parse(row.Field<String>("electrical")) != 19 &&
row.Field<String>("print") == printName
select row;
I keep getting the exception:
Input string was not in a correct format
The main problem occurs when electrical == ""
Unfortunately, the framework doesn't provide a nice clean way to handle parsing scenarios where it fails. Of what's provided, they only throw exceptions or use out parameters, both of which does not work well with linq queries. If any one value you're parsing fails, the entire query fails and you just can't really use out parameters. You need to provide a method to handle the parsing without that does not throw and does not require using out parameters.
You can handle this in many ways. Implement it where upon failure, you return some default sentinel value.
public static int ParseInt32(string str, int defaultValue = 0)
{
int result;
return Int32.TryParse(str, out result) ? result : defaultValue;
}
Or what I would recommend, return a nullable value (null indicating it failed).
public static int? ParseInt32(string str)
{
int result;
return Int32.TryParse(str, out result) ? result : null;
}
This simplifies your query dramatically while still leaving it readable.
public bool GetElectricalStatus(string printName)
{
var query =
from row in singulationOne.Table.AsEnumerable()
where row.Field<string>("print") == printName
// using the nullable implementation
let electrical = ParseInt32(row.Field<string>("electrical"))
where electrical != null
where electrical == 19 || electrical >= 100 && electrical <= 135
select row;
return !query.Any();
}
p.s., your use of the Convert.ToInt32() method is incorrect. It is the same as calling Int32.Parse() and does not return a nullable, it will throw on failure.
I would check if the data in the column does not contain leading/trailing whitespaces - i.e. "15 " rather than "15" and if it does (or might do) trim it before trying to convert:
Int32.Parse(row.Field<String>("electrical").Trim())
BTW: not related to the error but I'd use let statement to introduce a local variable and do the conversion once:
let x = Int32.Parse(row.Field<String>("electrical").Trim())
where x >= 100...
I could not get anything to work, so I re-did the whole method:
public bool GetElectricalStatus(string printName)
{
List<object> eGoodList = new List<object>();
var eGoodCountQuery =
from row in singulationOne.Table.AsEnumerable()
where row.Field<String>("print") == printName
select row.Field<String>("electrical");
foreach (var eCode in eGoodCountQuery)
{
if (!string.IsNullOrEmpty(eCode.ToString()))
{
int? eCodeInt = Convert.ToInt32(eCode);
if (eCodeInt != null &&
(eCodeInt >= 100 && eCodeInt <= 135) || eCodeInt == 19)
{
eGoodList.Add(eCode);
}
}
}
if (eGoodList.Count() > 0)
{
return false;
}
else
{
return true;
}
}
The main problem occurs when electrical == ""
Why not make a function that does your evaluation, and call it in your Linq query. Put logic in to check the validity of the data contained within (so if you can't parse the data, it should return false)...
The function:
bool IsInRange(string text, int lower, int upper, params int[] diqualifiers)
{
int value = int.MinValue;
if (!int.TryParse(text, out value)) {
return false;
}
if (!(value >= lower && value <= upper)) {
return false;
}
if (disqualifiers != null && disqualifiers.Any(d => d == value)) {
return false;
}
return true;
}
The Linq query...
var eGoodCountQuery =
from row in singulationOne.Table.AsEnumerable()
where
IsInRange(row.Field<String>("electrical"), 100, 135, 19)
&& row.Field<String>("print") == printName
select row;
Related
I have the following code method, which is designed to check if a user is blocked temporarily after invalidating their maximum attempts (say, 10) to login in a two hour period.
public MyError ValidationMethod(MyObject myObject) {
int maxMaximumAttempts = 10;
if (myObject.Attempts >= maximumAttempts && myObject.LastAttempt.Value.AddHours(2)) < DateTime.Now)
return new MyError();
return null;
}
Both of the DateTime fields, "Attempts" and "LastAttempt" are nullable, what is the proper way to format this if statement to ensure I don't get null reference exceptions?
Null for either of the fields, should be the same result as if the if statement returned false, i.e. they haven't tried to login before. I will then return null from the method itself which symbolises, no error was found with the input.
I thought the fields being null would make the whole statement false but instead I get a null reference exception, can anyone explain why please?
Edit: I have added the full method to make the context clearer.
you can this code:
if (myObject != null && myObject.LastAttempt != null)
if (myObject.Attempts >= maximumAttempts && myObject.LastAttempt.Value.AddHours(2) < DateTime.Now)
return new myError();
or:
try
{
if (myObject.Attempts >= maximumAttempts && myObject.LastAttempt.Value.AddHours(2) < DateTime.Now)
return new myError();
}
catch(Exception ex)
{
return new myError(ex.Message);
}
Don't bother with nullables, use DateTime.MaxTime as not yet set value.
and for Attempts use 0 as not yet set value
public class myType {
// initialize myObject.LastAttempt = DateTime.MaxTime
DateTime LastAttempt = DateTime.Max;
int Attempts = 0;
/*
.... rest of the class
*/
}
public MyError ValidationMethod(MyObject myObject) {
int maxMaximumAttempts = 5;
if (myObject.Attempts >= maximumAttempts && myObject.LastAttempt != DateTime.Max && myObject.LastAttempt.AddHours(2)) < DateTime.Now)
return new MyError();
return null;
}
The nullable DateTime object has a property called HasValue, so you could write:
if (myObject.Attempts.HasValue && myObject.Attempts.Value >= maximumAttempts && myObject.LastAttempt.HasValue && myObject.LastAttempt.Value.AddHours(2) < DateTime.Now)
return new myError();
First of all, I would move these checks to the class which holds Attempts and LastAttempt fields (or at least created an extension for this class) - check Tell Don't Ask Principle
public class MyObject
{
public bool IsBlockedOn(DateTime time)
{
if (!Attempts.HasValue || !LastAttempt.HasValue)
return false;
var hasTooManyAttempts = MaximumAttempts <= Attempts.Value;
var timeoutPassed = Timeout < (time - LastAttempt.Value);
return hasTooManyAttempts && !timeoutPassed;
}
private TimeSpan Timeout = TimeSpan.FromHours(2);
private int MaximumAttempts = 10;
// other properties
}
Your if statement:
if (myObject.IsBlockedOn(DateTime.Now))
return new MyError();
The good part - this logic is now unit-testable because you can execute it with different time arguments.
Also, consider making Attempts non-nullable and initialize it with 0.
I am working on a requirement where in I need to check my ASP.NET Model Property for a value 000000.If the value is 000000 then it should be displayed as blank string.
I thought of achieving this using implicit operators.
Here is my model class
public class OrgName
{
private string Value { get; set; }
public static implicit operator string(OrgName org)
{
return org.Value;
}
public static implicit operator OrgName(string value)
{
bool isAllZeros = value.Where(x => char.IsDigit(x)).All(x => x == '0');
if (isAllZeros)
value = string.Empty;
return new OrgName() { Value = value };
}
}
The problem is that we are using reflection to set property values.The above code does not work and the property is always displayed as blank.
Here is the reflection code
var prName = (String.IsNullOrWhiteSpace(parentPrefix) ? objKey : parentPrefix + '.' + objKey);
var pi = modelMap[prName.ToLowerInvariant()].Property;
var value = (collectionProperties.ContainsKey(objKey)) ? collectionProperties[objKey] : pi.GetValue(parentObj);
if (value == null || pi.PropertyType.IsSimpleType())
{
value = (prName == fieldToSet && pi.PropertyType.IsSimpleType())
? (Convert.IsDBNull(valueToSet)) ? null : valueToSet
: createObject(pi.PropertyType);
var type = Nullable.GetUnderlyingType(pi.PropertyType);
//check to see if we need to convert the type when assigning
if (type == typeof(Guid))
value = Guid.Parse(value.ToString());
pi.SetValue(parentObj, type != null ? Convert.ChangeType(value, type) : value);
if (pi.PropertyType != typeof(string) && IsContainerProperty(pi.PropertyType))
continue;
if (pi.PropertyType == typeToReturn)
objToLoad = value;
}
else if (!collectionProperties.ContainsKey(objKey) && IsContainerProperty(pi.PropertyType))
{
var innerType = pi.PropertyType.GetGenericArguments()[0];
var add = pi.PropertyType.GetMethod("Add",
BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public);
if (innerType.IsSimpleType())
{
collectionProperties[objKey] = valueToSet;
add.Invoke(value, new[] { valueToSet });
}
else
{
// Since we can't access the property
var innerObj = createObject(innerType);
collectionProperties[objKey] = innerObj;
add.Invoke(value, new[] { innerObj });
if (innerType == typeToReturn)
objToLoad = innerObj;
continue;
}
}
Can someone help me out with this?
I am also open to other suggestions to achieve this.
Thanks
You could just put the code in the setter?
public class OrgName
{
private string _value;
private string Value
{
get { return _value; }
set
{
bool isAllZeros = value?.All(x => x == '0') ?? false;
if(isAllZeros)
{
_value = string.Empty;
}
else
{
_value = value;
}
}
}
}
This might be an indirect solution to your problem as right now your code has a flaw.
Ex. a0000a0b0 will be detected as isAllZeros
To explain any further what exactly the issue in your code is.
First let's look at this line:
bool isAllZeros = value.Where(x => char.IsDigit(x)).All(x => x == '0');
The first thing you do is taking value and performing a Where on it. The condition for the where to pass is that each value (x) is a digit. Which means that any non-digit characters such as a, b, c will be skipped.
As contrary to what you may have interpreted Where as then it just filters away any values that doesn't match the condition.
This means that values that aren't digits in your case will not pass and thus when the enumeration hits All then it will only enumerate the characters that are digits.
What your code is basically equivalent to in an English speaking is:
Take value
Skip all characters that aren't digits
Check if all digit characters are 0's
What you want your code to actually do is:
Take value
Check if all characters are digits and 0. In this case you actually only have to check if the character is '0'. The char.IsDigit check is redundant.
It could be solved by doing this:
bool isAllZeros = value.All(x => x == '0');
You might want to put in a null check in case value is null though.
bool isAllZeros = value?.All(x => x == '0') ?? false;
In case you aren't using C# 6
bool isAllZeros = string.IsNullOrEmpty(value) ? false : value.All(x => x == '0');
I have an object that has a calculation field; The calculation formula is too complex, and it have to check some condition, and load some data from another objects (have to join with other tables).
This is my Object:
public class LoadAndExitPermit : Master, IColorSource
{
private int? _ladingBillId;
[DataMember]
public virtual int? LadingBillID
{
get { return _ladingBillId; }
set { SetPropertyValue(ref _ladingBillId, value, "LadingBillID"); }
}
.
. {lots of Properties)
.
[DataMember]
public virtual ICollection<LoadAndExitPermitDocumentDetail> LoadAndExitPermitDocumentDetails { get; set; }
The important part of my object is SumGoodsUnitPrice, and it's:
[NotMapped, DataMember]
Public decimal? SumGoodsUnitPrice
{
get
{
if (LoadAndExitPermitDetails == null || !LoadAndExitPermitDetails.Any())
{
return -1;
}
if (LoadAndExitPermitDetails.First().RequestDetail.OrderDetail == null)
{
decimal? sum = 0m;
foreach (var item in LoadAndExitPermitDetails)
{
decimal? tarrif = item.Good.GoodsTariffDetails.Where(g =>
g.Price > 0 && g.IsActive == true && g.GoodsTariff.GoodsTariffType.IsPublic == true && g.GoodsTariff.FromDate < Date)
.OrderByDescending(w => w.GoodsTariff.FromDate).Select(c => c.Price).FirstOrDefault();
if (tarrif != null)
{
sum += ((item.Quantity ?? 0) * (1.09m * (tarrif))) ?? 0m;
}
else
{
decimal? lastTariff = item.Good.GoodsTariffDetails.Where(x => x.Price > 0 && x.IsActive == true
&& x.GoodsTariff.FromDate < Date).OrderByDescending(w => w.GoodsTariff.FromDate).Select(c => c.Price).FirstOrDefault() ?? 0m;
sum += ((item.Quantity ?? 0) * (1.09m * (lastTariff))) ?? 0;
}
}
return sum;
}
var z = LoadAndExitPermitDetails.Sum(l => (l.Quantity ?? 0) * (1.09m * (l.RequestDetail.OrderDetail.Quantity == 0 ? 0 : (l.RequestDetail.OrderDetail.Price / l.RequestDetail.OrderDetail.Quantity) ?? 0)));
return z;
}
private set
{
}
}
I have some Issues:
I can't use Projection, I have two reasons: first readability of the code and the 2th one is my project structure doesn't allow me to use projected query.
We use database first method and this field is not in database, because its calculated field.
We deal with a large database and I can't Include (join) all tables i need, so i prefer to use SQL service side query to calculate it for me.
We can't use Stored Procedure Or View, Because we have lots of this kind of columns, and we need to use dynamic query for dynamic filters.
The performance of query is very important for us.
I would appropriate if anyone can help me.
If I where you, I add a view that calculate this field on the server and use for queries this view. This is compatible with CQRS architectural pattern.
I am working on a Windows Form application. I am parsing an XML file and doing some queries. For example, in this case I am trying to find all users weighing between 55 and 100. For some reason, when I run this code, I get a format exception unhandled. Why am I getting a format exception. I have indicated the breakpoint where the exception occurs. I think the problem is a syntactical error.
Thanks for your help.
private bool UserWeighsBetween55and100(IEnumerable<XElement> paramsList) {
bool result = false;
foreach (XElement parameter in paramsList) {
if (parameter.Attribute("name").Value == "Weight") {
--->HERE if ((Int32.Parse(parameter.Attribute("value").Value) > 55) &&
(Int32.Parse(parameter.Attribute("value").Value) < 100)){
return true;
}
}
}
return result;
}
Convert your value once instead of converting same value two time.
Try to do like this.
int iValue = 0;
if (Int.TryParse(parameter.Attribute("value").Value, out iValue)) //If the value converted
{
if (iValue > 55 && iValue < 100)
{
return true;
}
}
else //Failed to convert value into int datatype
{
//Code here if conversion faild
}
if the parameter.Attribute("value").Value is containing non-numeric value then it wont convert in int datatype.
Instead of using the "Parse" method, use the tryParse.
In your case, it would look like :
foreach (XElement parameter in paramsList) {
if (parameter.Attribute("name").Value == "Weight") {
int value;
if(!Int32.TryParse(parameter.Attribute("value").Value, out value)){
//Not a number, handle this case
}
if ((value > 55) && (value < 100)){
return true;
}
return result;
}
I will be answering my question because I found out my mistake and I hope it would help everybody reading this post.
The problem with using Int32.Parse() was that while I was parsing the XML file, I didn't pay attention to the values of type double.
The fix for this code would be as follows:
private bool UserWeighsBetween55and100(IEnumerable<XElement> paramsList) {
bool result = false;
foreach (XElement parameter in paramsList) {
if (parameter.Attribute("name").Value == "Weight") {
if ((parameter.Attribute("value").Value)!=null) {
if ((Convert.ToDouble(parameter.Attribute("value").Value) > 55) && (Convert.ToDouble(parameter.Attribute("value").Value) < 100)) {
return true;
}
}
}
}
return result;
}
I'm having a List<String> l_lstTemp and which contains
"A1"
"A1_1"
"A1_2"
"1A"
"B2_1"
"B1_2"
"B1_1_2"
"A10"
"B11"
"A"
"Z"
I need to sort the items based on the character and numeric value.
So the sorted list will be like
"1A"
"A"
"A1"
"A1_1"
"A1_2"
"A10"
"B1_1_2"
"B1_2"
"B2_1"
"B11"
"Z"
Here is my code:
l_lstTemp.Sort(delegate(String One, String Two)
{
Match l_mOne = Regex.Match(One, #"(\D*)(\d*)");
Match l_mTwo = Regex.Match(Two, #"(\D*)(\d*)");
int Result;
if (l_mOne.Success || l_mTwo.Success)
{
String l_strX, l_strY;
l_strX = l_mOne.Groups[1].Value;
l_strY = l_mTwo.Groups[1].Value;
Result = l_strX.CompareTo(l_strY);
if (Result != 0)
return Result;
l_strX = l_mOne.Groups[2].Value;
l_strY = l_mTwo.Groups[2].Value;
if (l_strX == String.Empty || l_strY == String.Empty)
{
Result = l_strX.CompareTo(l_strY);
if (Result != 0)
return Result;
}
else
{
long X = long.Parse(l_strX);
long Y = long.Parse(l_strY);
Result = X.CompareTo(Y);
if (Result != 0)
return Result;
}
}
return 0 ;
}
);
But its not working (sorting) properly.
How do I modify my code to sort the list properly?
Please post me a way to do this.
Thanks in advance.
I did some modifications to your code. The thing was that when both Group 1 and Group 2 are equals, you still need to check what remains.
Important: I did the modifications inside your code, so this could be a little tricky. I really suggest you refactoring your code now that you know it works:
l.Sort(delegate(String One, String Two)
{
while (One != "" && Two != "")
{
if (One == Two)
return 0;
//Add one more group to capture what remains of the expression
Match l_mOne = Regex.Match(One, #"_*(\D*)(\d*)(.*)$");
Match l_mTwo = Regex.Match(Two, #"_*(\D*)(\d*)(.*)$");
int Result;
if (l_mOne.Success || l_mTwo.Success)
{
String l_strX, l_strY;
l_strX = l_mOne.Groups[1].Value;
l_strY = l_mTwo.Groups[1].Value;
Result = l_strX.CompareTo(l_strY);
if (Result != 0)
return Result;
l_strX = l_mOne.Groups[2].Value;
l_strY = l_mTwo.Groups[2].Value;
if (l_strX == String.Empty || l_strY == String.Empty)
{
Result = l_strX.CompareTo(l_strY);
if (Result != 0)
return Result;
}
else
{
long X = long.Parse(l_strX);
long Y = long.Parse(l_strY);
Result = X.CompareTo(Y);
if (Result != 0)
return Result;
One = l_mOne.Groups[3].Value; //Store in 'One' the remaining part of the regex
Two = l_mTwo.Groups[3].Value; //The same in Two
continue; //The result will be the result of the comparison of those two new values.
}
}
}
return One.CompareTo(Two);
});
Edit:
I also added _* to remove all the _ characters from the begining of the strings. I assumed here that the strings will only contain _ after the numbers and not something like B1B or B1$.
The thing here is that you don't really explain how the comparison should be made, and I had to assume those things from your original data and the sorted data, otherwise what would happen if you want to sort A1A and A1_? What should it return?
Here's how I would implement such a comparer. Much easier to follow IMHO.
var re = new Regex(#"^(\d+)?([A-Z]+)(\d+)?(?:_(\d+)(?:_(\d+))?)?$");
Func<Group, int, int> intOrDefault = (g, d) => g.Success ? Convert.ToInt32(g.Value) : d;
list.Sort((x, y) =>
{
var xm = re.Match(x);
var ym = re.Match(y);
int cmp;
// compare the first group
// compare the leading numbers (if any)
cmp = intOrDefault(xm.Groups[1], int.MaxValue).CompareTo(intOrDefault(ym.Groups[1], int.MaxValue));
if (cmp != 0)
return cmp;
// compare letters
cmp = xm.Groups[2].Value.CompareTo(ym.Groups[2].Value);
if (cmp != 0)
return cmp;
// compare the trailing numbers (if any)
cmp = intOrDefault(xm.Groups[3], 0).CompareTo(intOrDefault(ym.Groups[3], 0));
if (cmp != 0)
return cmp;
// compare the next group
cmp = intOrDefault(xm.Groups[4], 0).CompareTo(intOrDefault(ym.Groups[4], 0));
if (cmp != 0)
return cmp;
// compare the last group
cmp = intOrDefault(xm.Groups[5], 0).CompareTo(intOrDefault(ym.Groups[5], 0));
return cmp;
});
For this example, just calling sort l_lstTemp.Sort() would get you the result you are looking for