Entity Framework model.edmx and many to many relationships - linq

In the database I have 3 tables, User, Group and a lookup table UserGroup which contains UserID and GroupId.
I create a model.edmx and this allows me to:
var users = context.User
var groups = context.Group
but does not create an object for context.UserGroup.
what is the syntax for getting all the users in a group, and all the groups that a user belongs to?
var results = from groups in db.Groups.Where(t => t.Users(u => u.UserID ==1))
select groups;

If you have the proper keys setup, EF will hide away your junction table, and create a navigation property to make the connection between the tables
For getting all the groups a user belongs to:
var groupResults = db.Groups.Where(g => g.Users.Select(u => g.UserId).Contains(userId));
and for getting all the Users:
var userResults = db.Users.Where(u => u.Groups.Select(g => g.GroupId).Contains(groupId));

The junction table will not be created. You'll get two navigation collection properties. Groups in User entity and Users in Group entity.
To get all users from a group:
var usersInGroup = context.Groups.Where(g => g.ID == groupId).SelectMany(g => g.Users);
To get all groups the user belongs to:
var groupsOfUser = context.Users.Where(u => u.ID == userId).SelectMany(u => u.Groups);

Related

Entity Framework & Linq Orderby

We have a 3 table relationship in an MVC application that is using the EF6. For example a Customer Table related to an Orders Table(the many side) and the Orders Table is related to an OrderItems Table (the many side).
We would like in ONE TRIP to the database get all the records for all tables and be able to order each table.
We know we can use the include extension like context.Customers.Include("Orders.OrderItems") to eagerly load all data. But we keep getting errors when we try to order each table.
For example we would like to order the Customer Table by CustomerId column, and the Orders Table by Date column and the OrderItems by ProductId column.
Any assistance would be apreciated
If you have navigation properties from OrderItem to Order and from Order to Customer, then you can get ordered `OrderItems' you can achieve this like:
var orderItems = context.OrderItems
// Filter if needed with .When
.Include(m => m.Order.Customer)
.OrderBy(m => m.Order.Customer.CustomerId)
.ThenBy(m => m.Order.Date)
.ThenBy(m => m.ProducetId)
.ToList();
But, if you want to get customers, what you can do is load ordered customers like:
var customers = context.Custoemr
.Include(m => m.Orders.Select(o => o.OrderItem))
.OrderBy(m => m.CustomerId)
.ToList();
Then order Order and OrderItem. As you have loaded all data, ordering collections will not make any database call, and it will remain one-trip to database:
foreach(Customer customer in customers)
{
customer.Orders = customer.Orders.OrderBy(m => m.Date);
foreach (Order order in customer.Orders)
{
order.OrderItems = order.OrderItems.OrderBy(m => m.ProducetId);
}
}

Linq where IN using method syntax

I have been struggling trying to get this one to work and my brain is shot...hoping someone can help out.
I have a table called Company which is the main entity returned from the query. The company table is linked to a table called Category through the table CompanyCategory. The category table has 2 fields that are relevant here: Name and Meaning. These fields contain the same data on a row...ie...Name = "Marketing" and Meaning = "MARKETING". This allows me to query by Meaning, while the display name could potentially change in the future. I will always query the category table by Meaning, and then display the Name.
Since a company can contain 1 to many categories, there are times when I want to return all companies that have a set of categories....say...all companies that are tied to marketing or public relations. Here is the query that I have right now that returns all companies with only the data I want.
IQueryable<ICompanyModel> q = base.Context.Set<KD.CompanyAdventures.Data.Implementation.Company>()
.Include(x => x.Address.City.FirstAdminDivision)
.Include(x => x.CompanyBioInfoes)
.Select(x => new CompanyModel
{
CompanyId = x.CompanyId,
Name = x.Name,
BusinessPhone = x.BusinessPhone,
PrimaryEmail = x.PrimaryEmail,
WebsiteUrl = x.WebsiteUrl,
LogoUrl = x.LogoUrl,
Address = new AddressModel
{
AddressId = x.AddressId,
Address1 = x.Address.Address1,
Address2 = x.Address.Address2,
PostalCode = x.Address.PostalCode,
City = new CityModel
{
CityId = x.Address.City.CityId,
Name = x.Address.City.Name,
FirstAdminDivision = new FirstAdminDivisionModel
{
FirstAdminDivisionId = x.Address.City.FirstAdminDivision.FirstAdminDivisionId,
Name = x.Address.City.FirstAdminDivision.Name
}
}
}
});
I am passing a List<string> to the method (GetCompanies) that has this LINQ query and I need to filter the companies that are returned by that list of category meanings that are being passed in. I have been able to get this to work in a test with 2 simple lists using the following (where list 1 = all employees (my wife and kids names) and list 2 is just the names of the my kids):
IQueryable<string> adultEmployees = employees.Where(emp => !kids.Contains(emp.ToString())).AsQueryable();
But I am not able to get this to work with the company and category example as I can't figure out how to drill down to the meaning with complex objects....ie...not list of strings.
Object classes used by the LINQ query look like the following:
CompanyModel [ CompanyId, CompanyName, List<CategoryModel> ]
CategoryModel [ CategoryId, Name, Meaning ]
Any help is greatly appreciated.
Assume that meanings is a list containing "marketing" and "public relations". If I understand this correctly, you can get companies having CategoryModels's Meaning equals "marketing" or "public relations" like so :
companies.Where(c => c.CategoryModels.Any(cm => meanings.Contains(cm.Meaning)))

Entity Framework Many-To-Many Join Query

I've got a standard social networking paradigm where a User has a collection of friends who are also users.
I'm using Entity Framwork Code First, and my Friend Relationship is defined as follows:
modelBuilder.Entity<User>()
.HasMany(u => u.Friends)
.WithMany()
.Map(m =>
{
m.ToTable("Friendships");
m.MapLeftKey("UserId");
m.MapRightKey("FriendId");
});
What I want to do is to search my users table returning all users with an indicator of whether each returned user is friends with the current user. To be clear, I want to return Users who are Friends and Users who are not friends, but also with a boolean indicating whether each user is a friend. I know how to do this in TSQL its a basic left outer join.
I've seen examples of how to do a left join in LINQ, but all the examples I've seen are joining to a mapped type. My Friendships column doesn't have a Mapped type.
How do I do this in EntityFramework?
var list = context.Users
.Where(u => u.Age >= 20) // sample condition, if you want ALL remove the line
.Select(u => new
{
User = u,
FriendOfCurrentUser = u.Friends.Any(f => f.UserId == currentUserId)
})
.ToList();
Result is a list of anonymous objects containing the user and the boolean friendship indicator. You can also create a helper class UserWithFriendship and project into it (Select(u => new UserWithFriendship { ... }) instead of an anonymous type.

Can I filter the Users returned by GetAllUsers based on a role they are in

I am trying to create an administration interface where users and roles (among other things) can be administered. I have a list of users who can be edited, deleted or viewed. I have this code in the action:
var model = Membership.GetAllUsers()
.Cast<MembershipUser>()
.Select(x => new UserModel
{
UserName = x.UserName,
Email = x.Email,
UserRoles = Roles.GetRolesForUser(x.UserName)
});
return View(model);
This is all fine except that I don't want admins to be able to edit each other. So I need to filter out all users in the "super admin" role. I can certainly figure this out by stepping through each role for each user to see if they are a member. I am wondering if there is a nice sucinct way to do this by filtering the result set in the Select statement, or using Except or Where
I would normally think about the sql I want generated then try write the linq, filtering by roles should be fairly easy with a simple where statement.
However it appears you're trying to abstract each part of the query into smaller bits, this may make it easier to write but can have a devastating effect on performance. For example, I wouldn't be suprised if the GetRolesForUser method you are calling causing an extra database query per user that is returned by GetAllUsers, using the Include method is a much nicer way to get all roles at the same time.
var model = context.Users
.Include(user => user.UserRoles)
.Where(user => !user.UserRoles.Any(role => role == superAdmin)
.Select(user => new UserModel() { UserName = user.UserName, Email = user.Email, UserRoles = user.UserRoles});

LINQ - select query in related objects Entity Framework (EF4)

For the following (I'm using EF4) I need to select all messages in a Thread (ContactThreadId) that were NOT yet read given LoginId
So, i based on ContactThreadId and LoginId I need to know if this LoginId has already read all messages in a Thread. If not I need to update ContactReadState with all messages from the thread with his/her LoginId and when she/he read it.
I've tried these but am stuck:
// Update read state
var thread = this.contactMessageThreadRepository.GetRow(id);
var loginEntity = this.loginRepository.GetRow(ProfileContext.LoginId);
var unreadMsg = loginEntity.Contact
.Where(x => x.ContactThread.Any(y => y.ContactThreadId == id))
.Select(b => b.ContactMessage.FirstOrDefault())
.Where(q => q.ContactReadState.Count() == 0);
var unreadMsg = loginEntity.Contact
.Where(x => x.ContactThread.Any(y => y.ContactThreadId == id))
.Where(y => y.ContactReadState.Any(q => q.ContactId != loginEntity.ContactId));
var msg = thread.Contact
.Where(x => x.LoginId == loginEntity.LoginId)
.Where(y => y.ContactReadState.Count() == 0);
please help.
thanks
EDIT:
Login - this table hols logins/admin records
Contact - this table holds all contacts (including Login items because Logins can also participate in a conversation-can reply to messages etc.) When a LoginId in a contact table is NOT NULL, it means it's a Login user otherwise it's a public user submitting a message.
QUESTION -> Each Login can have only 1 Contact record (I have a trigger that creates a Contact record upon Login creation). How do I make it so that it's 1 to 1.??
I've added a FK from Contact to Login table but for some reason EF created 0..1 -> * association. And it should be 1 to 1. For a given Login there can be only 1 Contact with that LoginId.
Whenever Login is viewing a list of messages a new record is inserted to ContactReadState for that Login (marking a message read by that Login (with ContactId for that Login).
I have a fragment, but can't put it completely together. Especially I don't know how you would set the ContactId in the new ContactReadState objects (see ??? below) because apparently a given loginId can have many contacts. So, which one to set in the new read states? Probably I misunderstood something in your model, because all those relationships look very complicated to me.
...based on contactThreadId and loginId...
I'm using a context directly, you need to translate this into your repository structure:
using (var context = new MyContext())
{
var unreadMsgs = context.ContactMessages
.Where(cm => cm.ContactThreadId == contactThreadId
&& !cm.ContactReadState
.Any(crs => crs.Contact.LoginId == loginId))
.ToList();
// These should be the unread messages you want to select.
// Now, updating ContactReadState:
foreach(var unreadMsg in unreadMsgs)
{
var newContactReadState = new ContactReadState
{
ContactMessageId = unreadMsg.ContactMessageId,
ContactId = ???,
ReadDate = DateTime.Now
};
context.ContactReadStates.AddObject(newContactReadState);
}
context.SaveChanges();
}
Edit
If I understand correctly there can be contacts without login but if there is a login it is uniquely assigned to a contact.
Creating such a one-to-one relationship in EF correctly requires the following:
You must remove the LoginId column and the corresponding relationship from your Contact table (it's the reason for the one-to-many relationship EF creates).
The LoginId primary key column in the Login table must not be an autogenerated identity.
You must create a foreign key relationship between Contact and Login table where the foreign key column in Login is the primary key column LoginId at the same time. So, the relationship is between Login.LoginId (the "dependent" with the FK) and Contact.ContactId (the "principal" with the PK).
This would mean that a Login with LoginId=x refers to a Contact with ContactId=x (same value) which finally makes it easy to fill the ??? in the code snippet above: ??? is simply = loginId.
Edit 2
...and of course you can replace then in the query ...
.Any(crs => crs.Contact.LoginId == loginId)
...by...
.Any(crs => crs.ContactId == loginId)
(which avoids an unnessacary join to the Contact table)

Resources