Entity framework - how to remove data from junction table - c#

my problem is following - I have 2 tables (users, roles) with relation many-to-many. When I want to add new role for user, everything is OK, but I am not able to remove role from user.
List<Role> roles = new List<Role>();
roles.Add(role1); roles.Add(role3);
User user = db.users.Find(1);
user.roles = roles;
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
As I mentioned above, this code inserts new roles (1,3), but it does not delete role 2 (if exists).
I have no clue how to delete record from this junction table, can you help me please?
I know, that when I delete object from one table, all data related with this object are deleted from junction table, but i do not want to delete -> add object since it is related with another tables.

Here is another solution, you firstly have to load the related roles of the user before replacing the whole roles with your new list, like this:
User user = db.users.Include<User, ICollection<Role>>(u=>u.roles)
.FirstOrDefault(u=>u.ID == 1);
if(user != null){
user.roles = roles;
db.SaveChanges();
}
I supposed your roles navigation property has type of ICollection<Role> and the user's key property is ID, you can read more about using Include extension method (declared in QueryableExtensions.

You have to remove role2 from user.
List<Role> roles = new List<Role>();
roles.Add(role1); roles.Add(role3);
User user = db.users.Find(1);
user.roles.Remove(role2);
user.roles = roles;
db.SaveChanges();

First you need to include roles when loading your user:
var user = db.users
.Include(x=>x.Roles)
.Single(x=>x.Id == 1);
Then you need to identify Role to remove:
var roleToRemove = user.roles.Single(x => x.Id == 1);
And Finally you remove the role and save changes
user.Roles.Remove(roleToRemove);
db.SaveChanges();
For DbContext to be able to track changes you have to let it load collections first then just by modifying that collection DbContext will know how to update DB.
PS. You can use lazy load option if collection is large and you don't want to load all linked elements.

The problem is EF doesn't know you want to delete the older roles. When you set the Roles navigation property with the new list, you are only telling to EF that you want to add those new two roles, but you are not changing the state of the older roles to Deleted. There are two options to achieve what you need:
Changing the state to Deleted:
User user = db.users.Find(1);
// change the state to Deleted.
foreach(var r in user.Roles)
db.Entry(r).State = System.Data.Entity.EntityState.Deleted;
// Add the new roles
user.Roles.AddRange(roles);
Using the Remove method:
User user = db.users.Find(1);
// delete older roles
foreach(var r in user.Roles.ToList())
user.Roles.Remove(r);
// Add the new roles
user.Roles.AddRange(roles);

Related

How to update many to many in Entity Framework with AutoDetectChangesEnabled = false

Please, help me to handle this situation:
I meaningly switched off AutoDetectChangesEnabled and I load my
entities AsNoTracked() meaningly either.
And I can't update many-to-many relationship in this case:
Here is the code of Update method:
public void Update(User user)
{
var userRoleIds = user.Roles.Select(x => x.Id);
var updated = _users.Find(user.Id);
if (updated == null)
{
throw new InvalidOperationException("Can't update user that doesn't exists in database");
}
updated.Name = user.Name;
updated.LastName = user.LastName;
updated.Login = user.Login;
updated.Password = user.Password;
updated.State = user.State;
var newRoles = _roles.Where(r => userRoleIds.Contains(r.Id)).ToList();
updated.Roles.Clear();
foreach (var newRole in newRoles)
{
updated.Roles.Add(newRole);
}
_context.Entry(updated).State = EntityState.Modified;
}
All simple fields, like Name, LastName updated. But the set
of Roles for User doesn't get updated - it stays the same.
I tried loading Roles using
_context.Entry(updated).Collection("Roles").Load();
But I can't update this loaded set in any way.
I searched for similar items but failed to find the answer, thought it definitely already exists.
I'm really sorry for possible dublicate.
PS. I want to add that I don't want to delete or update child entities at all.
A lot of existing answers suggest manually delete / add child entities to database in whole, but it is not suitable for me.
Roles are independent entities, any other user can use them.
I just want to update User_Role table in database, but I can't.

Table column make enum and return values as strings

I have an ASP.NET WebForms application with a (EF) database.
Basically the two tables I'm concerned with are Users and Roles.
In Roles there's: Id (pk), UserId (fk), Type : String - which contains either Admin, User, Moderator, Publisher, etc.
In Users there's: Id (pk), Email, NameFirst, NameLast, Password, Username.
In designer I connected Users with Roles so that in Roles -> UserId == Id of User.
And now, after creating a class that inherits from RoleProvider, in function GetRolesForUser(string username) I want to get the enum of all the roles of a user whose id is that of the username.
So for instance if I get a user Agon, I want to be able to get an enum of all his roles for later use and also return them as string[] in said method.
So for after hours of head-numbing attempts I've been getting consistent errors. Not sure where to go from here:
public override string[] GetRolesForUser(string username)
{
using (SMEntities db = new SMEntities())
{
User user = db.Users.First(x => x.Username == username);
}
//throw new NotImplementedException();
}
I'm not sure where enums come into play really on this one, but how about the following:
using (SMEntities db = new SMEntities())
{
User user = db.Users.First(x => x.Username == username);
return user.Roles.Select(r => r.Type).ToArray();
}

Get logged in user's id

