I'm a bit stuck on this. Basically I want to do something like the following SQL query in LINQ to SQL:
SELECT *
FROM UnitPrice
WHERE EffectiveDateTime = (SELECT MAX(EffectiveDateTime)
FROM UnitPrice AS InnerUnitPrice
WHERE InnerUnitPrice.EffectiveDateTime < GETDATE())
You could do the following.
Emulating your scenario with DataTable
var unitPrice = new DataTable();
unitPrice.Columns.Add("EffectiveDateTime",typeof(DateTime));
unitPrice.Columns.Add("SomeOther",typeof(string));
unitPrice.Rows.Add(new DateTime(2018,12,1), "Sample1");
unitPrice.Rows.Add(new DateTime(2018,12,2), "Sample2");
unitPrice.Rows.Add(new DateTime(2018,12,3), "Sample3");
unitPrice.Rows.Add(new DateTime(2018,12,4), "Sample41");
unitPrice.Rows.Add(new DateTime(2018,12,4), "Sample4");
unitPrice.Rows.Add(new DateTime(2019,12,4), "Sample5");
You can query the required result as,
var result = unitPrice.AsEnumerable()
.Where(x=>x.Field<DateTime>("EffectiveDateTime") < DateTime.Today)
.GroupBy(x=>x.Field<DateTime>("EffectiveDateTime"))
.OrderByDescending(x=>x.Key)
.First().ToList();
Output
04-12-2018 00:00:00 Sample41
04-12-2018 00:00:00 Sample4
I give you an example on this short list of objects
List < Entry > list = new List<Entry>();
list.Add(new Entry("a","_a",new DateTime(2019,1,30),1));
list.Add(new Entry("b", "_b", new DateTime(2018, 12, 31), 2));
list.Add(new Entry("c", "_c", new DateTime(2018, 12, 31), 3));
list.Add(new Entry("d", "_d", new DateTime(2018, 12, 30), 4));
list.Add(new Entry("e", "_e", new DateTime(2018, 11, 30), 5));
the properties of the class Entry are, in order Reference (string), Donation (string), Date (datetime), Amount (double).
First step, we want to select the most recent date which is before today (what you're doing in your subquery); starting from our list we could do something like this:
var max_date = from l in list
where l.Date < DateTime.Today
group l.Date by 1 into g
select new { Val=g.Max() };
foreach (var m in max_date)
{
Console.WriteLine(m.Val);
}
Running this you'll get 31/12/2018 as desired. But we need another step to select all the informations on the selected date. So, step 2, we put our starting list in inner join with the one we've just built:
var list2 = from l in list
join m in max_date on l.Date equals m.Val
select l;
foreach (var l in list2)
{
Console.WriteLine($"{l.Reference}, {l.Donation}, {l.Date}, {l.Amount}");
}
the result will be
b, _b, 31/12/2018 00:00:00, 2
c, _c, 31/12/2018 00:00:00, 3
as desired. Hope it helps!
Linq lamda query:
var q = db.UnitPrice
.Where(x1 => x1.EffectiveDateTime == db.UnitPrice
.Where(x2 => x2.EffectiveDateTime < DateTime.Now)
.Max(x3 => x3.EffectiveDateTime)
)
.ToList();
Related
I'm using ASP.NET Core 3.1. I have written some code like the following and now I want to get the result of these two queries that has the same size and iterate over each one of them and divide their element and store the result in a list. But now the problem is in my zip method, I can not specify exactly which attribute of each query I want to divide.
var mytotal = _context.Apiapp.GroupBy(o => new
{
Month = o.ApiRequestDate.Substring(4, 2),
Year = o.ApiRequestDate.Substring(0, 4)
}).Select(g => new
{
Month = g.Key.Month,
Year = g.Key.Year,
Total = g.Count()
}).OrderByDescending(a => a.Year).ThenByDescending(a => a.Month).ToList();
var numerator = from t1 in _context.Apiapp
join t2 in _context.ApiAppHistory on t1.Id equals t2.ApiApplicantId
join t3 in _context.EntityType on t2.LastReqStatus equals t3.Id
where t1.IsDeleted == false && t1.LastRequestStatus == t2.Id && t3.Name == "granted"
group new { Year = t1.ApiRequestDate.Substring(0, 4), Month = t1.ApiRequestDate.Substring(4, 2) }
by new { t2.LastReqStatus } into g
select new
{
Year = g.Max(n => n.Year),
Month = g.Max(n => n.Month),
GrantedCount = g.Count()
};
var GrantedReqStatus = numerator.ToList();
var GrantedAccessPercent = new List<Double>();
//-------------------------------------------------------
var res = mytotal.Zip(GrantedReqStatus, (total, GrantedCount) => new { Num = total, Denum = GrantedCount });
foreach(var r in res)
{
GrantedAccessPercent.Add(r.Num/r.Denum);
}
Here inside the body of foreach, r.Num and r.Denum is unknown! I appreciate of any help to fix the error.
The Num and DemNum in the Zip function represent objects for the first and second collection, that contains Month,Year and Total for the total object and Month,Year and GrantedCount for the grantedCount object.
You could use total.Total and grantedCount.GrantedCount to get numbers, like the following code:
var res = mytotal.Zip(GrantedReqStatus, (total, grantedCount) => new { Num = total.Total, Denum = grantedCount.GrantedCount });
foreach(var r in res)
{
GrantedAccessPercent.Add(Math.Round(r.Num / (double)r.DemNum, 2));
}
Note that, to divide int1/int2 you need to cast int2 to double, Will give the expected result, and you can use also Math.Round to specify numbers after comma.
I hope this help you fix the issue.
I have following LINQ query
var unallocatedOrders = (from orderLine in context.OrderLineItemDboes
where (orderLine.Status == unallocated || orderLine.Status == null)
&& orderLine.orderline.order.order_status_fk == verified
group orderLine
by new { orderLine.orderline.ol_id,orderLine.orderline.order.order_id }
into g
select new { OrderLineId = g.Key.ol_id, Count = g.Count(), OrderId = g.Key.order_id })
.ToList();
Above query giving me results in the following way
Order1 ol1 2
order1 ol2 3
order1 ol3 1
order2 ol1 1
order2 ol2 2
order3 ol1 4
order3 ol2 3
order3 ol3 2
I need to iterate through the above list based on order ids and need to fetch corresponding lines and quantity.
I need to get this line id and quantity to a Dictionary.
Can somebody suggest how can I get it done.
Thanks
Here's how you can select the items using GroupBy. (Your question doesn't really specify how you want to use the lines, so I just output them to the Debug console.)
// group by the OrderId
foreach (var group in unallocatedOrders.GroupBy(row => row.OrderId))
{
Debug.WriteLine(
// for each line, output "Order x has lines y1, y2, y3..."
string.Format("Order {0} has lines {1}",
// here the key is the OrderId
group.Key,
// comma-delimited output
string.Join(", ",
// select each value in the group, and output its OrderLineId, and quantity
group.Select(item =>
string.Format("{0} (quantity {1})", item.OrderLineId, item.Count)
)
)
)
);
}
You can get a dictionary lookup by using ToDictionary.
// two-level lookup: 1) OrderId 2) OrderLineId
var lookup = new Dictionary<int, Dictionary<int, long>>();
foreach (var group in unallocatedOrders.GroupBy(row => row.OrderId))
{
// add each order to the lookup
lookup.Add(group.Key, group.ToDictionary(
// key selector
keySelector: item => item.OrderLineId,
// value selector
elementSelector: item => item.Count()
));
}
I am using BLToolKit in a project of mine and I was trying to get this to work. What I don't like is that I am trying to average a bunch of temps down to the minute, but the select statement that is being generated groups by the minute but then selects the original time. I think I am doing the linq expression correctly (but then again, i am not getting the results i expect). (this is C#, if you care) Anyone know what is going wrong?
var test = (from r in db.SensorReadingRaws
where r.TimeLogged < DateTime.Now.AddMinutes(-2)
group r by new
{
Sensor = r.SensorNumber,
//group time down to the minute
Time = r.TimeLogged.AddSeconds(-1 * r.TimeLogged.Second).AddMilliseconds(-1 * r.TimeLogged.Millisecond)
} into grouped
select new SensorReading
{
SensorNumber = grouped.Key.Sensor,
TimeLogged = grouped.Key.Time,
Reading = (int)grouped.Average(x => x.Reading)
}).ToList();
textBox1.Text = db.LastQuery;
and the resulting query is this
SELECT
[r].[SensorNumber],
[r].[TimeLogged],
Avg([r].[Reading]) as [c1]
FROM
[SensorReadingRaw] [r]
WHERE
[r].[TimeLogged] < #p1
GROUP BY
[r].[SensorNumber],
DateAdd(Millisecond, Convert(Float, -DatePart(Millisecond, [r].[TimeLogged])), DateAdd(Second, Convert(Float, -DatePart(Second, [r].[TimeLogged])), [r].[TimeLogged])),
[r].[TimeLogged]
I discovered that
BLToolkit.Data.Linq.Sql.AsSql<T>(T obj)
can be used as a workaround for this case.
When applying this function to the required grouped key properties in select statement you get rid of grouping/selecting an original field.
It may look something like:
_queryStore.Leads().
GroupBy(x => new {
x.LeadDate.Hour,
x.LeadDate.Minute
}).
Select(x => new {
Hour = Sql.AsSql(x.Key.Hour),
Minute = Sql.AsSql(x.Key.Minute),
Count = x.Count()
});
and in your particular case:
var test = (from r in db.SensorReadingRaws
where r.TimeLogged < DateTime.Now.AddMinutes(-2)
group r by new
{
Sensor = r.SensorNumber,
//group time down to the minute
Time = r.TimeLogged.AddSeconds(-1 * r.TimeLogged.Second).AddMilliseconds(-1 * r.TimeLogged.Millisecond)
} into grouped
select new SensorReading
{
SensorNumber = grouped.Key.Sensor,
TimeLogged = Sql.AsSql(grouped.Key.Time),
Reading = (int)grouped.Average(x => x.Reading)
}).ToList();
I got same issue yesterday.
Today I found a workaround. The idea is to write 2 linq queries. First transforming the data and the second grouping the result:
var bandAndDate =
(from r in repo.Entities
select new {Band = r.Score / 33, r.StartTime.Date});
var examsByBandAndDay =
(from r in bandAndDate
group r by new {r.Band, r.Date } into g
select new { g.Key.Date, g.Key.Band, Count = g.Count() }).ToList();
Both this queries run one SQL that do the job:
SELECT
[t1].[c1] as [c11],
[t1].[c2] as [c21],
Count(*) as [c3]
FROM
(
SELECT
[r].[Score] / 33 as [c2],
Cast(Floor(Cast([r].[StartTime] as Float)) as DateTime) as [c1]
FROM
[Results] [r]
) [t1]
GROUP BY
[t1].[c2],
[t1].[c1]
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#.
I have to work out how to write the following SQL query usingLINQ query or method syntax. (Edit: This is to return a list of latest AgentActivities for all Agents).
SELECT
a.[AgentActivityId],
a.[AgentId],
a.[ActivityId],
a.[StartedAt],
a.[EndedAt],
a.[Version]
FROM
[dbo].[AgentActivity] a
INNER JOIN
(
SELECT
[AgentId],
MAX([StartedAt])[StartedAt]
FROM
[dbo].[AgentActivity]
WHERE
([StartedAt] > '2010/01/24 23:59:59')
AND ([StartedAt] < '2010/10/25')
GROUP BY
AgentId
)grouped
ON (a.[AgentId] = grouped.[AgentId]
AND a.[StartedAt] = grouped.[StartedAt])
Just to recap, here's how I interpret the question:
What you want is a list with the most recently started activity for an agent, with the added requirement that the activity must be started within a given date interval.
This is one way to do it:
// the given date interval
DateTime startDate = new DateTime(2010, 1, 24);
DateTime endDate = new DateTime(2010, 10, 25);
IEnumerable<AgentActivity> agentActivities =
... original list of AgentActivities ...
IEnumerable<AgentActivity> latestAgentActivitiesByAgent = agentActivities
.Where(a => a.StartedAt >= startDate && a.StartedAt < endDate)
.GroupBy(a => a.AgentId)
.Select(g => g
.OrderByDescending(a => a.StartedAt)
.First());
(If the question involves LINQ to SQL, there may be some gotchas. I haven't tried that.)