AspNetUserRoles not in EDMX when generating from database - entity-framework

I have did some searching around on this issue and have come across a few questions in regards to AspNetUserRoles not being in the EDMX designer when generating from the database. However its in the ModelBrowser and I can't get this table to show up so I can use Roles Authorization.
When I hit this method in my Roles class
public override string[] GetRolesForUser(string username)
{
DTE = new DatabaseTestingEntities();
string userID = DTE.AspNetUsers.Where(w => w.Email == username).Select(s => s.Id).FirstOrDefault();
string roleID = DTE.AspNetUsers.Include("AspNetRoles").Where(s => s.Id == userID).FirstOrDefault().ToString();//.AspNetUserRoles.Where(w => w.UserId == userID).Select(s => s.RoleId).FirstOrDefault();
string roleName = DTE.AspNetRoles.Where(w => w.Id == roleID).Select(s => s.Name).FirstOrDefault();
string[] results = { roleName };
return results;
}
The results always come back as null..
However it should look like this instead
public override string[] GetRolesForUser(string username)
{
DTE = new DatabaseTestingEntities();
string userID = DTE.AspNetUsers.Where(w => w.Email == username).Select(s => s.Id).FirstOrDefault();
string roleID = DTE.AspNetUserRoles.Where(w => w.UserId == userID).Select(s => s.RoleId).FirstOrDefault();
string roleName = DTE.AspNetRoles.Where(w => w.Id == roleID).Select(s => s.Name).FirstOrDefault();
string[] results = { roleName };
return results;
}
But that way throws an error because the AspNetUserRoles isn't in the EDMX designer when I generate the EF from the database.
How can I get this table to appear so I can continue on with what I need to do?
I have tried updating the EDMX and that doesn't work either.

I just had this question myself more or less... "Where is the AspNetUserRoles table in the model?"
My understanding is that the AspNetUserRoles table is created and consists of two foreign keys, one to the AspNetUsers table for it's Id value, and one to the AspNetRoles table, also for its Id value. When you assign a role to a user, it adds a row into the AspNetUserRoles table so as to give you what is called a "Navigation Property" on the AspNetUsers table. Look at your edmx and find the AspNetUsers table, at the bottom you'll see a Navigation Property of "AspNetRoles" and this collection is available to you in code on an AspNetUser object.
As a user can belong to many roles, this Navigation Property is a collection that can be assigned to a List something like this:
AspNetUser selectedUser = dbContext.AspNetUsers.FirstOrDefault(u => u.UserName == "foo");
if (selectedUser == null) return;
List<AspNetRole> selectedUsersRoles = selectedUser.AspNetRoles.ToList();
For the original poster's I would return the List and work with that...
public override List<AspNetRoles> GetRolesForUser(string username)
{
DTE = new DatabaseTestEntities();
AspNetUser selectedUser = DTE.AspNetUsers.FirstOrDefault(u => u.UserName == username);
if (selectedUser == null) return null; //User not found - return null
return List<AspNetRole> selectedUsersRoles = selectedUser.AspNetRoles.ToList();
}
This basically means you don't "need" the AspNetUserRoles table explicitly. You should be able to work with a user's roles as noted above. I'm not sure if it's recommended or not, but I would not directly insert into the AspNetUserRoles table either. You should just add a role to the user object and let the UserRoles table update automatically.

Related

MVC4 save data using Many to Many relationship

Many to Many Relationship
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasMany(c => c.Tags)
.WithMany(t => t.Companies)
.Map(m =>
{
m.MapLeftKey("Companyid");
m.MapRightKey("tagid");
m.ToTable("CompanyTags");
}
}
Add company
var company = new Company() { Name = "FooBar Inc" };
Add Tag
int tagId = _db.Tags.Where(x => x.Title == tag).Select(x => x.Id).SingleOrDefault();
if (tagId==0)
company.Add(new Tag { Title = tag});
else
?????? //still create a relationship in CompanyTags (companyid,tagid)
context.Companies.Add(company);
context.SaveChanges();
How to configure so when a new company gets created and if the tag exits in the Tag table. Dont create the tag but still create the relationship in the CompanyTags table
UPDATE
without if condition, if user for example adds tag title dog and if it exists a new record gets created in the tag table. Instead I want no tag to be created in the tag table just in the mapping table, see screenshot below
solved by implementing
_db.Tags.FirstOrDefault(x => x.Title == tag)
instead of doing new Tags...

Entity Framework with mapping table adding duplicate entries

