Equivalent of SQL Between Statement Using Linq or a Lambda expression - linq

Don't think this is a repost, difficult to search for the word between because it is used in everything (like searching for AND).
I want to filter a list based on a date range.
I have a list with some dates and I want to filter them by a date range. Is there a Linq or Lambda equivalent of the between statement in SQL.
For example, the code below will not work in Linqpad (or Visual Studio):
void Main()
{
List<ListExample> list = new List<ListExample>();
list.Add(new ListExample("Name1","23 Aug 2010"));
list.Add(new ListExample("Name2","23 Aug 2009"));
var query = from l in list
where l.DateValue between "01 Jan 2010" and "01 Jan 2011"
select l;
}
public class ListExample
{
public ListExample(string name, string dateValue)
{
Name = name;
DateValue = DateTime.Parse(dateValue);
}
public string Name{get;set;}
public DateTime DateValue{get;set;}
}

Something like this?
var query = from l in list
where l.DateValue >= new DateTime(2010, 1, 1)
&& l.DateValue <= new DateTime(2011, 1, 1)
select l;
You can write your own extension method:
public static bool IsBetween(this DateTime dt, DateTime start, DateTime end)
{
return dt >= start && dt <= end;
}
In which case the query would look something like (method syntax for a change):
var start = new DateTime(2010, 1, 1);
var end = new DateTime(2011, 1, 1);
var query = list.Where(l => l.DateValue.IsBetween(start, end));
I see you've provided some samples with the dates as strings. I would definitely keep the parsing logic (DateTime.ParseExactor other) separate from the query, if at all possible.

var query = from l in list
where new DateTime(1,1,2010) <= l.DateValue and DateValue <= new DateTime(1,1,2011)
select l;
of course, normally warning about timezones and different times on clients and servers apply

Datetime DT1 = DateTime.Parse("01 Jan 2010");
Datetime DT2 = DateTime.Parse("01 Jan 2011");
var query = from l in list
where l.DateValue >= DT1 && l.DateValue <= DT2
select l;
in linq you use the && and || like you would in a normal boolean statement of C#.

Related

Inner join with linqJS

I create a dateRange wich is an array of dates.
Then I have an array of day numbers like 0 for Sunday, 1 for Monday etc...
Now I want to get all dateRange dates according to the visibleWeekDays array.
The solution is in the getVisibleDateRange function.
But I want to do it with LINQ because why reinvent the wheel...
The inner or outer selector would still need a .day() because one of the selector is a momentJS object.
But to get the day of week I would need to put the ".day()" into the linqJS string which can not work...
What would be your solution with linqJS ?
// Arrange
var startDate = moment(new Date(2014, 1, 1));
var endDate = moment(new Date(2014, 1, 15));
var visibleWeekDays = [0,1]
// Act
var dates = dateFactory.dateRange(startDate, endDate);
var visibleDays = dateFactory.getVisibleDateRange(visibleWeekDays ,dates);
function getVisibleDateRange(visibleWeekDays, dateRange) {
var visibleDateRange = [];
for (var i = 0; i < dateRange.length; i++) {
for (var j = 0; j < visibleWeekDays.length; j++) {
var currentDate = dateRange[i];
var dayOfWeek = currentDate.day();
var visibleDayOfWeek = visibleWeekDays[j];
if (visibleDayOfWeek === dayOfWeek) {
visibleDateRange.push(currentDate);
}
}
}
return visibleDateRange;
}
var visibleDateRange = Enumerable.from(visibleWeekDays).join(dateRange,"","","outer,inner=>outer + ':' + inner")
Here's how I would write the inner join:
var dateRange = Enumerable.Range(1, 15).Select("new Date(2014, 1, $)");
var visibleWeekDays = Enumerable.From([0, 1]);
var visibleDateRange = dateRange.Join(visibleWeekDays,
"$.getDay()", // outer selector
"$", // inner selector
"$") // result selector (select outer value, the date)
.ToArray();
Here, I've used a more compact syntax for defining lambdas. Basically lambdas will generally have at most 4 paramters. So you can reference the nth parameter simply by adding additional $'s in the identifier. So $ refers to the first parameter, $$ the second, etc.
The parameters of the join is exactly the same as in the call you would make in LINQ. The first parameter is the inner collection, then the outer selector, inner selector, and result selector.
Since the outer collection is the dates, you have access the corresponding item and its properties. Since we want to get the result of calling date.getDay(), you simply call getDay() on the object (the first parameter).

