I have the following model:
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string Email { get; set; }
public IList<SubmissionNote> Notes { get;set; }
}
public class SubmissionNote
{
public virtual Int64 Id { get; set; }
public virtual string Text { get; set; }
public virtual DateTime CreatedOn { get; set; }
public virtual User Creator { get; set; }
public virtual Submission BelongsTo { get; set; }
}
public class Submission
{
public virtual Int64 Id { get; set; }
public virtual DateTime HappenedOn { get; set; }
public virtual int StatusCode { get; set; }
}
I would like to write a query that brings all the Submissions whose last note was entered by some user or a list of users (identified by their names). I would appreciate your help for building this query using NHibernate QueryOver API. I have been able to create a query to bring all the last notes as follows:
SubmissionNote alias = null;
var lastNote = session.QueryOver<SubmissionNote>()
.SelectList(list => list.
SelectGroup(x => x.BelongsTo).WithAlias(() => alias.BelongsTo).
SelectMax(x => x.CreatedOn).WithAlias(() => alias.CreatedOn).
Select(x => x.Creator).WithAlias(() => alias.Creator).
Select(x => x.Id).WithAlias(() => alias.Id).
Select(x => x.Text).WithAlias(() => alias.Text))
.TransformUsing(Transformers.AliasToBean<SubmissionNote>())
.List();
Can I use the specified query as a subquery to produce the required result? Or do you suggest another solution?
I would try something like this with a subquery (not tested though)
SubmissionNote subNoteAlias = null, subNoteAliasMax =null;
User userAlias = null;
String[] userNames = new[]{"John","Joe"};
var subquery =
QueryOver.Of(() => subNoteAliasMax)
.Where(subNote => subNote.BelongsTo.Id == subNoteAlias.BelongsTo.Id)
.Select(Projections.Max<SubmissionNote>(subNote => subNote.CreatedOn));
var result = session
.QueryOver<SubmissionNote>(() => subNoteAlias)
.WithSubquery.WhereProperty(subNote=>subNote.CreatedOn).Eq(subquery)
.JoinQueryOver(
subNote=>subNote.Creator
,()=>userAlias
,JoinType.InnerJoin
,Restrictions.In(Projections.Property(() => userAlias.Username ), userNames))
.List();
The generated SQL query would (very) roughly look like :
SELECT SubmissionNote.*
FROM SubmissionNote INNER JOIN User
ON SubmissionNote.creatorId = User.userId
AND User.userName in ('john','joe')
WHERE SubmissionNote.CreatedOn = (SELECT MAX(CreatedOn) FROM SubmissionNote snAlias
WHERE snAlias.SubmissionId = SubmissionNote.SubmissionId)
Hope this will help. There might be issues with the subquery returning a date instead of an id. LMK and I will give it a look.
Related
I am trying to extract list of Categories with the corresponding Tickets for a specific userId using Linq Lambda expression.
Category:
public class Category
{
public int Id { get; set; }
public string CategoryName { get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
Ticket:
public class Ticket
{
public int Id { get; set; }
public string Title { get; set; }
public User User { get; set; }
public Category Category { get; set; }
}
User:
public class User : IdentityUser
{
public ICollection<Ticket> Tickets { get; set; }
}
This is what I tried so far, but it is only returns Category if it has any ticket.
I want all Categories with Tickets (for a specific userId)
var query = from cate in _context.Categories.Include(c => c.Tickets)
join tickets in _context.Tickets
on cate.Id equals tickets.Category.Id
where tickets.User.Id.Equals(id)
select (cate);
DatabaseContext.Categories
.Include(c => c.Tickets)
.Where(c => c.Tickets.Any(t => t.User.Id.Equals(id)));
This may help you and I recommend you to learn more about linq operators first.
I am trying to get each user with its projects using entity framework core in a web api project in the controller with linq
I tried with this query but it gave me an empty object
var users = _context.Users.Include(x => x.userProjects.Select(up => up.UserId == x.Id)).ToListAsync();
I also tried this one and got the same result
var users = _context.Users.Include(x => x.userProjects.Where(up => up.UserId == x.Id)).ToListAsync();
This is the User class
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserProject> userProjects { get; set; }
}
This is the Project class
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
public ICollection<UserProject> UserProjects { get; set; }
}
and this is the UserProject class
public class UserProject
{
[ForeignKey("User")]
public int UserId { get; set; }
public User User { get; set; }
[ForeignKey("Project")]
public int ProjectId { get; set; }
public Project Project { get; set; }
}
I want to get a json with each user and an array of its projects
For multiple level of includes, you need ThenInclude.
var users = _context.Users
.Include(x => x.userProjects)
.ThenInclude(y => y.Project)
.ToListAsync();
var users = _context.Users.Include(u => u.userProjects)
.ThenInclude(up => up.Projects))
.Where(u => u.UserId == Id)
.ToListAsync();
What happens here:
You will retrieve all users which have UserId = yourId and also all the UserProjects and Projects of those users.
Example of code which shows you you can access all the projects of the first returned user:
var projectsForFirstUser = users.First().Select(x => x.UserProjects).Select(x => x.Project).ToList();
EDIT: Modified to ThenInclude because of EF Core.
I'm using EF6 and I created this two models:
public class Publication
{
public int Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
public class ViewLog
{
public int Id { get; set; }
public int? UserId { get; set; }
[ForeignKey("UserId")]
public User.User User { get; set; }
public int? SessionId { get; set; }
[ForeignKey("SessionId")]
public User.Session Session { get; set; }
public int PublicationId { get; set; }
[ForeignKey("PublicationId")]
public Publication.Publication Publication { get; set; }
public DateTime Created { get; set; }
}
Every time I visit a Publication I create a new record into ViewLog table.
Now, using Linq, I need to get all Publications ordered by the number of ViewLogs (visits) per publication in the last 24hs. (If Publication has no ViewLogs they need to appear too, but obviously after the publications that has viewlogs)
You can use GroupJoin when you don't have navigation properties and need a left outer join
The lambda syntax goes like this:
var publicationQuery = new List<Publication>().AsQueryable();
var viewLogQuery = new List<ViewLog>().AsQueryable();
var leftJoinQuery = publicationQuery
.GroupJoin(viewLogQuery, x => x.Id, x => x.PublicationId, (pub, logs) => new
{
PublicationId = pub.Id,
LogCount = logs.Count()
})
.OrderByDescending(x => x.LogCount);
I also found this Query Expression Translation Cheat Sheet very useful to go from query expressions (which are closer to SQL) to lambda method syntax (which I prefer)
If you do have a navigation property Publication.ViewLogs (which is a List<ViewLog>), then you can just use Select() with a projection
var publicationQuery = new List<Publication>().AsQueryable(); // From a DbSet...
var query = publicationQuery
.Select(x => new
{
PublicationId = x.Id,
LogCount = x.ViewLogs.Count
})
.OrderByDescending(x => x.LogCount);
In continuation of yesterday's post
Two Entities
public class Realtor
{
public Realtor()
{
Guid = Guid.NewGuid();
Registration = DateTime.Now;
}
public int Id { get; set; }
public Guid Guid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Registration { get; set; }
public int SubdivId { get; set; }
public Subdiv Subdiv { get; set; }
}
public class Subdiv
{
public Subdiv()
{
Created = DateTime.Now;
}
public int Id { get; set; }
public string Name { get; set; }
public DateTime Created { get; set; }
public List<Realtor> Realtors { get; set; }
}
I spend test
I added one Subdiv (TOSTER TM) and received his ID
Next, I add a Realtor and I push Subdiv property found on the ID, the newly created TOSTER TM
Realtor.Subdiv is an object of type Subdiv. OK.
Then I try to select from the base the newly added Realtor.
Realtor.Subdiv = null OMG!!
We get Subdiv object, which is lacking in Realtor above and see his List<Realtor> = null
Please help in solving this problem.
Try this:
Relator rl = Context.Relators.Include(r => r.Subdiv).First(s => s.Id == id);
Now you can access to Subdiv property
For more related date you can call Include Methods more times:
Relator rl = Context.Relators
.Include(r => r.Subdiv)
.Include(r => r.AnotherRel)
.First(s => s.Id == id);
For Entities with multiple levels in depth:
If Subdir is a collection
Relator rl = Context.Relators
.Include(r => r.Subdiv)
.ThenInclude(sub => sub.SecondLevelDepth)
.First(s => s.Id == id);
if Subdir is an Entity
Relator rl = Context.Relators
.Include(r => r.Subdiv.Select(s => s.SecondLevelDepth)
.First(s => s.Id == id);
The problem is not with saving the related data (it should be saved correctly, you could check that inside the database), but loading it.
EF Core currently does not support lazy loading, so in order to get the related data you need to explicitly request it (the so called eager loading):
Realtor rl = context.Realtors.Include(r => r.Subdiv).First(r => r.Id == id);
For more info, see EF Core: Loading Related Data.
Entity Framework core allows to save related entities, you need to define Fluent API settings for both Master and detail table
public class Order
{
public int Id { get; set; }
public int AddressId { get; set; }
public string DeliveryNotes { get; set; }
public int PurchaseOrderNo { get; set; }
public virtual ICollection<OrderItem> Items { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public string ProductName { get; set; }
public int Quantity { get; set; }
public decimal UserPrice { get; set; }
public string Comment { get; set; }
[ForeignKey("OrderId ")]
public int OrderId { get; set; }
public virtual Order Order { get; set; }
}
Inside the DBConext OnModelCreating method , define the relation like, it will make sure when you have order object, its related or child objects i.e Enrolments will also be saved
modelBuilder.Entity<Order>()
.HasMany(c => c.Items)
.WithOne(e => e.Order);
modelBuilder.Entity<OrderItem>().Ignore(x => x.Order);
Now your code should look like this
Order _order = new Order{ AddressId = 1, DeliveryNotes ="some notes", PurchaseOrderNo =1};
_order.Items = new List< OrderItem>();
_ordert.Items.add(new OrderItem{ ProductName =”Laptop”, Quantity =1, UserPrice =1500.00, Comment =”some testing comments”});
repository.order.insert(_order);
repository.save();
I am using Entity Framework 5 and I have these classes. What I want to do is to be able to get the data to populate the view listed below:
public partial class Subject
{
public int SubjectId { get; set; }
public string Name { get; set; }
public virtual ICollection<Topic> Topics { get; set; }
}
public partial class Topic
{
public int TopicId { get; set; }
public string Name { get; set; }
public int SubjectId { get; set; }
public virtual Subject Subject { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic
{
public int SubTopicId { get; set; }
public string Name { get; set; }
public int TopicId { get; set; }
public virtual Topic Topic { get; set; }
}
Now I am trying to write a LINQ query to populate this class:
public class TopicSubTopicSelect
{
public int TopicId { get; set; }
public int SubTopicId { get; set; }
public string TopicName { get; set; }
public string SubTopicName { get; set; }
}
So far I have this:
return _subjectsRepository
.GetAll()
.Where(s => s.SubjectId == subjectId)
.Include(s => s.Topics.SelectMany(t => t.SubTopics))
.AsEnumerable()
.Select(item => new TopicSubTopicSelect(item.TopicId <<<
item.SubTopicId <<
item.Topic.Name <<
item.Name <<))
.ToList();
Can someone tell me how I can get data from the fields I marked with <<. I tried to do .item.Topic.TopicId etc but that does not seem to work.
You shouldn't start from Subject. You just start from SubTopic Repository, and you won't even need to use .Include. Do it like this:
_subTopicRepository
.GetAll()
.Where(s => s.Topic.SubjectId == subjectId)
.Select(s => new TopicSubTopicSelect()
{
TopicId = s.TopidId,
SubTopicId = s.SubTopicId,
TopicName = s.Topic.Name,
SubTopicName = s.Name
})
.ToList();
As I mentioned in my comment on ataravati's answer, you shouldn't actually have a SubTopicRepository so you are correct in starting at SubjectsRepository however you are querying by the Subject ID so you shouldn't be going via GetAll(), you should have a Get(int id) method. The include should be handled as an implementation detail inside Get as the children (SubTopics) are part of the Subject. That makes the method call look like this instead:
return _subjectsRepository
.Get(subjectId)
.SelectMany(subject => subject.SubTopics))
.Select(subTopic => new TopicSubTopicSelect
{
TopicId = subTopic.TopicId,
SubTopicId = subTopic.SubTopicId,
TopicName = subTopic.Topic.Name,
SubTopicName = subTopic.Name
}).ToList();