I have two tables customer and products with many to many relationship which I handle with mapping table called customerproductmap. I then created entity framework model from this database.
This is what I have for onmodelcreation for the customer:
modelBuilder.Entity<customer>()
.HasMany(e => e.products)
.WithMany(e => e.customers)
.Map(m => m.ToTable("customerproductMap").MapLeftKey("customerId").MapRightKey("productId"));
When I add new data using Context as follows, I see that duplicate entries are being added to customer. The json data I get from the client has parent object with multiple customers. Each customer can have multiple products mapped to it, but these products might have been already added by the previous customer. For e.g. customer1-> products1 and product2 and customer2->Product1. This results in addition of Product1 twice and then these are mapped in the mapping table.
What I want is Product1 is added only once and then mapped to customer1 and customer2 since it’s the same Product. How can I achieve this?
using (var context = new DbEntity())
{
context.ParentObject.Add(GetEntityFromClientData((clientdata)));
try
{
context.SaveChanges();
}
catch (DbEntityValidationException e)
{
.. processing of exception message
}
}
I am new to entity framework, so it might be a simple thing I am missing.
EDIT:
Here is what GetEntityFromClientData does (the relevant parts). You see that I am mapping product1 and product2 to customer1 and product1 to customer2. Then add the region to the database. If I dont then how will I map customer2 to product1.
var customers = new List<Customer>();
foreach (var clientCustomer in clientData.Customers)
{
var q = from o in clientCustomer.Products
select new Product()
{
..basic initialization,
};
var customer = new Customer()
{
Name = clientCustomer.Name,
Products = q.ToArray(),
CustomerId = clientCustomer.Id,
};
customers.Add(customer);
}
var customerLocation = new CustomerLocation ()
{
city = clientData.City,
Customers= customers.ToArray(),
};
var customerLocations = new CustomerLocation []
{
customerLocation
};
var region = new Region()
{
CustomerLocations = customerLocations,
};
return region;

Entity Framework Saving many to many changes

I have a Users 1-* UserGroupLinks *-1 UserGroups Table structure and have created the following method:
public static string SaveUser(User user, List<UserGroup> newUserGroups)
{
using (var dbContext = new DCSEntities())
{
var existingUserGroups = user.UserGroups.ToList<UserGroup>();
existingUserGroups.ForEach(d => user.UserGroups.Remove(d));
newUserGroups.ForEach(a => user.UserGroups.Add(a));
dbContext.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);
dbContext.SaveChanges();
return user.UserCode;
}
}
I would like to remove all the usergrouplinks for that user, and then add the new list of usergroups. When I run this method I get a violation of primary key on the UserGroups object/UsergroupLink table, indicating that my attempt at removing the existing usergrouplinks has failed. How can I resolve this error?
So I've changed the original code to confirm a suspicion.
public static string SaveUser(User user, List<UserGroup> newUserGroups)
{
using (var dbContext = new DCSEntities())
{
User dbUser = dbContext.Users.Where(u => u.UserCode == user.UserCode).Include(ug => ug.UserGroups).Include(s => s.Status).FirstOrDefault();
var existingUserGroups = dbUser.UserGroups.ToList<UserGroup>();
existingUserGroups.ForEach(d => dbUserUserGroups.Remove(d));
newUserGroups.ForEach(a => dbContext.UserGroups.Attach(a));
newUserGroups.ForEach(a => dbUser.UserGroups.Add(a));
dbContext.ObjectStateManager.ChangeObjectState(dbUser, EntityState.Modified);
dbContext.SaveChanges();
return dbUser.UserCode;
}
}
What I can confirm is that this code works when and only when adding new groups for that user. As soon as you try and add an existing group, it gives the primary key violation. It is almost as if the line
existingUserGroups.ForEach(d => dbUserUserGroups.Remove(d));
Is not taking effect.
My solution below is not elegant and therefore I have not marked it as an answer.
The idea to use 2 different dbcontexts actually worked, but as per my comment above, I don't think it is an elegant or the correct solution:
public static string SaveUser(User user, List<UserGroup> newUserGroups)
{
using (var dbContext = new DCSEntities())
{
User dbUser = dbContext.Users.Where(u => u.UserCode == user.UserCode).Include(ug => ug.UserGroups).Include(s => s.Status).FirstOrDefault();
var existingUserGroups = dbUser.UserGroups.ToList<UserGroup>();
existingUserGroups.ForEach(d => dbContext.UserGroups.Detach(d));
existingUserGroups.ForEach(d => dbUser.UserGroups.Remove(d));
dbContext.ObjectStateManager.ChangeObjectState(dbUser, EntityState.Modified);
dbContext.SaveChanges();
}
using (var dbContext = new DCSEntities())
{
User dbUser = dbContext.Users.Where(u => u.UserCode == user.UserCode).Include(ug => ug.UserGroups).Include(s => s.Status).FirstOrDefault();
newUserGroups.ForEach(a => dbContext.UserGroups.Attach(a));
newUserGroups.ForEach(a => dbUser.UserGroups.Add(a));
dbContext.ObjectStateManager.ChangeObjectState(dbUser, EntityState.Modified);
dbContext.SaveChanges();
return dbUser.UserCode;
}
}
I even went as far as to copy this solution verbatim and still got the primary key violation issue entity framework update many to many relationship: virtual or not
If anyone can explain why I need to do this with 2 different contexts I would greatly appreciate it. Thanx

Entity Framework Updating with Stub causes Primary Key Violation