Linq to EF in lightswitch WCF RIA Services

I have been trying for days now to solve the following problem in WCF RIA for lightswitch using linq:
the entity is:
moveDate Direction moveQuantity moveSignedQty
moveSignedQty is positive for In and negative for Out Direction
What I need to do is create a WCF Service in the form
tDate OBalance CBalance
CBalance is the sum of moveSignedQty for a particular moveDate
OBalance is the sum of mpveSignedQt for the previous moveDate; it is zero if there in no previous day value.
My approach below did not work:
Dim close = From c In Me.Context.StockMovements
Order By c.DateOfMovement
Group New With {c} By _
tranDate = CDate(c.DateOfMovement) _
Into g = Group
Let cBal = g.Sum(Function(s) s.c.SignedQuantity_KG)
Let fDate = g.OrderBy(Function(d) d.c.DateOfMovement).FirstOrDefault
Select New accStockBalance With {
.TransactionDate = tranDate, _
.ClosingBalance = cBal}
Dim sBal = close.GroupBy(Function(d) d.TransactionDate).Select( _
Function(b)
Dim subb = b.OrderBy(Function(t) t.TransactionDate)
Return subb.Select( _
Function(s, i) New With {
.TransactionDate = s.TransactionDate, _
.ClosingBalance = subb.ElementAt(i).ClosingBalance, _
.OpeningBalance = If(i = 0, 0, subb.ElementAt(i - 1).ClosingBalance)})
End Function)
Example:
moveDate Direction moveQuantity moveSignedQty
13/02/2013 In 30 30
13/02/2013 Out 4 -4
13/02/2013 Out 10 -10
14/02/2013 Out 4 -4
14/02/2013 Out 4 -4
14/02/2013 In 7 7
15/02/2013 In 15 15
Expected result:
tDate OBalance cBalance
13/02/2013 0 16
14/02/2013 16 15
15/02/2013 15 30
The last bit sBal threw up an error that lambda statements cannot be converted to expression trees.
Kindly guide me. I have read several Q and A in this and other forums help please.
Please forgive my terrible formatting couldnt figure out how to format the example to table format
The function below is composed of 2 statements.
Function(b)
Dim subb = ...
Return ...
This kind of function can't be used in LINQ query.
Moreover, the operators ElementAt and Select using index arguments aren't supported by EntityFramework.
I show you here a solution. It is written in C#. I do not think you'll have difficulty translating the code in VB. Furthermore, I do not use the fluent LINQ syntax that I do not find very understandable. Finally, for pedagogical concern I avoid using anonymous types.
The first thing to do is actually write a query that sums the movements by date. This request must allow to obtain a list of object (MovementSumItem class) each containing the date and the sum of the movements of the day.
class MovementSumItem
{
public DateTime Date { get; set; }
public int? TotalQty { get; set; }
}
The TotalQty property is declared as nullable. I'll explain why later.
I do not understand why your close query is so complicated!?! You just need to use the GroupBy operator once. And there is no interest in using the OrderBy operator.
IQueryable<MovementSumItem> movementSumItemsQuery =
context.StockMovements
.GroupBy(
// group key selector: the key is just the date
m => m.MoveDate,
// project each group to a MovementSumItem
(groupKey, items) => new MovementSumItem {
// date is the key of the group
Date = groupKey,
// items group sum
TotalQty = items.Sum(i => i.SignedQty),
});
Now, we must be able to determine for each item which is the previous item. And this of course without first execute the query below.
Here is the logical expression I propose:
Func<MovementSumItem,MovementSumItem> previousItemSelector = item =>
movementSumItemsQuery // from all items
.Where(b => b.Date < item.Date) // consider only those corresponding to a previous date
.OrderByDescending(b => b.Date) // ordering them from newest to oldest
.FirstOrDefault(); // take the first (so, the newest) or null if collection is empty
This expression does not use any index notion. It is therefore compatible with Entity Framework.
Combining this expression with the query above, we can write the complete query. This final request must allow to obtain a list of object (BalanceItem class) each containing the date, the opening balance (sum of movements from previous item) and the closing balance (sum of movements from current item).
class BalanceItem
{
public DateTime Date { get; set; }
public int OpeningBalance { get; set; }
public int ClosingBalance { get; set; }
}
Logically, the final query can be written:
IQueryable<BalanceItem> balanceItemsQuery =
movementSumItemsQuery
.Select(
item => new BalanceItem() {
Date = item.Date,
OpeningBalance = previousItemSelector(item).TotalQty ?? 0,
ClosingBalance = item.TotalQty ?? 0
});
Unfortunately Entity Framework does not support the invocation of the function previousItemSelector. So we must integrate the expression in the query.
IQueryable<BalanceItem> balanceItemsQuery =
movementSumItemsQuery
.Select(
item => new BalanceItem()
{
Date = item.Date,
OpeningBalance = movementSumItemsQuery
.Where(b => b.Date < item.Date)
.OrderByDescending(b => b.Date)
.FirstOrDefault().TotalQty ?? 0,
ClosingBalance = item.TotalQty ?? 0
});
Finally, to run the query, simply use (for example) the ToList operator.
List<BalanceItem> result = balanceItemsQuery.ToList();
Moreover, balanceItemsQuery being IQueryable, you can specify the query by adding, for example, a filter on the date:
IQueryable<BalanceItem> balanceItemsOfTheYearQuery = balanceItemsQuery
.Where(x => x.Date.Year == 2014);
Finally, you can verify that the query executes well via a single SQL query using the ToString function of the Query object.
Console.WriteLine(balanceItemsQuery.ToString());
Why TotalQty is declared nullable?
Otherwise, the OpeningBalance value expression should be written:
OpeningBalance = movementSumItemsQuery
.Where(b => b.Date < item.Date)
.OrderByDescending(b => b.Date)
.FirstOrDefault() == null ? 0 : movementSumItemsQuery
.Where(b => b.Date < item.Date)
.OrderByDescending(b => b.Date)
.FirstOrDefault().TotalQty
However, the comparison .FirstOrDefault() == null is not supported by Entity Framework.

