Entity Framework : better way to do many to many query? - asp.net-mvc

Environment: EF6 code first
I have two tables e.g user and activity which have a many-to-many relation.
I want to know whether any user's activity has a given key, using Entity Framework in an asp.net mvc application.
is the following query performance wise?
is there any other way to write this query?
Query:
var userEntity = _dbcontext.Users.Find(userId);
var isAvailable = _dbcontext.Entry(userEntity)
.Collection(e => e.Activities)
.Query().Any(e => e.Name== "givenname");
updated:
i ran those queries and here are the time to run:
1
var userEntity = _dbcontext.Users.Find(userId);
var isAvailable = userEntity.Activities.Any(a => a.Name == "givenname");
time: 5.39s + time to fetch user
2
var isAvailable =_dbcontext.Users.Include(e => e.Activities)
.Any( u => u.UserId == userId
&& u.Activities.Any(a => a.Name == "givenname")
);
time: 5.73s
3
var userEntity = _dbcontext.Users.Find(userId);
var isAvailable = _dbcontext.Entry(userEntity)
.Collection(e => e.Activities)
.Query().Any(e => e.Name== "givenname");
time: 5.95s + time to fetch user
so it seems that choosing the second form of query be reasonable.

You can also try this:
var isAvailable =_dbcontext.Users.Include(e => e.Activities)
.Any( u => u.UserId == userId
&& u.Activities.Any(a => a.Name == "givenname")
);
If you haven't disabled lazy loading, then you don't need to call the Include extension method.

I would simplify this to:
var userEntity = _dbcontext.Users.Find(userId);
var isAvailable = userEntity.Activities.Any(a => a.Name == "givenname");
or maybe (I'm not quite clear what you're trying to achieve, from your question) - this doesn't take the userId into account, it just searches for any users that has any activity with that givenname:
var isAvailable = _dbcontext.Users.Any(u => u.Activities.Any(a => a.Name == "givenname"));

Single trip to the DB:
var result = _dbcontext.Users.Where( u => u.UserId == userId )
.Select( u => new {
User = u, // optional
IsAvailable = u.Activities.Any( a => a.Name == "givenname" )
} )
.SingleOrDefault();

Related

Entity Framework query exception when looking for an entity contained in a collection

I'm running the query below to obtain all the events (as a registered student) I'm attending on a specific day and getting an error that says
System.NotSupportedException: Unable to create a constant value of type 'YogaBandy.Models.Profile.YogaProfile'. Only primitive types or enumeration types are supported in this context.
Here is the query I'm using to get all events I'm regsitered for on a specific day.
// my profile
var yogaProfile = dbContext.YogaProfiles.Where(i => i.ApplicationUserId == userId).First();
// events I'm registered for on a specific day
var eventsNew = dbContext.YogaSpaceEvents.Where(
i => i.EventDateTime.Day == date.Day
&& i.EventStatus == YogaSpaceEventStatus.Active
&& i.RegisteredStudentsNew.Contains(yogaProfile)).ToList();
I think it might have something to do with part, but not sure
&& i.RegisteredStudentsNew.Contains(yogaProfile)
FYI - my RegisteredStudentsNew looks like this in the 'YogaSpaceEvents' entity
public virtual ICollection<YogaProfile> RegisteredStudentsNew { get; set; }
and when I add a newly regsitered student I add him/her like this
spaceEvent.RegisteredStudentsNew.Add(yogaProfile);
dbContext.SaveChanges();
Try to move your YogaProfiles.Where(i => i.ApplicationUserId == userId) inside Include statement.
Example:
var eventsNew = dbContext.YogaSpaceEvents
.Include(p=>p.RegisteredStudentsNew.Where(rp => rp.ApplicationUserId == userId))
.Where( i => i.EventDateTime.Day == date.Day
&& i.EventStatus == YogaSpaceEventStatus.Active)
.ToList();
OR
use Any in your where clause
var eventsNew = dbContext.YogaSpaceEvents
.Include(p=>p.RegisteredStudentsNew)
.Where(i => i.EventDateTime.Day == date.Day
&& i.EventStatus == YogaSpaceEventStatus.Active
&& i.RegisteredStudentsNew.Any(rp => rp.ApplicationUserId == userId))
.ToList();
Please read this for why use Include in LINQ.

MVC Identity 2.1 - get a list of users NOT in a particular role

How do I get a list of users that are not in 1 particular role? I am using mvc 5, ef 6, identity 2.1.
To get a list of users in the "Host" role I can do:
var getRole = (from r in db.Roles where r.Name.Contains("Host") select r).FirstOrDefault();
var getHostUsers = db.Users.Where(x => x.Roles.Select(y => y.RoleId).Contains(getRole.Id)).ToList();
To get a list of all users I can do:
var getUsers = await db.Users.Where(u => u.Id != currentUser).OrderBy(u => u.FirstName).ToListAsync();
But what I need is a list of all users that are not in the "Host" role (getUsers - getHostUsers).
If you want all users, except those with the Host role you can just do the opposite of what you are doing and add an exclamation mark in the where clause.
var getRole = (from r in db.Roles where r.Name.Contains("Host") select r).FirstOrDefault();
var getNotHostUsers = db.Users.Where(x => !x.Roles.Select(y => y.RoleId).Contains(getRole.Id)).ToList();

