I have two tables, and I calculate Post Views from Views table using ViewDate column and then I want to get PostTItle using .GroupBy for Entity Framework using foreign key PostID.
Posts table:
PostID PostTitle
--------------------
1 post one
2 post two
3 post three
4 post four
5 post five
6 post six
Views table:
ViewID ViewDate PostID
---------------------------------------
1 2015 07 17 19:00:00 1
2 2015 07 17 20:00:00 1
3 2015 07 17 21:00:00 2
4 2015 07 18 19:00:00 2
5 2015 07 19 19:00:00 2
6 2015 07 21 19:00:00 1
7 2015 07 23 19:00:00 2
so far this is what I have done
return _db.ObjectSet.Where(p => DateTime.Now >= EntityFunctions.AddDays(p.ViewDate, -14))
.GroupBy(y => y.PostID, y => y.ViewDate, (ID, Date) => new ExampleViewModel
{
Post_ID = ID,
View_Date = Date.Count()
}).OrderByDescending(z => z.View_Date).Take(5);
but using this solution I can only assign Post_ID and View_Date to ExampleViewModel, How can I get the PostTitle using the foreign key?
Note: I am trying to get most viewed (Hot) Posts in last 14 days
Please help
Expected Output:
Title:post one, Id:1, Views:3
Title:post two, Id:2, Views:4
One solution could be applying a join between those entities and include the PostTitle in the fields you want to group:
var query= (from v in db.Views
join p in db.Posts on v.PostID equals p.Id
where DbFunctions.DiffDays(v.ViewDate,DateTime.Now)<=14
group new{v,p} by new {v.PostID, p.PostTitle, v.ViewDate} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.Key.PostTitle}
).Take(5);
As you can see, I'm using DbFunction class instead EntityFunction. The DbFunctions class was introduced in Entity Framework 6 and it is shipped separately from the .NET Framework. For any new applications using versions of EF starting with 6.0, you should use the DbFunctions class. Anyway, if you don't want to use now that class, you could also use the EntityFunctions.DiffDays method.
Now if both entities are related:
public class Post
{
public int ID{get;set;}
// ...
public virtual ICollection<View> Views{get;set;}
}
public class View
{
public int ID{get;set;}
public int PostID{get;set;}
// ...
public virtual Post Post{get;set;}
}
You could also do this:
var query= (from v in db.Views
where DbFunctions.DiffDays(v.ViewDate,DateTime.Now)<=14
group v by new {v.PostID, v.ViewDate} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.First().Post.PostTitle}
).Take(5);
Update 1
To avoid use EntityFunctions class you can subtract 14 days to the current date and compare directly both dates in your query:
var date = DateTime.Now.AddDays(-14);
var query= (from v in db.Views
join p in db.Posts on v.PostID equals p.Id
where v.ViewDate>=date
group new{v,p} by new {v.PostID, p.PostTitle, v.ViewDate} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.Key.PostTitle}
).Take(5);
Update 2
That is because you're grouping by date. To obtain the result you're expecting you need to remove that field from the elements you are grouping:
var query= (from v in db.Views
join p in db.Posts on v.PostID equals p.Id
where v.ViewDate>=date
group new{v,p} by new {v.PostID, p.PostTitle} into g
let count=g.Count()
orderby count descending
select new{ Post_ID=g.Key.PostID, View_Date=count, Title= g.Key.PostTitle}
).Take(5);
If you have the Posts table on your context you can retrieve it from there by the PostId:
return _db.ObjectSet.Where(p => DateTime.Now >= EntityFunctions.AddDays(p.ViewDate, -14))
.GroupBy(y => y.PostID, y => y.ViewDate, (ID, Date) => new ExampleViewModel
{
Post_ID = ID,
View_Date = Date.Count(),
Title = _db.Posts.Find(ID).PostTitle
}).OrderByDescending(z => z.View_Date).Take(5);
Related
I have tried every variation of this query in EF Core and I cannot figure it out.
I have simplified this as such:
Invoice Table
InvoiceId
stuff
1
A
2
B
InvoiceLog Table
Id
InvoiceId
Date
PersonId
1
1
11/12/2015
1
2
2
1/20/2018
2
3
2
3/15/2019
3
Person Table
Id
Name
1
Bob
2
Steve
Here's my question
var vm = (from i in _context.Invoice
join l in _context.InvoiceLog on i.InvoiceId equals l.InvoiceId
**//this is returning multiple records, how do I return only the one with the Min(Date)**
join pl in _context.Person on l.UpdateUserId equals pl.PersonId
select new InvoiceViewModel
{
InvoiceId = i.InvoiceId,
SubmitterName = pl.FirstName + " " + pl.LastName,
}).ToList();
ex> for Invoice 2 this should return ONE InvoiceLog record with the min date 1/20/2018 and the Person Name of Steve.
This is the error I get:
System.InvalidOperationException
HResult=0x80131509
Message=Processing of the LINQ expression 'DbSet
.GroupJoin(
outer: DbSet,
inner: i => i.InvoiceId,
outerKeySelector: l => l.InvoiceId,
innerKeySelector: (i, lj) => new {
i = i,
lj = lj
})' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core.
See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
Source=Microsoft.EntityFrameworkCore
Instead of using join, you can use a cross-join by using from twice and filtering the second source down to the matching InvoiceLog rows and then taking the lowest one:
var vm = (from i in _context.Invoice
from l in (from l2 in _context.InvoiceLog
where i.InvoiceId == l2.InvoiceId
orderby l2.Date
select l2).Take(1)
join pl in _context.Person on l.UpdateUserId equals pl.Id
select new InvoiceViewModel {
InvoiceId = i.InvoiceId,
SubmitterName = pl.Name
}).ToList();
NOTE: EF Core 5 supports filtered include, with OrderBy and Take, so you could setup a proper relation between Invoice and InvoiceLog and then just Include what you need.
I'm developing a web application using Entity Framework.
I need do a select and pass values for an Ilist but it's returns duplicate values.
IQueryable<establishmentInfo> filter = (from x in db.establishments
join t in db.establishment_categories on x.id equals t.establishment
join q in db.categories on t.category equals q.id
where (x.name.ToUpper().Contains(search.ToUpper()))
select new establishmentInfo
{
id = x.id,
name = x.name,
id_category = q.id,
category = q.name,
});
IList<establishmentInfo>establishments = filter.ToList();
Establishment table
id name email
---------------------------
1 AAA a#a.com
2 BBB b#b.com
Establishment_categories
id establishment category
-------------------------------
1 1 1
2 1 2
3 2 1
Categories
id name
---------------------
1 alpha
2 beta
The problem is that return 2 establishments, one with category 1 and other with category 2. I need remove one of these.
Can anyone help?
As #NetMage said,your linq statement should return two values that are not repeated.
We can see that there are two records with establishment set to 1 in your Establishment_categories table. You can check your establishments. The id_category should be 1, the category should be alpha, the other should be id_categoryis 2, and the category should be beta.
You can see below image:
If you only want to get the first data of establishments, you can write the code as follows:
IQueryable<Establishment> filter = (from x in _context.Establishments
join t in _context.Establishment_Categories on x.Id equals t.EstablishmentId
join q in _context.Categories on t.CategoryId equals q.Id
where x.Name.ToUpper().Contains(search.ToUpper())
select new Establishment
{
Id = x.Id,
Name = x.Name,
CategoryId = q.Id,
CategoryName = q.Name,
}).Take(1);
List<Establishment> establishments = filter.ToList();
Result:
By the way, assuming that there are duplicates in your returned data, you can add the .Distinct() method after your linq to remove duplicates.
Books Table
Id VendorId ASIN Price
-- -------- ---- ------
1 gold123 123 10
2 sil123 123 11
3 gold456 456 15
4 gold678 678 12
5 sil456 456 12
6 gold980 980 12
I want to write a linq query which will return me rows for which corresponding to every gold if sil vendor id not exist. The last three digit of vendor Id is corresponding ASIN column in that row.
Ex- For gold123 corresponding sil123 exist so that row will not be returned but for gold678 and gold980 corresponding sil not exist. So those rows will be returned.
I tried following
var gold = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("gold"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
var asinsForGold = gold.Select(x => x.ASIN).ToList();
var correspondingSilver = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("sil")
&& asinsForGold.Contains(x.ASIN)).ToList();
var correspondingSilverAsins = correspondingSilver.Select(x => x.ASIN).ToList();
var goldWithoutCorrespondingSilver = gold.Where(x =>
!correspondingSilverAsins.Contains(x.ASIN));
Can We apply self join or better way to get result only in one query instead of two query and several other list statement.
It's just another predicate, "where a corresponding silver vendor doesn't exist":
var goldWoSilver = _repository.Query<Books>()
.Where(x => x.VendorId.Contains("gold"))
.Where(x => !_repository.Query<Books>()
.Any(s => s.ASIN == x.ASIN
&& s.VendorId.Contains("sil"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
In many cases this is a successful recipe: start the query with the entity you want to return and only add predicates. In general, joins shouldn't be used for filtering, only to collect related data, although in that case navigation properties should be used which implicitly translate to SQL joins.
See if it helps -
var goldWithoutCorrespondingSilver = from b1 in books
join b2 in books on b1.ASIN equals b2.ASIN
where b1.VendorId.Contains("gold")
group b2 by b1.VendorId into g
where !g.Any(x => x.VendorId.Contains("sil"))
select g.FirstOrDefault();
What I have done is -
Selected records with matching ASIN
Grouped them by VendorID
Selected ones which do not have sil
I have two tables like below: (date format : mm/dd/yyyy)
Parameter master table:
Id Parameter
1 ST
2 GP
3 Interest
4 CC
And Second TaxValue table :
Date ParameterId Value
1/1/2017 1 4
2/1/2017 1 4.5
1/15/2017 2 15
3/20/2017 2 20
3/21/2017 3 18
4/28/2017 3 20
1/1/2017 4 10
I want to write a linq query to get all the latest parameters values from the date specified.
Desired Result: (if I want to get latest entries for today)
Date Parameter Value
2/1/2017 ST 4.5
3/20/2017 GP 20
4/28/2017 Interest 20
1/1/2017 CC 10
Please help!!
User OrderByDescending
var result = from n in table
group n by n.Value into g
select g.OrderByDescending(t=>t.Date).FirstOrDefault();
You can do a join on the latest TaxValue record like this:
var qry = (from pm in parameterMaster
from tax in taxValue.Where(t => t.ParameterId == pm.Id)
.OrderByDescending(t => t.Date)
.Take(1)
.DefaultIfEmpty()
select new {
date = tax.Date,
parameter = pm.Name,
value = tax.Value
});
var results = qry.ToList();
You can use this query: (I tested it and it worked properly)
using (testEntities db = new testEntities())
{
var res = from element in
(from p in db.Parameters
join t in db.TaxValues on p.Id equals t.ParameterId
select new { Date = t.Date, Parameter = p.Parameter1, Value = t.Value, ParameterId = t.ParameterId }).ToList()
group element by element.ParameterId
into groups
select groups.OrderBy(p => p.Value).Last();
}
Result
Please do not give me help in lamba
I have two tables.
Employees:
pk
name
ExpenseTeamMembers:
pk
expMgrPk
empPk
Example: pk expMgrPk empPk
1 7 81
2 7 101
3 13 99
4 13 22
5 13 56
Basically the first table is a list of employees and the second is a table for keeping track of which employees belong to which manager.
The first sql lookup works and mgr is getting me the pk of the selected name in the combo box.
What I am trying to do in the join is lookup the expMgrPk and see which employees belong to it and return their names instead of their pk. I am pretty off and need a little help. Again please do not give me help in lamba!! Thanks
private void cboManagers_SelectedIndexChanged(object sender, EventArgs e)
{
if (cboManagers != null)
{
string selectedMgr = (string)cboManagers.SelectedValue;
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
int mgr = (from f in db.employees
where f.name == selectedMgr
select f.pk).FirstOrDefault();
var emps = (from m in db.employees
join t in db.expenseTeamMembers on m.pk equals t.pK
where t.expMgrPk == mgr
select m.name).ToList();
lstSelected.DataSource = emps;
}
}
}
In linq-to-sql, you can write joins more easily by writing two from statements combined with a where statement. Something like this:
var emps = (from f in db.employees
from m in db.expenseTeamMembers
where m.pk == mgr && f.pk == m.empPk
select f.name).ToList();
I've found this syntax to be easier and when your code is compiled, the query is converted to a traditional T-SQL join.