I have the following query:
List<Meeting> meetings =
(from m in crdb.tl_cr_Meetings
join p in crdb.tl_cr_Participants on m.MeetingID equals p.MeetingID
where p.TimeOut == null
select new Meeting
{
MeetingID = m.MeetingID,
MeetingName = m.MeetingName,
HostUserName = m.HostUsername,
BlueJeansMeetingID = m.tl_cr_BlueJeansAccount.MeetingID,
StartTime = m.StartTime,
Participants = (from pa in crdb.tl_cr_Participants
where pa.MeetingID == m.MeetingID
select pa.tl_cr_BlueJeansAccount.DisplayName).ToList()
}).Distinct().ToList();
And I want it to bring back a list of unique meetings. For some reason it brings back an entry for every participant, even though the data is identical:
Am I missing a grouping somewhere?
EDIT
The meeting class is currently very basic:
public class Meeting
{
public int MeetingID { get; set; }
public string MeetingName { get; set; }
public string HostUserName { get; set; }
public DateTime StartTime { get; set; }
public List<string> Participants { get; set; }
public string BlueJeansMeetingID { get; set; }
}
I believe the reason you get an entry for every participant is that you perform two joins.
You need to do a groupjoin.
var meetings = crdb.tl_cr_Meetings.GroupJoin(crdb.tl_cr_Participants,
k => k.MeetingID,
k => k.MeetingID,
(o,i) => new Meeting
{
MeetingID = o.MeetingID,
MeetingName = o.MeetingName,
HostUserName = o.HostUsername,
BlueJeansMeetingID = o.tl_cr_BlueJeansAccount.MeetingID,
StartTime = o.StartTime,
Participants = i.Select(s => s.DisplayName)
}).ToList();
You can use the way that juharr advise you, or you can implement comparer as separate class that implements IEqualityComparer interface and pass this comparer to distinct.
public class MeetingComparer : IEqualityComparer<Meeting>
{
public bool Equals (Meeting x, Meeting y)
{
return x.smth.Equals (y.smth);
}
public int GetHashCode (Meeting obj)
{
return obj.smth.GetHashCode ();
}
}
I think you just have to delete the 3rd line of your code (join ...).
Is this Linq to Entities? Regardless, I would remove the Distinct and add a group by.
List<Meeting> meetings =
(from m in crdb.tl_cr_Meetings
join p in crdb.tl_cr_Participants on m.MeetingID equals p.MeetingID
where p.TimeOut == null
group m by new { m.MeetingID, m.MeetingName, m.HostUsername, MeetingID2 = m.tl_cr_BlueJeansAccount.MeetingID, m.StartTime } into m
select new Meeting
{
MeetingID = m.Key.MeetingID,
MeetingName = m.Key.MeetingName,
HostUserName = m.Key.HostUsername,
BlueJeansMeetingID = m.Key.MeetingID2,
StartTime = m.Key.StartTime,
Participants = (from pa in crdb.tl_cr_Participants
where pa.MeetingID == m.Key.MeetingID
select pa.tl_cr_BlueJeansAccount.DisplayName).ToList()
}).ToList();
gabba explained why your code is failing. Here's a way you can re-work your query to not even need a Distinct() and make it a little cleaner looking, using a group join:
from m in crdb.tl_cr_Meetings
join p in crdb.tl_cr_Participants on new { m.MeetingID, null }
equals new { p.MeetingID, p.TimeOut } into meetingParticipants
select new Meeting
{
MeetingID = m.MeetingID,
MeetingName = m.MeetingName,
HostUserName = m.HostUsername,
BlueJeansMeetingID = m.tl_cr_BlueJeansAccount.MeetingID,
StartTime = m.StartTime,
Participants = meetingParticipants
.Select(x => x.tl_cr_BlueJeansAccount.DisplayName)
.ToList()
}
Related
I have three tables like this:
public partial class PriceSite
{
public DateTime ValidFromGmtDtm { get; set; }
public long PriceSiteId { get; set; }
public virtual ICollection<PriceSiteProduct> PriceSiteProduct { get; set; }
}
public partial class PriceSiteProduct
{
public long PriceSiteId { get; set; }
public long ProductId { get; set; }
public virtual Product Product { get; set; }
}
public partial class Product
{
public string ProductCd { get; set; }
public string ProductNm { get; set; }
public long ProductId { get; set; }
public virtual ICollection<PriceSiteProduct> PriceSiteProduct { get; set; }
}
We need to group the data by Productname and choose the product with highest ValidFromGmtDtm (Pricesite).
I tried this query
using (var dbContext = _dbContextProvider.DbContext)
{
var data = from t1 in dbContext.PriceSite
join t2 in dbContext.PriceSiteProduct
on t1.PriceSiteId equals t2.PriceSiteId
join t3 in dbContext.Product
on t2.ProductId equals t3.ProductId
where t1.ValidFromGmtDtm > shellSiteNotification.ValidFromDtm
select new
{
PriceSiteId = t1.PriceSiteId,
ValidFrom = t1.ValidFromGmtDtm,
ProductId = t3.ProductId,
ProductNm = t3.ProductNm,
ProductValues = t3.PumpPriceProduct
};
var res1 = data.ToList();
var data2 = from element in res1
group element by element.ProductNm into groups
select groups.OrderByDescending(p => p.ValidFrom).FirstOrDefault();
}
I am getting timeout error by running this query.
I also tried this query:
var grouped = from priceSite in _dbContextProvider.DbContext.PriceSite
where priceSite.ValidFromGmtDtm < shellSiteNotification.ValidFromDtm
from priceSiteProduct in priceSite.PriceSiteProduct
group priceSiteProduct by priceSiteProduct.Product.ProductNm into produtGroup
select new
{
ProductNamKey = produtGroup.Key,
produtGroup = produtGroup.OrderByDescending(x => x.PriceSite.ValidFromGmtDtm)
};
This code throws a runtime error as we cannot access the navigation property in select in EF Core.
I also tried another query - but this also results in a timeout
var result = from s in _dbContextProvider.FreshReadOnlyPricingDbContext.PriceSite
from r in s.PriceSiteProduct
let p = r.Product
select new
{
PriceSiteId = s.PriceSiteId,
ValidFrom = s.ValidFromGmtDtm,
ProductId = p.ProductId,
ProductNm = p.ProductNm,
ProductValues = p.PumpPriceProduct
};
var list = result.ToList();
It is known EF and SQL limitation, you cannot get items after grouping. Only Key and aggregation result is supported. So basically EF proposes to put ToList() before grouping and it means that you will load several million records to the client.
Without third party extensions which can do that effectively, you can use the following workaround:
using (var dbContext = _dbContextProvider.DbContext)
{
var dataQuery =
from t1 in dbContext.PriceSite
join t2 in dbContext.PriceSiteProduct
on t1.PriceSiteId equals t2.PriceSiteId
join t3 in dbContext.Product
on t2.ProductId equals t3.ProductId
where t1.ValidFromGmtDtm > shellSiteNotification.ValidFromDtm
select new
{
PriceSiteId = t1.PriceSiteId,
ValidFrom = t1.ValidFromGmtDtm,
ProductId = t3.ProductId,
ProductNm = t3.ProductNm,
ProductValues = t3.PumpPriceProduct
};
var query =
from key in dataQuery.Select(d => new { d.ProductNm }).Distinct()
from d in dataQuery.Where(d => d.ProductNm == key.ProductNm)
.OrderByDescending(d => d.ValidFrom)
.Take(1)
select d;
var result = query.ToList();
}
i have a question, i want to create a linq query that returns a list of object.
This is the model
public class Test
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(5)]
public string Code { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[NotMapped]
public string Reference { get; set; }
}
The query that i want to do is simple: context.Test.ToList();
this returns the database mapping Reference is null since is not part of the table.
Now if i create a linq query i know that i can do select new { all fields here }
i want to avoid this:
select new Test
{
Reference = r,
ID = t.ID,
Code = t.Code,
Name = t.Name
}).ToList();
is it possible to do something like this
(from t in context.Test
join r in context.Reference on f.ID equals r.ID
select new
{
t.Reference = r.Reference,
t
}).ToList();
i want to set the Reference value inside the same query, is that possible?
What are you asking is not directly supported in LINQ to Entities - neither projection to entity type, nor expression block which is the only way to assign properties of an existing object.
As usual, the typical workaround is to split the query on two parts - one being LINQ to Entities query selecting the necessary data (usually into intermediate anonymous type), then switch to LINQ to Objects with AsEnumerable() and do the rest - in this case using block inside Select:
var result =
(from t in context.Test
join r in context.Reference on f.ID equals r.ID
select new { t, r.Reference }
).AsEnumerable()
.Select(x =>
{
x.t.Reference = x.Reference;
return x.t;
}).ToList();
Don't select an anonymous object, just create a new T from the one you have.
(from t in context.Test
join r in context.Reference on t.ID equals r.ID
select new Test
{
Reference = r,
ID = t.ID,
Code = t.Code,
Name = t.Name
}).ToList();
EDIT:
To avoid having to manually copy over all the properties
public class Test
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Reference { get; set; }
public Test CopyWithReference(string reference)
{
var copy = (Test)this.MemberwiseClone();
copy.Reference = reference;
return copy;
}
}
Then...
(from t in context.Test
join r in context.Reference on t.ID equals r.ID
select t.CopyWithReference(r)).ToList();
Try following :
(from t in context.Test
join r in context.Reference on f.ID equals r.ID
select new Test()
{
ID = t.ID,
Code = t.Code,
Name = t.Name,
Reference = r.Reference
}).ToList();
Try:
var result = context.Test.Include("Reference").ToList();
or:
var result = context.Test.Include(t => t.Reference).ToList();
or Try Lambda Expressions:
var result = context.Test.Select(t => new {
t,
t.Reference = t.Reference.Select(r => new {
r.Reference })
}).AsEnumerable().Select(x => x.r).ToList();
I have the following classes.
Course;
public class Course
{
//pk
public int Id{ get; set; }
public int SourceCourseId { get; set; }
public string Name { get; set; }
}
Registration
public class Registration
{
//primary key
public int Id { get; set; }
//...more fields
public int CourseId { get; set; }
}
I want to obtain a collection of annonymous objects with the two fields below for all Courses that are Distinct in the registrations table that are not in the Courses table.
var distinctCourses = (from registration in db.Registrations
join courses in db.Courses on registration.CourseId equals courses.SourceCourseId
where registration.CourseId != courses.SourceCourseId
select new
{
SourceCourseId = registration.CourseId,
Name = registration.CourseName,
}).Distinct().ToList();
For some reason the above is returning 0... Any suggestions?
try a left join:
var query = from r in registrations
join c in courses on r.CourseId equals c.id into newCourses
from nullCourse in newCourses.DefaultIfEmpty()
where nullCourse == null
select new { }
Edit - per comment from Alex :
Also, your where clause needs to change to
where nullCourse == null
Edit - changed join columns and added correct where clause.
Edit - group registrations on CourseID so they will be distinct
var distinctCourses =
(from registration in db.Registrations
group registration by registration.CourseId into grp
from reg in grp
join courses in db.Courses on reg.CourseId equals courses.SourceCourseId into newCourses
from nullCourse in newCourses.DefaultIfEmpty()
where nullCourse == null
select new
{
SourceCourseId = reg.CourseId,
Name = reg.CourseName,
}).ToList();
Try this
var result = Registrations.GroupJoin(Courses,r=>r.CourseId,c=>c.SourceCourseId,
(k,g) => new {k,g})
.Where(x=>x.g.Count()==0)
.Select(s=> new {id=s.k.CourseId,name=s.k.CourseName});
Hi I have following two model classes
public class c1
{
public int id { get; set; }
public int ptId { get; set; }
public int bId { get; set; }
public int rId { get; set; }
public IEnumerable<styles> newStruct { get; set; }
}
public class styles
{
public int id { get; set; }
public int bId { get; set; }
public string desc { get; set; }
}
I am trying to write a linq query
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
select new c1
{
pId= c.pId,
id = c.id,
newStruct = new List<styles>
{
new styles
{
id=y.room_id,
desc=y.desc,
}
}
});
return records.ToList();
Problem I am having is that in newStruct is suppose to be List of all the styles but it just returns one style at a time rather than one list.
Please let me know how can it return record where inside it contains list of styles
Thanks
If you want to get a sublist by main list, you should use group by,
You can try this, but I'm not sure it worked. Becasue I couldn't compile it.
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
group c by new
{
c.pId,
c.id
} into gcs
select new c1
{
pId = c.Key.pId,
id = c.Key.id,
newStruct = from g in gcs select new styles { id=g.room_id, desc=g.desc}
});
Is this LINQ to Entities? If so, and the mappings are correct in the edmx, you can try:
var records =
from c in db.secondary
where c.id == id
select new c1
{
pId = c.pId,
id = c.id,
newStruct = c.main.Select(m => new styles { id = m.room_id, desc = m.desc })
};
return records.ToList();
I have a linq query, which is further having a subquery, I want to store the result of that query into a user defined type, my query is
var val = (from emp in Employees
join dept in Departments
on emp.EmployeeID equals dept.EmployeeID
select new Hello
{
EmployeeID = emp.EmployeeID
Spaces = (from order in Orders
join space in SpaceTypes
on order.OrderSpaceTypeID equals space.OrderSpaceTypeID
where order.EmployeeID == emp.EmployeeID group new { order, space } by new { order.OrderSpaceTypeID, space.SpaceTypeCode } into g
select new
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})
}).ToList();
Definition for my Hello class is
public class Hello
{
public IEnumerable<World> Spaces { get; set; }
public int PassengerTripID { get; set; }
}
Definition for my World class is
public class World
{
public int ID { get; set; }
public string Code { get; set; }
public int Count { get; set; }
}
You are creating anonymous object but you need to specify type name World
select new World
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})