Complex Computed Column in EF - c#

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.

Related

C# - Correct way to check for nullable DateTime in if statement

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.

Compacting multiple similar if statements in C#

I would like to make some code for a game a little more compact. Here is the original.
if (killPosX != 0) // float x position you would like the object to be destroyed at
{
killX = true; // automatically makes the value of killX true
}
if (killPosY != 0) // float y position you would like the object to be destroyed at
{
killY = true; // automatically makes the value of killY true
}
if (killPosT != 0) // float position in time you would like the object to be destroyed at
{
killT = true; // automatically makes the value of killT true
}
And I want to turn it into something like this:
if ([killPosX, killPosY, killPosT] != 0)
{
[killPosX, killPosY, killPosT] = true;
}
How would I do that?
if (killPosX != 0)
{
killX = true;
}
Could be translated as follow
killX = (killPosX != 0) ? true : killX;
Or more simply
killX |= killPosX != 0;
if (killPosX != 0)
{
killX = true;
}
else
{
killX = false;
}
Could be translated as follow
killX = (killPosX != 0) ? true : false;
Or more simply
killX = killPosX != 0;
But since comparing floats using == or != is not advised, I would go for:
killX |= !Mathf.Approximately( killPosX, 0 );
killY |= !Mathf.Approximately( killPosY, 0 );
killT |= !Mathf.Approximately( killPosT, 0 );
AFAIK, there is no built-in syntax similar to what you wrote in order to achieve what you want.
I'm not sure if this applies to what you're working on (and there may be a better way in Unity), but it seems to me that one way to do it would be to make the KillN properties calculated on the value of their corresponding KillPosN property:
public class SomeClass
{
public float KillPosX { get; set; }
public float KillPosY { get; set; }
public float KillPosT { get; set; }
// Values are based on the related property values and cannot be set directly
public bool KillX => !IsRoughlyZero(KillPosX);
public bool KillY => !IsRoughlyZero(KillPosY);
public bool KillT => !IsRoughlyZero(KillPosT);
private static bool IsRoughlyZero(float input)
{
// Use a tolerance for comparing floating point numbers to 0
return Math.Abs(input) < .0000001;
}
}
I can't think of a way to do it as you suggest but it might be a little neater and more compact to use a variation of the approach hellium suggested:
public void fnc(ref bool t1, float t2) { t1 |= !Mathf.Approximately( t2, 0 ); }
fnc(ref KillX, KillPosX);
fnc(ref KillY, KillPosY);
fnc(ref KillT, KillPosT);
You could of course wrap it in a method which uses arrays as parameters but unless you need a generic way to do this for variable sized arrays, the setup cost is bulkier than just using discrete calls.

C# Implicit Operator not working with reflection

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');

How to simplify complex long nested if else statements in C#