How can I get the logged in user's UserId? I'm using the standard system generated AccountModel. I can get the username using:
User.Identity.Name
but I don't see the UserId field. I want to use the UserId as a foreign key for another table.
Try this:
using Microsoft.AspNet.Identity;
User.Identity.GetUserId();
That's how its done in the partial views for current MVC (MVC5/EF6/VS2013) templates.
Correct me if I'm wrong, because I've seen Aviatrix's answers a lot, but what happens if more than one user has the same name in the database?
I think you're looking for ProviderUserKey - Gets the user identifier from the membership data source for the user.
object id = Membership.GetUser().ProviderUserKey
Membership.GetUser() - Gets the information from the data source and updates the last-activity date/time stamp for the current logged-on membership user.
The best way to do so is to use the WebSecurty class
var memberId = WebSecurity.GetUserId(User.Identity.Name);
and don't forget to add [InitializeSimpleMembership] on top of your controller :)
Their are already very good answer but what i understood from your question is that you want to fetch data from database using id.so here is what you can do.
public List<ForeignkeyTable> RV()
{
var email = User.Identity.Name;
Usertable m = db.Uusertables.Where(x => x.user_Email == email).SingleOrDefault();
var id = m.user_Id;
var p = db.ForeignkeyTable.Where(x => x.user_fk_id == id).ToList();
return p;
}
This way you can return all the data from database of that id in foreignkey.

Oracle ODP.Net and EF CodeFirst - SaveChanges Error

Could someone help me with this:
The code:
Role r = new Role { ID = 1, Name = "Members" };
ctx.Roles.Attach(r);
//Saving User
User u = new User {
Login = login,
Password = password,
Status = 1
};
u.Roles.Add(r);
ctx.Users.Add(u);
ctx.SaveChanges();
What I'm trying to do is to save a new user with an existing role. User and Role classes have a many-to-many relationship mapped by fluent-api as follows:
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(x => {
x.ToTable("USER_ROLE_XREF", dbsch);
x.MapLeftKey("ID_USER");
x.MapRightKey("ID_ROLE");
});
But when the SaveChanges is called I get this error:
{"The specified value is not an instance of type 'Edm.Decimal'\r\nParameter name: value"}
Actually, always I try to save related entities with a single SaveChanges() call I get the same error. so, what I had to do to figure this was to make multiple calls and so works well:
Role r = new Role { ID = 1, Name = "Members" };
ctx.Roles.Attach(r);
//Saving User
User u = new User {
Login = login,
Password = password,
Status = 1
};
ctx.Users.Add(u);
ctx.SaveChanges();
//Assigning Member Role
u.Roles.Add(r);
ctx.SaveChanges();
It is my understanding that EF support to save multiple changes with a single SaveChanges call, so I'm wondering what's wrong here.
The idea with the Attach() method is that you have an entity that is known to be in the DB but not being tracked by this context, right? My question to you is do you know for sure that this Role here:
Role r = new Role { ID = 1, Name = "Members" };
is something that exists already? If it doesn't, I don't think what you want to do is use
ctx.Roles.Attach(r);
rather it is that you'd write:
ctx.Roles.Add(r);
and then you could turn around and write
User u = new User {
Login = login,
Password = password,
Status = 1,
};
ctx.Users.Add(u);
u.Roles.Add(r);
ctx.SaveChanges();
The issue your first example has is that this new Role is really new to the DB so attaching it isn't what you'd want to do, rather you'd want to Add it.
And the single call to ctx.SaveChanges() should work just fine here.

EF - Navigation property explanation

I use EF 4 and Membership Provider Shipped with ASP.Net 4.0.
I need find all Users in a Specific Role and Populate a DropDownList using List<ListItem>.
DataBase Tables involved are:
aspnet_Roles
aspnet_Users
aspnet_UsersInRoles
Navigation Property from aspnet_Users to aspnet_Roles (using junction table aspnet_UsersInRoles) is:
myUser.aspnet_Roles
Matching RoleID (Guid) "CE44ED48-E9F9-49C6-9E15-E40EEFDC7479")
My Script:
using (CmsConnectionStringEntityDataModel context = new CmsConnectionStringEntityDataModel())
{
IQueryable<aspnet_Users> userQuery = from aspnet_Users in context.aspnet_Users select aspnet_Users;
IQueryable<aspnet_Roles> roleQuery = from aspnet_Roles in context.aspnet_Roles select aspnet_Roles;
List<ListItem> myListUsersInRoles = new List<ListItem>();
foreach (aspnet_Users myUser in userQuery)
{
// PROBLEM HERE
if (myUser.aspnet_Roles.ToString() == "CE44ED48-E9F9-49C6-9E15-E40EEFDC7479")
myListUsersInRoles.Add(new ListItem(myUser.UserName.ToString(), myUser.UserId.ToString()));
uxListUsers.DataSource = myListUsersInRoles;
uxListUsers.DataBind();
}`
Problems:
IF return FALSE always, so I am not able to populate List<>.
I suppose I am doing wrong in if an some Properties.
Do you have any ideas? Thanks for your time.
myUser.aspnet_Roles is an EntitySet<aspnet_Roles> (in other words, a collection of roles, so it's highly unlikely that it's string representation happens to match the string representation of the role you're after's ID.
There are several ways you could fix it. One is:
// ...
if (myUser.aspnet_Roles.Any(r => r.RoleId.ToString() == "CE44ED48-E9F9-49C6-9E15-E40EEFDC7479"))
// ...
Also note that from y in x select y is just the same as x, so you could write
foreach (aspnet_User myUser in context.aspnet_Users)
// ...
And not bother declaring userQuery or roleQuery. (You don't actually seem to be using roleQuery anywhere anyway...)
Also, you could look at using the membership/roles API to fetch users in a role:
using System.Web.Security; // at the top of your file
var usersInRole = Roles.GetUsersInRole("name of role here, NOT the guid");
Note that this returns an array of usernames, not user objects, so you'd have to do a little extra work to get the User ID, but it is something you could think about...

Categories

Resources