I have the following common tables with the relationships setup in a many to many fashion in my entity model:
Users - UserCodePK, UserName
UserGroups - UserCodeFK,GroupCodeFK
Groups - GroupCodePK,GroupDescription
My Code when trying to add a user:
public static string CreateUser(User user)
{
using (var dbContext = new DCSEntities())
{
User u = new User
{
UserCodePK = "NewUser",
txtUserName = "New User Name
};
u.Groups.Add(new UserGroup {GroupCode = "ADMIN"});
u.Groups.Add(new UserGroup {GroupCode = "SUPER"});
dbContext.Users.AddObject(user);
dbContext.SaveChanges();
}
}
The error that I'm getting is :
"Violation of PRIMARY KEY constraint 'PK_Groups'. Cannot insert duplicate key in object 'dbo.Groups'. The duplicate key value is (ADMIN)"
Basically saying that I'm trying to add the group "ADMIN", which already exists in that table. I thought that by using the stub as above, that I won't need to go the database to fetch the "ADMIN" group and add it to the User object.
Any advice on how to get rid of the error?
EDIT: My Completed Code Based on the Suggestions Below(I hope this is in the right place?)
UI Method
protected void CreateUser()
{
User user = new User();
user.UserCodePK = txtUserCode.Text;
user.UserName = txtUserName.Text;
List<UserGroup> userGroups = new List<UserGroup>();
for (int i = 0; i < chkListGroups.Items.Count; i++)
{
if (chkListGroups.Items[i].Selected == true)
{
userGroups.Add(new UserGroup { GroupCodePK = chkListGroups.Items[i].Value });
}
}
string userCode = BLL.UserFunctions.CreateUser(user, userGroups);
}
BLL Method
public static string CreateUser(User user, List<UserGroup> userGroups)
{
return UserDAL.CreateUser(user,userGroups);
}
DAL Method
public static string CreateUser(User user,List<UserGroup> userGroups)
{
using (var dbContext = new DCSEntities())
{
foreach (UserGroup g in userGroups)
{
var ug = new UserGroup { GroupCode = g.GroupCode };
dbContext.UserGroups.Attach(ug);
user.UserGroups.Add(ug);
}
dbContext.Users.AddObject(user);
dbContext.SaveChanges();
return user.UserCode;
}
}
It's a good idea to work with stubs. You only have to make sure that EF won't see them as new object, which you can do by attaching the stub to the context. Now EF will not give it the status Added.
var adminGroup = new UserGroup {GroupCode = "ADMIN"};
db.Groups.Attach(adminGroup);
...
u.Groups.Add(group);
If GroupCode is the primary key, EF will know how to associate the objects.

How to log changed values to database on all action?

The controller above has a standard edit ActionResult. I simply find rows in a database by ID and update it. Before db.SaveChanges() there is log.Save() static function that saves all changes in model to separate tables in the database.It simply check old and new values from ChangeTracker.
The problem is, i want use log.Save() after db.SaveChanges(), not before, to be sure that data was really saved.
But after, in the ChangeTracker there aren't any changes so log.Save() doesn't have anything to save.
Controller:
[HttpPost]
public ActionResult edit(int id, MyModel model)
{
var hihi = db.MyModel.First(s => s.ID == model.ID);
hihi.col1 = model.col1;
hihi.col2 = model.col2;
...
log.Save(Log.ChangeType.Edit, db, id);
^ Here i save changes to log.
db.SaveChanges();
return RedirectToAction("Index");
}
Log Class:
public void Save(ChangeType changeType, DBContext parentContext, int id)
{
DBContext db = new DBContext();
foreach (System.Data.Entity.Infrastructure.DbEntityEntry ee in parentContext.ChangeTracker.Entries())
{
foreach (string column in ee.OriginalValues.PropertyNames)
{
string oldValue = ee.OriginalValues[column].ToString();
string newValue = ee.CurrentValues[column].ToString();
if (oldValue != newValue)
{
var model = new LogModel
{
Log_Time = DateTime.Now,
Log_Operator = User.Ope_ID,
Log_Table = ee.Entity.ToString().Replace("xxx.Models.", ""),
Log_Key = id,
Log_Column = column,
Log_Type = (int)changeType,
Log_OldValue = oldValue,
Log_NewValue = newValue
};
var log = db.Log.Add(model);
db.SaveChanges();
}
}
}
}
public enum ChangeType
{
Create = 1,
Delete = 2,
Edit = 3
}
... or maybe someone has another way to save all changes in a database to another table on all controller actions, so after the project release I can see what users do.
PS. I don't what user triggers.
SaveChanges in EF4 is virtual, so you can override it, add custom logging etc.
Why don't you use try{} catch{} within Log Class and change the return parameter from 'void' to 'bool'. This would return true if the db.SaveChanges() succeeds. Then within "ActionResult edit" use bool result = log.Save(Log.ChangeType.Edit, db, id); to retrieve if the log saved the changes, then use a simple if-sentence to validate if you can save all changes to db or not.