EF Insert Many Entities Based on User Selections - c#

We have an ASP .NET MVC application that allows users to provide a set of filter criteria that are logically ANDed together. The result of this is to graphically show the user how many items match those criteria. The user can then accept those criteria to create a set of entities in another table.
This is the intuitive and, IMO, sloppy approach:
// where "context" is an EF Context...
foreach (var person in allMatchingPeople) {
context.MailRequest.Add(new MailRequest {
Person = person
});
}
context.SaveChanges();
I don't like that it's iterative. If it were SQL I could just do something like:
INSERT INTO MailRequest (PersonId)
SELECT Id
FROM Person
WHERE ... -- filter by criteria provided from user input

In such case use stored procedure EF doesn't offer such functionality.

You can write it like this
allMatchingPeople.ForEach(p => context.MailRequest.Add(p));

Related

LINQ Grouping a List of Objects into Anonymous Type

I am having difficulty trying to use LINQ to query a sql database in such a way to group all objects (b) in one table associated with an object (a) in another table into an anonymous type with both (a) and a list of (b)s. Essentially, I have a database with a table of offers, and another table with histories of actions taken related to those offers. What I'd like to be able to do is group them in such a way that I have a list of an anonymous type that contains every offer, and a list of every action taken on that offer, so the signature would be:
List<'a>
where 'a is new { Offer offer, List<OfferHistories> offerHistories}
Here is what I tried initially, which obviously will not work
var query = (from offer in context.Offers
join offerHistory in context.OffersHistories on offer.TransactionId equals offerHistory.TransactionId
group offerHistory by offerHistory.TransactionId into offerHistories
select { offer, offerHistories.ToList() }).ToList();
Normally I wouldn't come to SE with this little information but I have tried many different ways and am at a loss for how to proceed.
Please try to avoid .ToList() calls, only do if really necessary. I have an important question: Do you really need all columns of OffersHistories? Because it is very expensive grouping a full object, try only grouping the necessary columns instead. If you really need all offerHistories for one offer then I'm suggesting to write a sub select (this is also cost more performance):
var query = (from offer in context.Offers
select new { offer, offerHistories = (from offerHistory in context.OffersHistories
where offerHistory.TransactionId == offer.TransactionId
select offerHistory) });
P.s.: it's a good idea to create indexes for foreign key columns, columns that are used in where and group by statements, those are going to make the query faster,

LINQ To Entities Multiselect

