Simplifying LINQ query? - linq

I use this LINQ query in an app I am writing:
internal int GetNoteCount(DateTime startDate, DateTime endDate)
{
var a = DataStore.ObjectContext.Notes.Where(n => n.LastRevised >= startDate);
var b = a.Where(n => n.LastRevised <= endDate);
return b.Count();
}
Obviously, the query gets Notes that fall between two dates. I'd like to simplify the query by consolidating the first two lines into one. I know I can use fluent syntax to add the Count() method call to the end of my query.
Here's my question: How can I consolidate the two queries? Apparently, the && operator doesn't work with two lambda expressions. Thanks for your help.

You can do this:
internal int GetNoteCount(DateTime startDate, DateTime endDate)
{
return DataStore.ObjectContext.Notes.Where(n => n.LastRevised >= startDate && n.LastRevised <= endDate).Count();
}
Just use the && on your conditions, not the entire lambda :)

First, there's nothing wrong with the && operator and it should work just fine.
var a = DataStore.ObjectContext.Notes.Where(n => n.LastRevised >= startDate && n.LastRevised <= endDate);
return a.Count();
Second, with LINQ, it delays performing the query until the .Count() so there is no functional difference between your example and this one.

Related

.net Core 3.1 Linq Expression "...could not be translated." Exception

var asd = (from a in _context.Nodes
where (Convert.ToDateTime(a.NodeProcessTime).Date >= DateTime.Now.Date.AddMonths(-5) && Convert.ToDateTime(a.NodeProcessTime).Date <= DateTime.Now.Date.AddMonths(-4))
select a).Count();
I am trying to get the count of the records between two dates. But an exception appears below. NodeProcessTime is a string. Needs to be converted to dateTime. Am I missing something?
System.InvalidOperationException: 'The LINQ expression 'Where(
source: DbSet,
predicate: (n) => ToDateTime(n.NodeProcessTime).Date >= DateTime.Now.Date.AddMonths(-5) && ToDateTime(n.NodeProcessTime).Date
<= DateTime.Now.Date.AddMonths(-4))' could not be translated. Either
rewrite the query in a form that can be translated, or switch to
client evaluation explicitly by inserting a call to either
AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
You could do something like this:
// Convert (I asumme) string to date
Date ToDate(string dateTime) => Convert.ToDateTime(dateTime).Date;
// Offset current datetime
Date OffsetNow(int offset) => DateTime.Now.Date.AddMonths(offset);
// Check date is between range
bool IsBetween(Date d) => ToDate(d) >= OffsetNow(-5) && ToDate(d) <= OffsetNow(-4);
var asd = _context.Nodes.Count(n => IsBetween(n));
you should use sqlFunction to translate it.
SqlFunctions.DateAdd("day", 0 , a.NodeProcessTime)
just add zero day to your string column
List<DateTime> dateTimes = new List<DateTime>();
foreach (var item in _context.Nodes.ToList())
{
dateTimes.Add(Convert.ToDateTime(item.NodeScanDateTime.Substring(0, 10)));
};
int counts = dateTimes.Where(a => a > DateTime.Now.AddMonths(-5)& a < DateTime.Now.AddMonths(-4)).Count();
Now at first I get the string NodeScanDateTime for all nodes and put them to a list. After that I cut them like "10.05.2020" type by using substring and convert them to DateTime. Then I made my query and now it works. Thanks for all...

Convert string to int in linq query

Here is the query
db.setupBrands.Where(x =>
Convert.ToInt32(x.SortKey) <= id &&
Convert.ToInt32(x.SortKey) >= desID)
.Select(x => x);
Here SortKey is string type i want to convert to int. On Convert.ToInt32() i got the following error.
LINQ to Entities does not recognize the method 'Int32 ToInt32(System.String)' method, and this method cannot be translated into a store expression.
EF can't translate Convert and Parse. I completely agree with the above, but, if your SortKey is nchar(4) you can try this:
string s_id = string.Format("{0:0000}", id);
string s_desID = string.Format("{0:0000}", desID );
db.setupBrands.Where(x =>
x.SortKey <= s_id &&
x.SortKey >= s_desID)
.Select(x => x);

BETWEEN EQUIVALENT in LINQ

I am looking LINQ equivalent for the following query
Select * from ct_rate
WHERE
'2010-10-01 00:00:00'
BETWEEN start_date and end_date;
ANY IDEA?
You need to use two comparison operations:
DateTime date = new DateTime(2010, 10, 1);
var results = from rate in ct_rates
where rate.StartDate <= date && rate.EndDate >= date
select rate;
Just use ordinary comparison operators
var result = ct_rates
.Where(x => x.start_date <= myDate && x => x.endDate >= myDate);
In our case, Robert's answer was close to what I was looking for. In our data model, our 'end' or 'through' date columns are nullable.
Something like the following was needed:
Category.Where(
w => w.ValidFrom <= now &&
(w.ValidThru == null ? DateTime.MaxValue : w.ValidThru) >= now).Select(s => s).Dump();
I found this works when wanting only to compare the data part.
var results = from rate in ct_rates
where rate.StartDate.Date <= date && rate.EndDate.Date >= date
select rate;
something like this?
var result = from context.ct_rate.Where(d => d.start_date >= "2010-10-01 00:00:00" && d.end_date <= "2010-10-01 00:00:00")