How to get all the birthdays of today?

Does anyone know how to make a Linq query that gets all the birthdays of today? The code below doesn't work :
var getBirthdays =
orgContext.CreateQuery<Contact>()
.Where(c => c.BirthDate != null
&& c.BirthDate.Value.Month == DateTime.Now.Month).ToList();
I get an error like this:
"Invalid 'where' condition. An entity member is invoking an invalid
property or method."
Thanks in advance!
Anytime a vendor writes a four part blog series on how to do something as simple as finding a birthday (as Microsoft did in 2007), you have to know this won't be simple. So far as I can tell, this hasn't updated since then.
Find contacts with upcoming birthdays
Find contacts with upcoming birthdays - Part 2
Find contacts with upcoming birthdays - Parts 3 and 4
So you have limited options:
Make new fields called something like new_birthmonth and new_birthday that's updated every time a contact is created or updated via a plugin, and then query on those int fields.
Using Dynamic Linq, construct an OR clause in your WHERE clause that checks to see if the birthday falls in a reasonable range of years (say, 140 for the long-livers) (code below).
List<string> birthdays = new List<string>(); //will contain list of OR clauses
//makes sure no CRM unsupported dates are passed (less than 1/1/1900)
for (int i = Math.Min(140, DateTime.Today.Year - 1900); i > -1; i--)
{
//adds a different date per year
birthdays.Add
(
string.Format
(
//DateTimes are stored in UTC
"BirthDate = DateTime.Parse(\"{0}\")",
DateTime.Today.ToUniversalTime().AddYears(-i)
)
);
}
//completes the correct dynamic linq OR clause
string birthdayList = string.Join(" OR ", birthdays);
var getBirthdays = orgContext.CreateQuery<Xrm.Contact>()
.Where(c => c.BirthDate != null)
.Where(birthdayList)
.ToList();
I solved my problem based on the example of "Peter Majeed" and using "LinqKit"!
var predicate = PredicateBuilder.False<Contact>();
for (int i = Math.Min(140, DateTime.Today.Year - 1900); i > -1; i--)
{
DateTime cleanDateTime = new DateTime(DateTime.Today.AddYears(-i).Year, DateTime.Today.AddYears(-1).Month, DateTime.Today.AddYears(-i).Day);
predicate = predicate.Or(p => p.BirthDate == cleanDateTime.ToUniversalTime());
}
var getBirthdays = (from c in orgContext.CreateQuery<Contact>().AsExpandable().Where(predicate)
select c).ToList();
The above query gave me the correct result! Thx to all who helped me!
If c.BirthDate is nullable, you have to convert it to a datetime first:
var getBirthdays = orgContext.CreateQuery<Contact>()
.Where(c => c.BirthDate != null &&
(Convert.ToDateTime(c.BirthDate).Month ==
DateTime.Now.Month) &&
Convert.ToDateTime(c.BirthDate).Day ==
DateTime.Now.Day))
.ToList();
You could fetch this info with a Query, if that is possible in your situation?
//set up the condition + filter
var ce = new Microsoft.Xrm.Sdk.Query.ConditionExpression();
ce.Operator = Microsoft.Xrm.Sdk.Query.ConditionOperator.LastXDays;
ce.AttributeName = "birthdate";
ce.Values.Add(30);
var fe = new Microsoft.Xrm.Sdk.Query.FilterExpression();
fe.AddCondition(ce);
//build query
var query = new Microsoft.Xrm.Sdk.Query.QueryExpression();
query.EntityName = "contact";
query.Criteria.AddFilter(fe);
//get results
var results = CrmHelperV5.OrgProxy.RetrieveMultiple(query);
//if you want early bound entities, convert here.
var contacts = new List<Contact>();
foreach(var result in results.Entities)
{
contacts.Add(result.ToEntity<Contact>());
}
You may want to investigate the other operators for the filters + conditions
You can use QueryExpression (it works for Microsoft CRM Plugin)
public EntityCollection getBirthdateList(IOrganizationService orgsService)
{
List<string> birthdays = new List<string>();
//makes sure no CRM unsupported dates are passed (less than 1/1/1900)
for (int i = Math.Min(140, DateTime.Today.Year - 1930); i > -1; i--)
{
//adds a different date per year
birthdays.Add
(
DateTime.Now.AddYears(-i).ToString("yyyy-MM-dd")
);
}
// Instantiate QueryExpression
var query = new QueryExpression("contact");
// Define filter QEquote.Criteria
var queryfilter = new FilterExpression();
query.Criteria.AddFilter(queryfilter);
// Define filter
queryfilter.FilterOperator = LogicalOperator.Or;
queryfilter.AddCondition("birthdate",ConditionOperator.In,birthdays.ToArray());
return orgsService.RetrieveMultiple(query); ;
}

