Linq To Sql ManyToMany Update Existing Records - c#

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

Related

Updating a record using Linq-to-SQL

I do my query...
var result = from u in tdc.tblUsers
where u.UserID == userID
select u;
and then I change the values I want to:
foreach (tblUsers u in result)
{
//change values (and no im not changing the primary key or foreign keys)
}
then I submit changes
tdc.SubmitChanges();
When it hits submit changes, it throws exception that the row wasn't found or was changed. I am the only person using this so there's no other conflicts with accessing the db or locking. Why would it throw the ChangeConflictException? I have stepped through with debugger and the data persists all the way through the process, including the changes that I'm trying to make.
I have also tried it this way before and got the same error
tblUsers result = (from u in tdc.tblUsers
where u.UserID == userID
select u).Single();
result.Address = address;
result.Phone = phone;
tdc.SubmitChanges();
It will only ever retrieve 1 record with this query since UserID is the primary key.
I've done this many times and it has worked. Every example I've found is exactly what I have.
Maybe you work with different context? Try to encapsulate it by using
using (myContext ctx = new myContext())
{
var user = ctx.users.first();
user.name="blah";
ctx.SubmitChanges();
}
Quite often, if you look at the actual SQL that Linq-to-SQL generates, you'll see that it is being really over-zealous with matching the database row that you initially retrieved. Imagine that you have a table with columns ID(PK), A, B, C. You would think that if you updated column C for a row, it should be sufficient to update the row with a matching primary key. But what often happens is that Linq-to-SQL is also trying to match on columns A and B as well. Normally, that's fine. Unless you have concurrent writes, either from multi-threading or multi-processes, and something else modifies column A or B for the record that you are trying to update. Then you get these System.Data.Linq.ChangeConflictException: Row not found or changed. errors when you call SubmitChanges() on your data context.

Query Context for Inserted but yet Uncommitted records

I've searched and searched stackoverflow and www, but have found no answers to this question.
I am looping through a number of records and under certain conditions I'm inserting new records into table A. Then I'm looping again on another data source (which cannot be merged with the first one), and if that be the case, I want to insert new records into the same table A. I only want to commit the records at the end of the process, but it'll give a primary key violation error if I just insert them.
Note: linq is not managing primary keys. Probably because I'm sort of a noob with linq and don't really know how to get linq to work with Oracle sequences.
My question is how do I check the existing context for the records I have inserted. This is what I am doing.
foreach(var rec in recordList1)
{
...
dataContext.InsertOnSubmit(obj);
}
foreach(var rec in recordList2)
{
if ( ! [check context here for existing record] )
{
...
dataContext.InsertOnSubmit(obj);
}
}
dataContext.SubmitChanges();
I've tried querying the context in different ways, but it'll only return committed values.
Thanks in advance!
Best regards.
To access the objects inserted, updated, deleted in the datacontext you need to call GetChangeSet.
var changed = dataContext.GetChangeSet();
var inserted = changed.Inserts;
var updated = changed.Updates;
var deleted = changed.Deletes;

EF Insert Many Entities Based on User Selections

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));

Determine if foreign key table has any links to primary key table with LINQ & DataTable

I have 2 datatables.
One is the master the other is the detail
When someone goes to delete a master record how can I check that there are no rows in my detail datatable that relate to the master table's ID.
UPDATE: The user has the ability to select more than one master record at a time
I want to use LINQ if possible.
I started looking at DataTable.Rows.Cast()......
You don't need LINQ for this.
Instead, you can check masterRow.GetChildRows("RelationName").Length.
EDIT: You should use a DataRelation.
If you really don't want to, you can check
childTable.AsEnumerable().Any(dr => dr["ParentIdColumn"] == someValue)
EDIT: To check for multiple parents:
var parentKeys = parentRows.Select(dr => dr["id"]).ToList();
if (childTable.AsEnumerable().Any(dr => parentKeys.Contains(["ParentIdColumn"])))

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