I have the function shown below that calculates the tax value based on five input parameters.
For each combination of input parameters there is a matching 'condition' which is simply the return statement, e.g. if the input parameters are:
nationality == "German" and
netIncome == 45000 and
(birthDate >= 01.01.1950 && birthDate < 01.01.1960) and
childrenCount == 1
then tax value is 0.15%
the if else statements can get very very long and nested if we consider all input parameter combinations.
public static decimal GetTax(int netIncome, string nationality, DateTime birthDate, int childrenCount, bool handicapped)
{
if (nationality == "DE")
{
if (handicapped)
{
// condition1
return 0;
}
if (birthDate >= new DateTime(1930, 1, 1) && birthDate < new DateTime(1936,1,1))
{
if (childrenCount == 1)
{
// condition2
return 0.05m;
}
else
{
// other conditions
}
}
else if(birthDate >= new DateTime(1950, 1, 1))
{
if (netIncome >= 30000 && netIncome < 40000)
{
if (childrenCount == 1)
{
// condition3
return 0.15m;
}
else if (childrenCount == 2 || childrenCount == 3)
{
// other conditions
return Convert.ToDecimal(netIncome * 0.10);
}
else
{
// other conditions
}
}
if (netIncome >= 40000 && netIncome < 50000)
{
if (childrenCount >= 1 && childrenCount < 3)
{
// other conditions
return Convert.ToDecimal(netIncome * 0.17);
}
else if (childrenCount >= 3 || childrenCount < 5)
{
// other conditions
return Convert.ToDecimal(netIncome * 0.16);
}
else
{
// other conditions
}
}
}
else
{
}
}
else if (nationality == "FR")
{
}
else if (nationality == "IT")
{
}
// just to satisfy the compiler
throw new Exception("The input parameters don't match any condition!");
}
The question is: How to simplify the above if else statements to become a more understandable and easier to maintain code?
I came up with a simple (I hope) solution that I would like to present:
(1) I created a class called Condition shown below
private class Condition
{
private readonly decimal tax;
private readonly Predicate<string> nationalityPredicate;
private readonly Predicate<DateTime> birthDatePredicate;
private readonly Predicate<int> childrenCountPredicate;
private readonly Predicate<int> netIncomePredicate;
private readonly Predicate<bool> handicappedPredicate;
public Condition(decimal taxParameter, Predicate<string> nationality, Predicate<DateTime> birthDate, Predicate<int> netIncome, Predicate<int> childrenCount, Predicate<bool> handicapped)
{
this.tax = taxParameter;
this.nationalityPredicate = new Predicate<string>(nationality);
this.birthDatePredicate = new Predicate<DateTime>(birthDate);
this.netIncomePredicate = new Predicate<int>(netIncome);
this.childrenCountPredicate = new Predicate<int>(childrenCount);
this.handicappedPredicate = new Predicate<bool>(handicapped);
}
public bool IsTrue(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
if (this.nationalityPredicate(nationality))
{
if (this.birthDatePredicate(birthDate))
{
if (this.netIncomePredicate(netIncome))
{
if (this.childrenCountPredicate(childrenCount))
{
if (this.handicappedPredicate(handicapped))
{
return true;
}
}
}
}
}
return false;
}
public decimal CalculateTax()
{
Debug.WriteLine(this.tax);
return this.tax;
}
}
this class corresponds to one condition in the nested if else statements in the GetTax() function above. e.g. When the 5 input parameters are Nationality="DE", birthDate="01.01.1955", netIncome=35,000, childrenCount=1 and handicapped=false then the tax for this condition is 0.15
This class is used to break down long complex nested if else statements.
For each return statement in the GetTax() function we use one Condition class instant.
(2) I created the TaxCalculator class that uses the Condition class as an internal helper class as shown below
public class TaxCalculator
{
// List of conditions
private readonly List<Condition> conditions = new List<Condition>();
/// <summary>
/// Constructor. All the conditions are created and initialized here.
/// The conditions initialized in the constructor correspond to the nested if else statements in the GetTax() function.
/// </summary>
public TaxCalculator()
{
// This corresponds to Condition1 in the 'GetTax()' function
Condition condition = new Condition(
0,
nationality => nationality == "DE",
birthDate => true,
netIncome => true,
childrenCount => true,
handicapped => handicapped);
this.conditions.Add(condition);
// This corresponds to Condition2 in the 'GetTax()' function
condition = new Condition(
0.05m,
nationality => nationality == "DE",
birthDate => (birthDate >= new DateTime(1930, 1, 1)) && (birthDate < new DateTime(1936, 1, 1)),
netIncome => true,
childrenCount => childrenCount == 1,
handicapped => !handicapped);
this.conditions.Add(condition);
// This corresponds to Condition3 in the 'GetTax()' function
condition = new Condition(
0.15m,
nationality => nationality == "DE",
birthDate => birthDate >= new DateTime(1950, 1, 1),
netIncome => (netIncome >= 30000 && netIncome < 40000),
childrenCount => childrenCount == 1,
handicapped => !handicapped);
this.conditions.Add(condition);
}
/// <summary>
/// This function corresponds to the GetTax() function, it determines which Condition object corresponds
/// to the input parameters.
/// </summary>
public decimal CalculateTax(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
// iterate the conditions
foreach (Condition c in this.conditions)
{
// check if the input parameters apply the current condition
if (c.IsTrue(nationality, birthDate, netIncome, childrenCount, handicapped))
{
// if yes, return the result of this condition
return c.CalculateTax();
}
}
// in case of no matching condition raise an exception.
throw new Exception("TaxCalculator class didn't match any condition for parameters...etc.");
}
}
The TaxCalculator class corresponds to the long nested if else statements in the GetTax() function above.
The class creates Condition objects in the constructor using simple lambda expressions, each Condition object corresponds to one condition in the GetTax() function (marked with //Condition1, //Condition2 and //Condition3)
(3) To calculate the tax for a given set of input parameters we do the following:
TaxCalculator taxCalculator = new TaxCalculator();
// check for conditions
// condition3
decimal tax = taxCalculator.CalculateTax("DE", new DateTime(1985, 1, 1), 33000, 1, false);
// condition2
tax = taxCalculator.CalculateTax("DE", new DateTime(1933, 1, 1), 0, 1, false);
// condition1
tax = taxCalculator.CalculateTax("DE", new DateTime(1977, 1, 1), 0, 0, true);
Questions:
Is there a better and easier solution that provides easy maintenance?
Any optimization or correction suggestions?
I would write the first If like this :
public bool IsTrue(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
return (this.nationalityPredicate(nationality) &&
this.birthDatePredicate(birthDate) &&
this.netIncomePredicate(netIncome) &&
this.childrenCountPredicate(childrenCount) &&
this.handicappedPredicate(handicapped))
}
For the rest it's a classical rule checking engine, seems fine. Perhap's just invert the last throw exception. It's a matter of choice, but the exception is not the normal application flow IMHO. So you should throw exception if something is wrong, not if something is not wrong. Like :
public decimal CalculateTax(string nationality, DateTime birthDate, int netIncome, int childrenCount, bool handicapped)
{
var result = this.condition.FirstOrDefault(c => c.IsTrue(nationality, birthDate, netIncome, childrenCount, handicapped));
if(result == null)
throw new Exception("TaxCalculator class didn't match any condition for parameters...etc.");
return result.CalculateTax();
}
Perhap's just be careful with the name. "IsTrue" ? What is true ? What does it means from a business point of view or even technically ? I'm sure it's crystal clear for you, but not for someone who will have to maintain it, he will have to take a look at the method definition (in the best case, if not the entire rule engine) to understand what this "IsTrue" means. At least you have comment, but a good method name is, IMHO, way better than thousands lines of comments.

Convert String To Int in LINQ

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;

Categories

Resources