Entity Framework OrderBy().Where() expression - entity-framework

I've been using Entity Framework for a little while now with no issues, until i stumbled upon a curly one....well it is for me atleast anyway. I have searched the internet and cannot find anything related to this but i assume it is merely because i am asking the wrong question. So here goes...
query= query.OrderByDescending(u => u.DateCreated);
This is simple and works fine. However the table being queried is for workflow and there are 4 date columns, CreatedDate, EstimatedDate, RevisedDate and ActualDate. At the beginning of the workflow for this element the CreatedDate will and all the other date columns will be NULL. As the element progresses through workflow the subsequent dates will be filled.
So what i am trying to achieve is this, i don't want any grouping, i just want the date to be used for OrderBy() to be the last date in the workflow.
I can achieve this by adding another column to my table called FilterDate which is used solely for sorting and gets updated with the appropriate date based upon workflow, however this is adding another column to my table just because I can't come up with a smart method of achieving this.

It's not pretty, but this should be what you're looking for, assuming that ActualDate (if populated) is always >= RevisedDate >= EstimatedDate => CreatedDate:
query= query.OrderByDescending(u => u.ActualDate.HasValue
? u.ActualDate.Value
: u.RevisedDate.HasValue
? u.RevisedDate.Value
: u.EstimatedDate.HasValue
? u.EstimatedDate.Value
: u.CreatedDate);
This will order by whichever date is available preferencing the Actual over Revised over Estimated and defaulting to the Created.
This doesn't handle if it's possible that a RevisedDate could be after an ActualDate for instance. If the row had an RevisedDate of 2020-12-02 and an ActualDate of 2020-11-25, this query would be using the ActualDate for comparison, not the later RevisedDate.

If I understand your question properly then you don't need to add an extra column to your table.
You just need to add a [NotMapped] decorated property to your object, because that property won't be saved in the database.
[NotMapped]
public DateTime FilteringDate
{
get
{
if (ActualDate.HasValue) return (DateTime)ActualDate;
else if (RevisedDate.HasValue) return (DateTime)RevisedDate;
else if (EstimatedDate.HasValue) return (DateTime)EstimatedDate;
else return CreatedDate;
}
}
usage
query = query.OrderByDescending(u => u.FilteringDate);

Related

Query annotation not working for max(id)

I have a domain object GenJournal and it has an "id" member (Long) that's auto-generated. I also have a JPA repository that I've added ...
#Query("select coalesce(max(u.id), 0) from GenJournal u")
Long getMaxId();
The method getMaxId() returns zero or null before I added coalesce. I have two rows in my database with ids 1 and 2. Can anyone help me determine why this doesn't work?
I'm trying to get the latest or max id so that I can use the find method after to return the most recent GenJournal object in my service class.
I'm stumped and really need some options or strategy to determine why this doesn't work.
You could use "Native Query" feature by passing nativeQuery = true param into #Query annotation like this
#Query("select coalesce(max(u.id), 0) from Gen_Journal_Table u", NativeQuery = true)
Long getMaxId();
My issue was two-fold. First I was getting null without the use of "coalesce". That caused me to think that this didn't work. When I adopted the use of "coalesce" I didn't realize that my table had no records and was returning the zero (0). My table in the production profile did have two records and I was expecting an id of 2.
I was manually checking the wrong database and setting expectations that were incorrect.

How to get all data without any filtering in "in" clause

As a part of reporting I want to get some values from database.
Also I included filtering in report UI, like :
select * from invoice where id in (92)
So I am making the postgres statement dynamically(here 92 is the value getting from UI and assigning dynamically). But I want to return all data without any condition if the user select no option, id in this case (no filtering). So how can I handle the "in" clause to return all data without any filtering in this case.
I am asking for a common term that can be included in 'in' clause, so it retun all rows without filtering.
Thanks!
One method is using logic like:
where (v_id is null) or (id = v_id)
Note: be careful about the use of in. It probably will not do what you intend if you expect multiple values to match.

Between... And... To work without values

I've tried to do this in a million different ways. At first I couldn't get it to work at all, but now I've managed to get it to work if I put in values.
What I need to happen is for my query to filter my records based on what I put into my form.
I've used this code in the 'Criteria' section of my MovieYear column, and when I put in numbers into my MovieYear1 and MovieYear2 text boxes in my form, it filters correctly.
Between [Forms]![SearchForm]![MovieYear1] And [Forms]![SearchForm]![MovieYear2]
But if I don't put in any values, it doesn't come up with any records at all. Any help?
I've tried pretty much everything (well, at least I think I have). I've tried using wildcards "*" but then I found out you can't actually use them with Between functions...
I've also trying doing Me.Filter in VBA, but it didn't seem to work. Maybe I just missed something?
This is my form.
Thanks in advance! :)
You can add a check for a Null in the form to the query, for example
SELECT *
FROM table
WHERE Between [Forms]![SearchForm]![MovieYear1]
And [Forms]![SearchForm]![MovieYear2]
OR [Forms]![SearchForm]![MovieYear1] Is Null
This will return all records if the first year is null. The second year will be ignored.
You could use a conditional query builder where after checking the value of the boxes you could build the query as per the following cases :
if only MovieYear1 is given then data from all years after MovieYear1 that is date>MovieYear1.
if only MovieYear2 is given then data from all years after MovieYear2 that is date<MovieYear2.
if both are given then use the between clause to get the data.
This can be implemented using CASE WHEN along the lines of following
CASE WHEN MovieYear2 IS NULL then date>MovieYear1
else when MovieYear1 IS NULL then date<MovieYear2
else date between MovieYear1 and MovieYear2