Entity Framework n-to-n query, get last records from navigation table

I am using entityframework in my project.
I have 3 tables which are navigated with many to many relationship.
This is my diagram.
I want to select all my counters id which have last approve status == 15.
I wrote query like this;
var sayacOnayDurumlari =
db.CounterApproveStatus
.Where(x => x.ApproveStatusId == 15).OrderByDescending(x=>x.Id)
.GroupBy(x => x.CountersId)
.Select(e => e.FirstOrDefault());
but it takes my older records which are ID == 15
var son =
db.Counters.Where(
x => x.CounterApproveStatus.OrderByDescending(t => t.Id).FirstOrDefault().ApproveStatusId == 15)
.ToList();
I tried this and I supposed I achieved it. Is it a good query?
You need group first, then find if the latest Id in that group has desired statusId.
Following syntax may not be exactly right, but you could get idea.
var sayacOnayDurumlari =
db.CounterApproveStatus
.GroupBy(x => x.CountersId)
.Select(g => new {
CountersId = g.Key,
LatestRecord = g.OrderByDescending(x=> x.Id)
.FirstOrDefault()
})
.Where(g=> g.LatestRecord.ApproveStatusId == 15)
.Select(g => g.CountersId).ToList();

Order results by two differents fields in entity framework

I have advertisement table, i want to order it first by special, then order by last added, but special ads has end date, so i want to order special ads that its date is still
This is my code
List<S_Advertisements> moduleItems = db.S_Advertisements
.Include(x => x.S_AdvertisementsImages)
.Where(x => x.CatId == pureId && x.IsActive == true)
.OrderByDescending(x => x.IsSpecial)
.ThenByDescending(x => x.AdId).ToList();
I want to know if Entity Framework will let me do something like that :
List<S_Advertisements> moduleItems = db.S_Advertisements
.Include(x => x.S_AdvertisementsImages)
.Where(x => x.CatId == pureId && x.IsActive == true)
.OrderByDescending(x => x.IsSpecial.Where(x => x.DateSpecialEnd > DateTime.Now))
.ThenByDescending(x => x.AdId).ToList();
may be :
DateTime dtn = Datetime.Now;
var moduleItems = db.S_Advertisements
.Include(x => x.S_AdvertisementsImages)
.Where(x => x.CatId == pureId && x.IsActive == true)
.Select( x => new {
o = x,
c = x.DateSpecialEnd > dtn ? 0 : 1 // not a boolean but an order info
})
.OrderByDescending(z => z.c)
.ThenByDescending(z => z.o.AdId).ToList();
same as in sql: view for calculating the field, then order the view.
The placement of the where clause doesn't affect ordering. Whether you order by IsSpecial or by IsSpecial.Where(...) the order would be the same, regardless. That's pretty much why what you're looking for doesn't exist: there's no need for it to.

linq: how to get sorted records from last ID

I have the following table,
ItemTable,
Col1 : ItemID(int)
Col2 : MRP(Decimal)
To one of the application I needed to pass selected number of items at a time, They will send me the lastId which I passed to them, the initial requirement was to pass the newest items, which I was able to get it using following query,
var itemList = itemRepository.AsQueryable()
.Where(r => r.ProductID == productID && r.IsActive == true && r.ItemID< lastId)
.OrderByDescending(r => r.ItemID)
.Take(numberOfItems)
.ToList();
However now there is a sort option added to it, which is the MRP column, though again i have only the last Id with me, how could I will able to get this? I tried with the following query, no luck.
var itemList = itemRepository.AsQueryable()
.Where(r => r.ProductID == productID && r.IsActive == true && r.ItemID< lastId)
.OrderByDescending(r => r.ItemID)
.OrderBy(r => r.MRP)
.Take(numberOfItems)
.ToList();
UPDATE : Working Code
As per CamperWill's suggesstion I updated my code and works great with skip.
var itemList = itemRepository.AsQueryable()
.Where(r => r.ProductID == productID && r.IsActive == true)
.OrderBy(r => r.MRP)
.Skip(pageNumber * numberOfItems)
.Take(numberOfItems)
.ToList();
LastID will not help you with paging if you are sorting by a different field (MRP). Also, as indicated in the comments above, the first ordering is effectively ignored by adding the second.
You could consider tracking the page number that is requested and use the Skip() extension.
// input parameter 'page'
var itemList = itemRepository.AsQueryable()
.Where(r => r.ProductID == productID && r.IsActive == true)
.OrderBy(r => r.MRP)
.Skip( (page-1)*numberOfItems )
.Take(numberOfItems)
.ToList();

Resources