date difference with linq

With this code:
i.SpesaAlloggio = db.TDP_NotaSpeseSezB.Sum(p => p.Costo / (((DateTime)p.DayEnd)
.Subtract((DateTime)p.DayStart).Days + 1));
I receive this error:
LINQ to Entities does not recognize the method
'System.TimeSpan Subtract(System.DateTime)' method, and this method cannot be
translated into a store expression.
How can I do this?
Use a calculated DB field and map that. Or use SqlFunctions with EF 4 as LukLed suggested (+1).
I wrote a function for removing time:
public static DateTime RemoveHours(DateTime date)
{
int year = date.Year;
int month = date.Month;
int day = date.Day;
return new DateTime(year, month, day);
}
and changed filtering condition:
var query =
from trn in context.IdentityTransactions
where trn.ClientUserId == userId && trn.DateDeleted == null
orderby trn.DateTimeCreated
select new
{
ClientServerTransactionID = trn.ClientServerTransactionID,
DateTimeCreated = trn.DateTimeCreated,
ServerTransDateTime = trn.ServerTransDateTime,
Timestamp = trn.Timestamp,
Remarc = trn.Remarc,
ReservedSum = trn.ReservedSum,
};
if (dateMin.HasValue && dateMin.Value > DateTime.MinValue)
{
DateTime startDate = Converters.RemoveHours(dateMin.Value);
query = from trn in query
where trn.DateTimeCreated >= startDate
select trn;
}
if (dateMax.HasValue && dateMax.Value > DateTime.MinValue)
{
var endDate = Converters.RemoveHours(dateMax.Value.AddDays(1.0));
query = from trn in query
where trn.DateTimeCreated < endDate
select trn;
}
dateMin and dateMax are nullable types and may be not set in my case.
Try (it is not very efficient, but it will work):
i.SpesaAlloggio = db.TDP_NotaSpeseSezB.ToList()
.Sum(p => p.Costo / (((DateTime)p.DayEnd)
.Subtract((DateTime)p.DayStart).Days + 1));
EDIT : This will be extremely slow for large tables, because it transfers whole table content form server
Entity Framework tries to translate your expression to SQL, but it can't handle ((DateTime)p.DayEnd).Subtract((DateTime)p.DayStart). You have to make it simpler. ToList() gets all rows and then makes the calculation on application side, not in database.
With EF4, you could use SqlFunctions DateDiff
With EF1, you could create calculated field or view with this field and make calculation based on this field.

"No supported translation in SQL" Linq workaround?

Currently im trying to make my query short with reusable peice of code like this to check for post if it's eligible to display.
// Logic to check if post is eligible for display
public bool isEligibleForDisplay(Post n)
{
var pubDate = n.PUBLISH_DATE ?? DateTime.MinValue;
var endDate = n.END_DATE ?? DateTime.MaxValue;
var correctState = (n.STATE == PostState.Publish || n.STATE == PostState.Furture);
var dateInRange = (DateTime.Now >= pubDate && DateTime.Now <= endDate);
return correctState && dateInRange;
}
my linq look like this:
var q = from n in _db.Posts
where isEligibleForDisplay(n)
group n by n.POST_ID into g
select g.OrderByDescending(t => t.CREATE_DATE).First();
return q.Take(quantity);
I ran into "No supported translation in SQL" problem for the first time of using linq to sql, I am just wondering if there are anyway that can use as a work around for this case, which could be troublesome if I include whole lot of those checking logic into my query everytime.
I'm so looking forward for a reply. Thanks!
You can create a function on your SQL server called isEligibleForDisplay that does the SQL equivalence of these checkes and add that to your dbml file.
I haven't tested this, but I'm thinking the easiest would be if you create a function where you pass the values you want, rather than the whole record, and I think something like this might work:
CREATE FUNCTION isEligibleForDisplay(
#publishDate DATETIME,
#endDate DATETIME,
#state TINYINT -- correct me if i'm wrong...
) RETURNS bit
AS
BEGIN
DECLARE #return bit
DECLARE #dateStart DATETIME, #dateEnd DATETIME
SET #return = 0
SET #dateStar t= COALESCE(#publishDate, CONVERT(DATETIME, '1900-01-01'))
SET #dateEnd = COALESCE(#endDate, CONVERT(DATETIME, '9999-12-31'))
IF getdate() BETWEEN #dateStart AND #dateEnd
BEGIN
IF #state IN(1,3) -- or whatever the int representations of your enum are
SET #return = 1
END
RETURN #return
END
I used the extension of linq to include a method, which can actually work very fine using IQueryable.
public static IQueryable<T> getPostActive<T>(this IQueryable<T> items) where T : P015.Models.SQLModel.Post
{
// Logic to check if post is eligible for display
var now = DateTime.Now;
return items.Where(n =>
(n.STATE.Trim() == PostState.Publish || n.STATE.Trim() == PostState.Furture || n.STATE.Trim() == PostState.Draft) &&
(
((n.END_DATE ?? SqlDateTime.MaxValue.Value) >= now) &&
((n.PUBLISH_DATE ?? SqlDateTime.MinValue.Value) <= now)
)
);
}
How maqny records are in _db.Posts? If not much, you can do .ToList() first, and than linq will be able to use isEligibleForDisplay function.

Resources