Entity Framework: Convert.ToDecimal not supported, any ideas? EF gives an error

I have been doing queries in EF and everything working great but now i have in the db 2 fields that are actually CHAR.. They hold a date but in the form of a number, in SQL Management Studio i can do date1 >= date2 for example and i can also check to see if a number i have falls in between these 2 dates.
Its nothing unusual, but basically a field that represents a date (the number grows as the date does)...
Now in EF when i try to do >= it states you can't do this on a string, ok understand its c# so i tried doing Convert.ToDecimal(date1) but it gives me an error saying that its not supported.
I have no option of changing the db fields, they are set in stone :-(
the way i got it to work was request of details and do a .ToList and then use the .ToDecimal and it works but of course this is doing it in memory! and this defeats the object of EF i.e. for example adding to the query using iqueryable.
Another way i got it to work was to pass the SQL query to SqlQuery of the dbcontext but again i lose a lot of ef functionality.
Can anyone help?
I am really stuck
As you say that you tried >= I assume that it would work for you if you could do that in plain SQL. And that is possible by doing
String.Compare(date1, date2) >= 0
EF is smart enough to translate that into a >= operator.
The advantage is that you do not need to compare converted values, so indexes can be used in execution plans.
First of all, you can at least enable deferred execution of the query by using AsEnumerable() instead of ToList(). This won't change the fact that the database would need to return all the records when you do in fact execute the query, however.
To let the database perform the filtering, you need your query to be compatible with SQL. Since you can't do ToDecimal() in SQL, you need to work with strings directly by converting your myvar to a string that is in the same format as dateStart and dateEnd, then form your query.

Database design preference: Using a DateTime and a BIT in SQL 2000

I need to explain this by example:
Is there a best practice or preference for specifying a DateTime and BIT in a database table?
In my database I have a Widget table. I need to know if a widget is "Closed" and it's "Closed Date" Business rules say that if a widget is closed, it must have a closed date. If a widget is not closed, it should not have a "Closed Date".
To design this, I could do the following:
(Example 1):
CREATE TABLE [Widget]
(
[WidgetID] INT IDENTITY(1,1)
,[ClosedDate] DATETIME NULL
)
or (Example 2):
CREATE TABLE [Widget]
(
[WidgetID] INT IDENTITY(1,1)
,[IsClosed] BIT NOT NULL CONSTRAINT [DF_Widget_IsClosed] DEFAULT (0)
,[ClosedDate] DATETIME NULL
)
I think that Example 1 is cleaner because it is one less column to have to worry about. But, whenever I need to evaluate whether a Widget is Closed, I would need an extra step to figure out if the ClosedDate column IS NOT NULL.
Example 2 creates extra overhead because now I have to keep both the IsClosed and ClosedDate values in sync.
Is there a best practice when designing something like this?
Would querying the table be more performant for Example 2? Is there any reason why I should choose one design over the other?
Note: I would be accessing this value through an ORM tool as well as Stored Procedures.
I think that option 1 is better. Data integrity is better kept (impossible to have a closed date with a flag which says the inverse), takes less disk space in the case of extra large tables, and queries would still be performant and clear to understand for teammates.
The first is better. Checking for null is cheap, whereas keeping a separate flag makes it possible to have a closed date yet not be closed.
I think you have the IsClosed column as a computed column.
CREATE TABLE [Widget](
[WidgetID] INT IDENTITY(1,1),
[ClosedDate] DATETIME NULL,
IsClosed AS CAST(CASE WHEN ClosedDate IS NULL THEN 0 ELSE 1 END AS BIT)
)
The reason is that you are not storing anything and you can now code your application code and stored procs to use this column. If your business rule ever changes you can convert this into a real column and you will not need to change other code. Otherwise you will have business logic sprinkled throughout your application code and stored procs. This way, it is only in 1 place.
Finally, when you move to SQL2005 you can add the "Persisted" clause. So it will be stored increasing the performance slightly and you will not have an issue with keeping them in sync.
I would not assign semantic meaning to NULL. Doing so will bubble through your business logic and you will get code like ...
public class Widget
{
// stuff
public bool IsClosed
{
// what do you put here?
// it was null in the db so you have to use DateTime.MinDate or some such.
return( _closeDate == ?? );
}
// more stuff
}
Using null in that fashion is bad. NULL (and null) mean "I don't know". You are assigning semantic meaning to that answer when in reality, you should not. The closed status is the closed status and the closed date is the closed date, don't combine them. (God forbid you ever want to re-open a Widget but still remember when it got closed in the first place, for example.)
Eric Lippert has a nice blog post on using null in this way (kidna) as well.