I have this scenario
public class TimeSheet
{
[Key]
public int TimeSheetID { get; set; }
public string Username { get; set; }
}
public class Approval
{
[Key]
public int ApprovalID { get; set; }
[Index(IsUnique = true)]
[StringLength(450)]
public string Approver { get; set; }
public virtual ICollection<ApprovalDetail> Details { get; set; }
}
public class ApprovalDetail
{
[Key]
public int ApprovalDetailID { get; set; }
[StringLength(450)]
public string Username { get; set; }
}
I want to the following syntax in EF.
SELECT
*
FROM
TimeSheet
WHERE
UserName IN (SELECT
[AD].Username
FROM
Approval [A]
INNER JOIN
ApprovalDetail [AD] ON [A].ApprovalID = [AD].ApprovalID
WHERE
[A].Approver = 'warheat1990')
How to achieve this?
UPDATE :
My Repo
public IEnumerable<TimeSheet> List()
{
return _timeSheet.AsEnumerable().ToList();
}
public IEnumerable<TimeSheet> ListByUsername(string username)
{
return _timeSheet.Where(w => w.Username == username).ToList();
}
This should do it:
var usernamesByApprover = approvals
.Where(a => a.Approver == "warheat1990")
.SelectMany(a => a.Details.Select(d => d.Username));
var timesheetsByApprover = timesheets
.Where(t => usernamesByApprover.Contains(t.Username));
Note that even if the query is split into two expressions, Entity Framework will convert it into a single SQL query once you evaluate the timesheetsByApprover variable because of deferred execution.
Related
I'm using EF Core and ASP.NET Core API, and I have entities that are more or less defined as follows:
public class MyUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Circle
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<MyUser> Members { get; set; } = new List<MyUser>();
}
public class Tender
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public TenderVisibility Visibility { get; set; }
public ICollection<TenderCircle> TenderCircles { get; set; } = new List<TenderCircle>();
}
public enum TenderVisibility
{
Public,
Circles,
}
public class TenderCircle
{
public int TenderId { get; set; }
[ForeignKey("TenderId")]
public Tender Tender { get; set; }
public int CircleId { get; set; }
[ForeignKey("CircleId")]
public Circle Circle { get; set; }
}
Then, I want to get all my Tenders where a certain user is member of the Circle if the visibility is set to Circles, so I have something like this:
public IEnumerable<Tender> GetTenders(string userId)
{
var user = _context.Users.Where(u => u.Id == userId);
return _context.Tenders
.Include("TenderCircles.Circle.Members")
.Where(t => t.Visibility == TenderVisibility.Public ||
(t.Visibility == TenderVisibility.Circles && t.TenderCircles.Select(tc => tc.Circle.Members).ToList().Contains(user.ToList()))).ToList()
.ToList();
}
But that seems to me way too complicated, and it actually doesn't work as it throws error saying it is not allowed for the second part where I select circle members and check whether user is contained. Any idea how to achieve what I want more efficiently?
You can't call ToList in Where condition. You can use Any instead;
public IEnumerable<Tender> GetTenders(string userId)
{
var user = _context.Users.Where(u => u.Id == userId);
return _context.Tenders
.Include("TenderCircles.Circle.Members")
.Where(t => t.Visibility == TenderVisibility.Public ||
(t.Visibility == TenderVisibility.Circles && t.TenderCircles.Any(tc => tc.Circle.Members.Any(m=>m.Id == userId)))
.ToList();
}
I couldn't try it because it is a bit complicated to reproduce. But please let me know if there is any syntax error
I am working with the following technologies: C#, SQL Server, ASP.NET and Entity Framework and Linq.
I have a many to many relation , using eager load. I want to get all the courses where a student is inscribed. As you can see I have a one to many relation from student to inscribe table.
The model classes:
public class Courses
{
[Required]
public int Id { get; set; }
//more properties here
public student stud { get; set; }
}
public class Enroll
{
[Key]
public intId { get; set; }
//properties here
[Required]
public string StudentId{ get; set; }
public Courses Courses{ get; set; }
}
public class student{
public intId { get; set; }
//other properties
public Inscripe Inscription {get;set}
}
This is what my controller:
public IEnumerable<course> GetCoursesStudent(Int studentId)
{
//some code here to validate
var result = _dbContext
.Enroll.Include(c => c.Courses)
.Where(c => c.StudentId == studentId)
.SelectMany(c => c.Courses).ToList();
}
problem :
I receive an error from the SelectMany: the type aregument for method Queribly.selectMany(IQueryableExpression>> can not be infered from the usage.
How can I fix it?
The type you specified for the IEnumerable is wrong. It should be "Courses" instead of "course":
public IEnumerable<Courses> GetCoursesStudent(Int studentId)
{
var result = _dbContext
.Enroll
.Include(c => c.Courses)
.Where(c => c.StudentId == studentId)
.SelectMany(c => c.Courses)
.ToList();
}
And the "Courses" property of Enroll class should be an enumerable:
public class Enroll
{
[Key]
public intId { get; set; }
[Required]
public string StudentId { get; set; }
public IEnumerable<Courses> Courses { get; set; }
}
I have a context called companyContext. There are Three tables Reports,Logs and Employees.I am given a case id and I need to get a list of all the employees who belong to a certain case (and a log if there is one, but i dont need to worry about that yet). So I made a query get all the employees where EmployeeID is equal to Employee.ID, where the Reports CaseID is eaual to case.id. However, its not reutning a list, its returning a Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable}]
Am I making the query correctly ? Thanks guys.
var employees = await context.Reports.Include(s => s.Employee)
.ThenInclude(e => e.ID)
.AsNoTracking()
.Where(r => r.CaseID == Case.Id);
Models
public class Log
{
public int ID { get; set; }
public string Input { get; set; }
public string Tag { get; set; }
public DateTime LogDate { get; set; }
public ICollection<Report> Reports { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public ICollection<Report> Reports { get; set; }
}
public class Report
{
public int ID { get; set; }
public string CaseID { get; set; }
public int EmployeeD { get; set; }
public int? LogID { get; set; }
public Employee Employee { get; set; }
public Log Log { get; set; }
}
SingleOrDefaultAsync
throws an exception if there is more than one element in the sequence.
So you should not be using SingleOrDefaultAsync if you expect multiple records.
If I'm interpreting your question properly and you want employees themselves, you could start with them and then narrow via the Employee class's .Reports navigation property.
var employees = await context.Employees.AsNoTracking()
.Where(e => e.Reports.Any(r => r.CaseID == case.Id))
.ToListAsync();
So entity is actually like using a real database. I had to do a where and select clause, but also a toList to convert it into something readable.
var employees = await context.Reports.Where(r => r.CaseID == Case.Id);
.Select(r => r.Employee)
.ToList();
How to use multiple condition in LINQ joins, i.e. in my scenario I need to get all the users from table User where group ID = 4 from table UserInGroup, where UserInGroup is intermediate table between User and Group table as in SQL-T we use join as
select *
from user
where user.userID = userIngroup.userID AND userIngroup.groupID == 4
....
In another approach I am using lambda expression along with LINQ, how I can apply where groupID = 4 in following one??
public IEnumerable<User> GetUsersByGroupID(int _groupID)
{
List<User> _listedUsersByGroupID = new List<User>();
using(var _uow = new UserManagement_UnitOfWork())
{
_listedUsersByGroupID = (from _users in _uow.User_Repository.GetAll()
.Include(s=>s.UserInGroup.Select(r=>r.Group))
select _users).ToList();
return _listedUsersByGroupID;
}
}
User Model
[Table("User")]
public class User
{
public User() { }
[Key]
public int UserID { get; set; }
[StringLength(250)]
[Required]
public string FirstName { get; set; }
[StringLength(250)]
[Required]
public string LastName { get; set; }
[Required]
public int Age { get; set; }
[StringLength(250)]
[Required]
public string EmailAddress { get; set; }
public ICollection<UserInGroup> UserInGroup { get; set; }
}
UserInGroup Model
[Table("UserInGroup")]
public class UserInGroup
{
public UserInGroup() { }
[Key]
public int UserGroupID { get; set; }
[Required]
public int UserID { get; set; }
[Required]
public int GroupID { get; set; }
public User User { get; set; }
public Group Group { get; set; }
}
Group Model
public class Group
{
public Group() { }
[Key]
public int GroupID { get; set; }
[StringLength(250)]
[Required]
public string GroupName { get; set; }
public ICollection<UserInGroup> UserInGroup { get; set; }
}
You only need to add a condition to filter the users that belong to the group 4. Try this:
_listedUsersByGroupID = (from _user in _uow.User_Repository.GetAll()
.Include(s=>s.UserInGroup.Select(r=>r.Group))
where user.UserInGroup.Any(ug=>ug.groupID==4)
select _user).ToList();
Lambda query would look something like:
ctx.User.Where(user=>
ctx.UserInGroup.Any(userIngroup=>
user.userID == userIngroup.userID && userIngroup.groupID == 4
)
)
That however is just query, if you want to get results add .AsList() or .AsEnumerable() to end.
However you can write silly and inefficient code if you do not fully understand what you are doing. I would reccomend you try this instead:
var publications = ctx.Database.SqlQuery<UserResults>(String.Format(#"
select UserID, FirstName,LastName,Age,EmailAddress,UserInGroup
from user
where user.userID = userIngroup.userID AND userIngroup.groupID == {0}
order by UserID
", Config.Group));
Where Config.Group is 4; UserResults can be User table as well, if you do not want other fields. You need to execute or enumerate over the sql query to use the data and like before you can use .AsList() or .AsEnumerable() for that.
Variable ctx is database context. For example:
using (var ctx = new toxicEntities())
{
}
I have Entity model classes as follows
public partial class User
{
public User()
{
this.webpages_Roles = new HashSet<webpages_Roles>();
}
public int UserID { get; set; }
public virtual ICollection<webpages_Roles> webpages_Roles { get; set; }
}
.
public partial class webpages_Roles
{
public webpages_Roles()
{
this.Users = new HashSet<User>();
this.Roles_X_ApplicationModules =
new HashSet<Roles_X_ApplicationModules>();
}
public int RoleId { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Roles_X_ApplicationModules>
Roles_X_ApplicationModules { get; set; }
}
.
public partial class Roles_X_ApplicationModules
{
public long ID { get; set; }
public Nullable<int> ModuleID { get; set; }
public Nullable<int> RoleID { get; set; }
public Nullable<bool> ViewPermission { get; set; }
public virtual ApplicationModule ApplicationModule { get; set; }
public virtual webpages_Roles webpages_Roles { get; set; }
}
.and
public partial class ApplicationModule
{
public ApplicationModule()
{
this.Roles_X_ApplicationModules =
new HashSet<Roles_X_ApplicationModules>();
}
public int ModuleID { get; set; }
public virtual ICollection<Roles_X_ApplicationModules>
Roles_X_ApplicationModules { get; set; }
}
you can see User object has a navigation property to webpages_Roles which again has navigation property to Roles_X_ApplicationModules and which in turn navigates to ApplicationModule..
now I want to get all the ApplicationModule from User..how do I write query using navigation properties..
I tried something like this..
var appModules = user.webpages_Roles.SingleOrDefault()
.Roles_X_ApplicationModules.Where(z => z.ViewPermission == true)
.Select(x => x.ApplicationModule);
but problem with this is, it doesn't issue a single query to database. It splits the query to get the webpages_Roles at SingleOrDefault then another query to get the Roles_X_ApplicationModules based on the RoleId and at the end as many queries as Roles_X_ApplicationModules matching the condition to get the ApplicationModule.
How do I write the LINQ query so that a single sql query is issued to database?
You can use Include() to do this.
Example:
card = Cards.Include(l => l.DocumentLinks)
.Include(l => l.Charges.Select(ch => ch.DocumentLinks)
.SingleOrDefault(c=>c.Id==id);
This is for three linked Entities:
public class Card
{
public Guid Id{get;set;}
public virtual ICollection<DocumentLink> DocumentLinks{get;set;}
public virtual ICollection<Charge> Charges{get;set;}
}
public class Charge
{
...
public virtual ICollection<DocumentLink> DocumentLinks{get;set;}
}
public class DocumentLink
{
...
}
try this:
var appModules = from u in user
from w in u.webpages_Roles
from am in w.Roles_X_ApplicationModules
where am.ViewPermission == true
select am;
if you want eager loading then you just need to call ToList:
var appModules = (from u in user
from w in u.webpages_Roles
from am in w.Roles_X_ApplicationModules
where am.ViewPermission == true
select am).ToList();