Linq static method error

I have created following function to get dates difference:
public static double MonthsDifference(DateTime dtStart, DateTime dtNow)
{
DateTime start = dtStart;
DateTime end = dtNow;
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;
return months;
}
I am using it in my LINQ query
var query = from c in context.AccountOwners.Where( MonthsDifference(p.Account.StateChangeDate,DateTime.Now) < 4 )
select c;
return query.Count();
but it is giving error:
LINQ to Entities does not recognize the method 'Double MonthsDifference(System.DateTime, System.DateTime)' method, and this method cannot be translated into a store expression.
Please suggest solution
The MonthsDifference function cannot be mapped to SQL, which is why you are getting this error. You need to either rewrite the query expression without any calls to your own methods to do what you want (which may be impossible -- I don't know exactly which native database functions LINQ to Sql supports), or fetch the result set and do the filtering locally.
The latter approach would go like this:
var count = context.AccountOwners.ToArray()
.Count(o => MonthsDifference(p.Account.StateChangeDate,DateTime.Now) < 4);
If you want to do this in the Linq then you need to inline this method so that Linq2Sql can translate it into sql.
So I think you'll need something like:
var start = DateTime.Now;
var query = from c in context.AccountOwners
let accountDate = c.Account.StateChangeDate
let diff = (start.Month + start.Year * 12) - (accountDate.Month + accountDate.Year * 12) + ...
where diff < 4
select c;
return query.Count();
Linked to Months difference between dates
In LINQ to Entities you can use Entity functions:
using System.Data.Objects;
var query = from c in context.AccountOwners.Where(EntityFunctions.DiffMonths(
p.Account.StateChangeDate,DateTime.Now) < 4 )
select c;
return query.Count();

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.

Resources