Please advise,, ALL the resources I've perused , books, videos tutorials none have what I would assume to be a simple, common scenario.
-----What is the Most efficient and standard way of querying through bridge tables with Link To Entities-----
I'm trying to to a query with Linq To Entities.I is my understanding that LinqToSQL is deprecated even if informally..
I have a standard
one to many (bridge table)
and the bridge table many to one for the final table
and lastly a specific key for the original table
If it helps there's a User table atop this mess with a one to many user to roles
Tables:
User, ( Not in the Query I have a specific KeyId for this table for the where clause),
Role,
&
RolePermission, ( Bridge / map whatever you want to call it, it has FK's for Role & Permission )
[unfortunately it has other cols or this wouldn't be so stressful. I.E. its not
abstracted in the entity framework its actually present],
& Permission.
Summary I want every permission for every role for this user, User ID lets say 5
I have a list of roles per user so (to start) I intended on feeding this query the role ID and calling it multiple times ,,, appending its results to a list 4Ea RoleId This is assuredly not ideal
Ideal would be to utilize the UserID & RoleID in a single query...
How do you do Multiple Inner Joins in Linq to Entities
This link above claimed that you can just pull his off by requesting all the tables involved without specifically joining them the PK-FK fields ?!?!?! Say What ??!
This is Just Peuedo code folks I've typed up several dozen attempts at this scenario
parameter p_RoleId --- potential version of this could get RoleId's per passed in UserID ideally
List<Permissions> listToReturn = new List<Permissions>();
var result=(from p in context.Permissions
from rp in m.RolePermissions
where m.roleID = p_RoleId
listToReturn result.ToList();
I really just want to know how to correctly hook these tables together and specify some where clause.
Two table examples are everywhere.. but there or four when you have a bridge table I found nothing
try this
var result = (from p in context.Permissions
join px in m.RolePermissions on p.roleID equals px.roleID
select p);
Well you can definitely do what you are writing in your pseudo code. After all what you do is an implied join. But it might not be very efficient.
You are essentially doing a
select <something> from Permissions , RolePermissions where permissions.id = permissionsRoles.id;
And you do not need a foreign key for that. However in your case you have many to many
//with the assumption that you have a
//navigation property between roles and rolepermissions in your model
var result = connection.Permissions
.Where(i => i.Id == RolePermissionsid)that
.SelectMany(i => i.RolePermissions).ToList();
Is this what you want?
var result=(from p in context.Permissions
join rp in m.RolePermissions on p.RoleId equals rp.RoleId
select p)

Entity Framework include vs where

My database structure is this: an OptiUser belongs to multiple UserGroups through the IdentityMap table, which is a matching table (many to many) with some additional properties attached to it. Each UserGroup has multiple OptiDashboards.
I have a GUID string which identifies a particular user (wlid in this code). I want to get an IEnumerable of all of the OptiDashboards for the user identified by wlid.
Which of these two Linq-to-Entities queries is the most efficient? Do they run the same way on the back-end?
Also, can I shorten option 2's Include statements to just .Include("IdentityMaps.UserGroup.OptiDashboards")?
using (OptiEntities db = new OptiEntities())
{
// option 1
IEnumerable<OptiDashboard> dashboards = db.OptiDashboards
.Where(d => d.UserGroups
.Any(u => u.IdentityMaps
.Any(i => i.OptiUser.WinLiveIDToken == wlid)));
// option 2
OptiUser user = db.OptiUsers
.Include("IdentityMaps")
.Include("IdentityMaps.UserGroup")
.Include("IdentityMaps.UserGroup.OptiDashboards")
.Where(r => r.WinLiveIDToken == wlid).FirstOrDefault();
// then I would get the dashboards through user.IdentityMaps.UserGroup.OptiDashboards
// (through foreach loops...)
}
You may be misunderstanding what the Include function actually does. Option 1 is purely a query syntax which has no effect on what is returned by the entity framework. Option 2, with the Include function instructs the entity framework to Eagerly Fetch the related rows from the database when returns the results of the query.
So option 1 will result in some joins, but the "select" part of the query will be restricted to the OptiDashboards table.
Option 2 will result in joins as well, but in this case it will be returning the results from all the included tables, which obviously is going to introduce more of a performance hit. But at the same time, the results will include all the related entities you need, avoiding the [possible] need for more round-trips to the database.
I think the Include will render as joins an you will the able to access the data from those tables in you user object (Eager Loading the properties).
The Any query will render as exists and not load the user object with info from the other tables.
For best performance if you don't need the additional info use the Any query
As has already been pointed out, the first option would almost certainly perform better, simply because it would be retrieving less information. Besides that, I wanted to point out that you could also write the query this way:
var dashboards =
from u in db.OptiUsers where u.WinLiveIDToken == wlid
from im in u.IdentityMaps
from d in im.UserGroup.OptiDashboards
select d;
I would expect the above to perform similarly to the first option, but you may (or may not) prefer the above form.

Linq To Sql ManyToMany Update Existing Records

I have a many to many relationship between 2 entities.
UserInfo and Campaign and the third table UserInfoCampaigns
On the front end a user should be able to update the user campaigns by using multiple checkboxes.
So I have:
var selectedCampaignsIds = new int[] {1,2,3}
var user = _extendedUserService.GetUser(new Guid(userId));
var newCampaigns =_campaignrepository.GetAllCampaignsByIds(selectedCampaignsIds);
I can assign newly selected campaigns by following:
foreach (var campaign in newCampaigns)
{
var userInfoCampaign = new UserInfoCampaign { UserInfoId = user.Id, CampaignId = campaign.Id };
_campaignrepository.SaveUserInfoCampaign(userInfoCampaign);
}
My question is how do I update UserInfo campaigns taking into consideration that a user might have or might not have existing campaigns?
Do I delete all existing UserInfoCampaigns and then reassign with new ones? Or there is a more optimal way of updating the UserInfo campaigns without deleting them first using LINQ to SQL?
Thanks a lot.
Generally, you'd delete them and recreate them. That's the way I handle this kind of situation of many to many.
If you went for (Delete all) solution then you may consider that the ids will increase in your table in none continues form (I'm assuming that your PK is an int), if you dont care about that then delete them then re-add
if you want the identity to be in a continues form you may set a flag (IsDeleted) in your campaigns table that refers to the campaign as deleted, you set it to ture when user delete a campaign and set it false when user re-select it from the UI this insure less more records but take a more bit work in your code
Generally, we just let SQL handle it. You can specify ON DELETE on the relationships in SQL, you can CASCADE or SET DEFAULT or SET NULL depending on what behaviour you want.
Here's an example for ON DELETE CASCADE:
http://www.codeproject.com/KB/cs/CascadingDeletes_cs.aspx

Linq Sort Nested Table

Follow up question to this:
Linq Combine Left Join Data
Say I have the following db tables:
Users
-------
UserId (PK)
UserName
Roles
-----
RoleId (PK)
RoleName
UserRoles
---------
UserId (PK)
RoleId (PK)
Users 1-M UserRoles M-1 Roles
Using LinqToSQL, I can return the following set (thanks to response from prior question):
[User1], [Role1, Role2, Role3]
[User2], [Role2, Role3]
[User3], [Role3]
The twist is I am trying to sort by Roles. How can I sort the result by Roles?
Clarification
I have a grid, when the user clicks on the Roles column, the rows would be sorted by that column.
So to start the grid would look like this:
[User1], [Role1, Role2, Role3]
[User2], [Role2, Role3]
[User3], [Role3]
Then if they sort on Roles column it would look like this
[User3], [Role3]
[User2], [Role2, Role3]
[User1], [Role1, Role2, Role3]
Just change the original answer very slightly:
from u in dataContext.Users
select new { User = u, Roles = u.UserRoles.Select(ur => ur.Role)
.OrderBy(r => r.RoleName) };
(That's assuming you want to sort each element of the result by the roles it contains. If that's not correct, please explain what you want in more detail.)
Could you not simpy use something like this?
// users is the collection you already got from Linq2Sql
var usersSorted = from u in users order by u.Roles select u;
int ascending = 1; //set to -1 for descending
from u in Users
orderby u.Roles.Count * ascending
select new { u, u.Roles.OrderBy(x => x.RoleName) }
Your query will need to cater for the many to many though (not shown).
Hey #zzz, the answers I've seen so far seem to indicate how to sort the rows for each user, whereas, if I understand your clarification, you do want that, but what you're asking for is how to then sort those statements alphabetically. I'll try to provide an answer to that question.
Though your request is very common, regrettably, SQL does not have a native way to convert a table (the Roles column) to a comma delimited string. This normally isn't a problem because you can simply return the Roles field as
{
UserName = u.UserName,
RolesList = string.Join(", ",
u.UserRoles.Select(ur => ur.Role.RoleName).ToArray())
}
This will work, surprisingly, even though I just mentioned that there is no equivalent function to string.Join in SQL. That's because LINQ is smart enough to just have SQL return the table and to apply the string.Join() at the last minute, in memory.
The problem comes when you then try to sort by the RoleList field as it is created in memory and not in SQL. If you do you'll get the following error message.
NotSupportedException: Method
'System.String Join(System.String,
System.String[])' has no supported
translation to SQL.
This leaves you with two choices:
Write a stored procedure to do this that utilizes a custom function to convert a table to a comma separated list.
OR bring the entire result set back into memory by returning it as .ToList() and then performing the sort ie (/my whole query/).ToList().OrderBy(q => q.Roles);
The second option will be much slower if you have a large dataset. If you user list is never going to grow very large or this query will only get called infrequently when an admin loads the user management screen, then the memory option may not be noticeably slower; however, if this query will be called frequently and/or the user table will get large, then this may not be a viable option.
I would suggest a third option. Reappraise the requirements. In this case, the user may really need a filtering feature where they can look at all users who are in a, b, c roles. If that is the true need, then sorting is not only much harder to implement, but it may also be a worse solution.
Good luck!

Categories

Resources