List of values as a user attribute - c#

I'm using identity 2.1.0 in ASP.NET MVC 5 application, and I have pages for admin to work (create/edit/delete user) with some custom user properties I defined. However I need to store a list of values in one field. How can I achieve this by using identity, and how to show this on a web page ?

To save such values you can extend your ApplicationUser, in case of multiple values you can do this via an n:m relationship:
First create a table to store the country values in (also add to your DbContext as e.g. public DbSet<Country> Countries { get; set; }):
public class Country
{
[Key]
public int Id { get; set; } // or e.g. "string Code" to save e.g. "us"
public string Name { get; set; }
public List<ApplicationUsers> Users { get; set; }
}
then you can also add a list of Country to your ApplicationUser:
public class ApplicationUser : IdentityUser<KEY>
{
// ...
public List<Country> Countries { get; set; }
}
and finally to update the countries of a user something like the following:
var user = // get user
var countryToAdd = db.Countries.FirstOrDefault(c => c.Name == countryName) ??
new Country() { Name = countryName };
if (user.Countries == null)
user.Countries = new List<Country>() { countryToAdd };
else if (!user.Countries.Contains(countryToAdd))
user.Countries.Add(countryToAdd);
db.SaveChanges();
And to get all users from one country:
var country = db.Countries.Include(c => c.Users)
.FirstOrDefault(c => c.Name == countryName);
if (country != null)
{
var users = country.Users;
}

Related

Entity Framework is trying to insert into wrong table?

I've built a test application with Entity Framework to simulate a database that contains friends lists.
I want the database to store the user's ID's and when I retrieve them (the "AcceptedFriends") I want Entity Framework to also return the friends "usermodel".
But every time I try to add 2 users as friends to the "AcceptedFriends" table it
throws an exception:
" Violation of PRIMARY KEY constraint 'PK_Users'. Cannot insert duplicate key in object 'dbo.Users'. The duplicate key value is (GUID value of a user's ID) "
Some attempted solutions:
Solution 1
Attempting to create 2 lists of the same friend list (received, sent) but that defeats the purpose of what I am trying to achieve.
Solution 2
Here are the code files:
"Users Model"
public class Users
{
#region Private fields
#endregion
#region Public properties
public string Username { get; set; }
public string ID { get; set; }
public string PasswordHash { get; set; }
public virtual List<AcceptedFriends> AcceptedFriendsList { get; set; }
// public virtual List<PendingFriends> PendingFriendsList { get; set; }
// public virtual List<RemovedFriends> RemovedFriendsList { get; set; }
#endregion
}
"Accepted Friends model"
public class AcceptedFriends
{
#region Public properties
public string RelationKey { get; set; }
public string RequestSenderID { get; set; }
public string RequestReceiverID { get; set; }
public virtual List<Messages> ChatList { get; set; }
public Users RequestSender { get; set; }
public Users RequestReceiver { get; set; }
#endregion
}
"Database model creation"
#region Users table
// Create primary key in Users table
modelBuilder.Entity<Users>().HasKey(property => property.ID);
// Map Username to be unique
modelBuilder.Entity<Users>().HasIndex(property => property.Username).IsUnique();
// Create a one to many relation with AcceptedFriends table
modelBuilder.Entity<Users>()
.HasMany(property => property.AcceptedFriendsList)
.WithOne(property => property.RequestReceiver)
.HasForeignKey(property => property.RequestReceiverID);
#endregion
#region Accepted friends table
// Create key for AcceptedFriends
modelBuilder.Entity<AcceptedFriends>().HasKey(property => property.RelationKey);
#endregion
Edit
Here is how I am inserting the friends
public static void AddFriends(AcceptedFriends friends)
{
using(Context context = ConnectToDatabase())
{
context.AcceptedFriends.Add(friends);
context.SaveChanges();
};
}
Edit 2
Here is where I add the friends/users
Plus I've noticed another odd behaviour When I add new users to the friends table
without adding them to the users table first it adds them both to the friends table and users table.
Console.WriteLine("Connecting to database");
DB.ConnectToDatabase();
Console.WriteLine("Connected to database successfully");
List<Users> userList = new List<Users>(DB.GetUsersList());
List<AcceptedFriends> friendsCount = new List<AcceptedFriends>(DB.GetAcceptedFriends());
if(userList.Count != 2)
{
DB.AddUser(new Users()
{
Username = "User1",
PasswordHash = "PasswordHash",
});
DB.AddUser(new Users()
{
Username = "User2",
PasswordHash = "PasswordHash",
});
userList = new List<Users>(DB.GetUsersList());
};
if(friendsCount.Count < 1)
{
Users user1 = userList[0];
Users user2 = userList[1];
DB.AddFriends(new AcceptedFriends()
{
RequestReceiver = user2,
RequestSender = user1,
});
};
Console.WriteLine("Server is great success!");
Console.ReadLine();
Edit 3
I might have found a solution.
It does return the models both for the user and friends,
But I can't accept this as a solution yet because it feels to hackey(?) for me
(Thanks to #wertzui, You helped me to get to this solution)
Basically everytime a new context is created it sets up the the friends and users to return thier usermodels
/// <summary>
/// Gets the friends user models
/// </summary>
/// <param name="context"> The database context that was created </param>
private static void SetupFriends(Context context)
{
// For every "AcceptedFriend"
foreach(AcceptedFriends friend in context.AcceptedFriends)
{
// Get sender and receiver usermodels
// by matching ID's
Users sender = context.Users.FirstOrDefault(user => user.ID == friend.RequestSenderID);
Users receiver = context.Users.FirstOrDefault(user => user.ID == friend.RequestReceiverID);
sender.AcceptedFriendsList.Add(friend);
receiver.AcceptedFriendsList.Add(friend);
friend.RequestSender = sender;
friend.RequestReceiver = receiver;
};
}
When you create new User Instances in your new AcceptFriends {...} Code, you are not setting their Id, so they keep their default which is 0. Now Entity Framework thinks, that you want to create a new Friendship with 2 new Users. Instead you should populate them with the Users, you created earlier.
if(friendsCount.Count < 1)
{
Users user1 = userList[0];
Users user2 = userList[1];
DB.AddFriends(new AcceptedFriends()
{
RequestReceiver = user1,
RequestSender = user2,
});
}

How to update foreign key in EF 6 - Code First

I'm trying to update a foreign key in EF6 (Code First) in an ASP.Net MVC manner.
Let me explain :
My entities
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
}
My database
Table Countries with 2 records :
Id = 1, Name = France
Id = 2, Name = Canada
Table People with 1 record :
Id = 1, Name = Nicolas, Country_Id = 1
My code
// In a MVC application, these class has been retrieved via EF in a previous page. So now, we've lost all "proxy" informations
var p = new Person() { Id = 1, Name = "Nicolas" };
// Change country
p.Country = new Country() { Id = 2, Name = "Canada" };
// Persist all in DB
using (var db = new TestDbContext())
{
db.Persons.Attach(p); // Reattach to EF context
db.Entry<Person>(p).State = EntityState.Modified; // Flag modified state
db.SaveChanges(); // Generate only modification on field "name"
}
My issue
When the previous code is executed, the generated SQL never include the country_Id field from the person table.
My "not" issue
I know that it works perfectly when doing all these lines of codes in one EF context but in my case, I will have the data coming from my ASP.Net MVC page.
I would also like to avoid to retrieve the existing data and modify each field one by one
By retrying #ivan solution, I was first able to do what was wanted.
Here are the modifications done :
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Country Country { get; set; }
public int Country_Id { get; set; }
}
// ...
// Change country
p.Country = new Country() { Id = 2, Name = "Canada" };
p.Country_Id = 2;
But now, I get an exception when getting entities from the database.
Using this code :
// Retrieve data first
using (var db = new TestDbContext())
{
var p2 = db.Persons.First();
}
I get the following SqlException : "Invalid column name 'Country_Id1'."
Does anyone have any clues to be able to retrieve data and to update the foreign key ?

How to delete record in Many to Many table in EF without deleting the entities it self ?

I have two entities Users and Roles
public partial class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public partial class Role
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<User> Users { get; set; }
}
And i have table(RoleUser) in Database contain only userid and role id.
I want to modify the user ,Delete the rows that exist in RoleUser Table and insert new record.
When i used this following code it delete the rows in RoleUser Table and also the roles itself
public void Update(User usr)
{
var existingParent = _context.Users
.Where(p => p.Id == usr.Id)
.Include(p => p.Roles)
.SingleOrDefault();
if (existingParent != null)
{
// Update parent
_context.Entry(existingParent).CurrentValues.SetValues(usr);
// Delete children
foreach (var existingChild in existingParent.Roles.ToList())
{
if (!usr.Roles.Any(c => c.Id == existingChild.Id))
_context.Roles.Remove(existingChild);
}
}
}
The question is how to delete the records that exist in RoleUser Table and insert new records without deleting the entities itself??
This is code for remove
_context.Users.find(userId).Roles.Remove(_context.Roles.find(roleId));
_context.SaveChange();
And this is code for add
_context.Users.find(userId).Roles.Add(_context.Roles.find(roleId));
_context.SaveChange();
It will save userid and roleid to table many-to-many
Hope help you
You need to do this to remove a record only in Many to Many table. Let me know if it works for you.
IObjectContextAdapter contextAdapter = (IObjectContextAdapter)_context;
ObjectStateManager stateManager = contextAdapter.ObjectContext.ObjectStateManager;
stateManager.ChangeRelationshipState(existingParent, existingChild, "Roles", EntityState.Deleted);
_context.SaveChanges();
To add new values:
_context.Entry(existingParent).Collection("Roles").CurrentValue = values;
Where values is your list of data to add (should be IEnumerable or ICollection, so List<Role> is ok).
values must contains object linked to database.
foreach (Role entry in newValues) {
values.Add(_context.Set(typeof(Role)).Find(entry.Id));
}

Entity Framework performing INSERT where it should not be

I have the following Entity Data models, simplified for brevity;
public abstract class Entity<T> : BaseEntity, IEntity<T>
{
[Key]
public virtual T Id { get; set; }
}
public class User : Entity<int>
{
public List<Category> Categories { get; set; }
}
public class Category : Entity<int>
{
public string Description { get; set; }
}
public class List : Entity<int>
{
public Category Category { get; set; }
}
These are accessed from the DbContext using a DbContext exposed by either a generic service, or a more customised implementation to provide business logic.
When I publish the database and add the following code to the Seed() method, all is well and the data looks good directly in the database.
var user = new User
{
Email = "",
Categories = new List<Category>
{
new Category
{
Description = "Category 1",
},
new Category
{
Description = "Category 2",
}
}
};
context.Users.AddOrUpdate(u => u.Email, user);
var list = new List()
{
Id = 1,
Description = "Test List",
UserId = 1,
Category = user.Categories.FirstOrDefault()
};
context.Lists.AddOrUpdate(u =>u.Id, list);
Please note that the User owns the categories and you can (should only be able to) create them by accessing the Categories Property.
This gives me;
I am using these objects in my controller as such;
public ActionResult Edit(int id)
{
var categories = _usersService.GetUser(User.Id).Categories;
categories.Insert(0, new Category {Description = "", Id = 0});
var list = _listsService.GetList(id);
var viewModel = new EditViewModel
{
Id = list.Id,
Reference = list.Reference,
Description = list.Description,
CategoryId = list.Category?.Id ?? 0,
Categories = new SelectList(categories.AsEnumerable(), "Id", "Description")
};
return View(viewModel);
}
In the above test, I am using the List inserted during the Seed and I can see the List does indeed have a Category, and the values are correct.
For information, I am using the following ViewModel. I have been investigation methods to be able to 'select' the User.Categories from a DropDown and this appeared to work the best at present.
public class EditViewModel
{
[Required]
public int Id { get; set; }
[Required]
public string Description { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
[ScaffoldColumn(false)]
public int CategoryId { get; set; }
[ScaffoldColumn(false)]
public Guid Reference { get; set; }
}
The populated ViewModel looks like this;
and finally, the POST method;
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if (ModelState.IsValid)
{
var categories = _usersService.GetUser(User.Id).Categories;
var list = _listsService.GetList(model.Id);
list.Description = model.Description;
list.Category = categories.FirstOrDefault(x => x.Id == model.CategoryId);
_listsService.Update(list);
categories.Insert(0, new Category { Description = "", Id = 0 });
model.Categories = new SelectList(categories.AsEnumerable(), "Id", "Description");
return View(model);
}
return View(model);
}
So, in the following scenarios, this is what happens. For clarity, each time I am doing this, I go back to the Lists Index and GET Edit again;
Select '' from the Dropdown - NO Categories INSERT,UPDATE on Lists table only, setting [Category_Id] = NULL - Correct
SELECT 'Category 1' from DropDown. INSERT categories, UPDATE lists - NOT Correct
The code being used to update the List is;
public void Update(T entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
_context.Entry(entity).State = EntityState.Modified;
_context.SaveChanges();
}
Now, I know this is something I am doing, but being new to EF, I have no idea.
The problem was down to how the values were being set.
I needed to set the Foreign Key and assign the value to this.
public class List : Entity<int>
{
public int CategoryId { get; set; }
[ForeignKey("CategoryId")
public Category Category { get; set; }
}
and the value was then set as
var list = _listsService.GetList(model.Id);
list.Description = model.Description;
list.CategoryId = model.CategoryId;
list.Category = null;
_listsService.Update(list);
After this, when getting the list from the repository, both the Category and CategoryID would be populated correctly.
The issue was down the setting the Entity as modified, this internally indicated that the Category was 'new' when in fact it was not. You could also 'attach' and existing category to the entity/context but decided the method above was better.
A slightly better approach to the above would be to create a new 'UpdateList' method which could be called rather than the generic update. This method would perform the setting of the relevant properties outside of the controller method.
I am not sure but possible you must write your update method as follow:
public void Update(T entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
var item = _collection.Find(item.Id);
if (item == null) throw new ArgumentNullException(nameof(item ));
_context.Entry(item).CurrentValues.SetValues(entity);
_context.SaveChanges();
}
The problem can be in view, of you don't have the I'd the EF will add new record instead of update the existing record.
In general: Watch out for existing classes when you name your custom class (like List):
public class List : Entity<int>
{
public Category Category { get; set; }
}
Be shoure that you use the right class in the right namespace.
var user = new User
{
Email = "",
Categories = new YourOwnNamespace.List<Category>
{
new Category
{
Description = "Category 1",
},
new Category
{
Description = "Category 2",
}
}
};
Avoid naming classes and properties to existing names. Beter change List class in e.g. 'MyList'.

Constructing linq query

I am struggling to figure out a linq statement that I would think is a very common scenario. Assuming the structure below, I have many users, each users has many diaries, each diary has many widgets.
I would like to say, get me the user where UserName = 'bob' and Password = 'password' and that users diaries where DisabledByAdmin == true and the list widgets where DisabledByAdmin == true only the diaries the previous statement returned.
Assuming the data is 1 user that is linked to 2 diaries (one has DisabledByAdmin == true), and those 2 diaries each have 2 widgets (1 has DisabledByAdmin == true), I would like to get my user object back that would have 1 diary object in my list and 1 widget in EACH diary.
So the return data would be user object, with one diary and one widget in that diary collection. For the life of me, I can't figure that out.
Anyone?
Thanks very much
Ralph
public class Widget
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Name { get; set; }
public bool DisabledByAdmin { get; set; }
public bool DisabledByUser { get; set; }
}
public class DigtalDiary
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public bool DisabledByAdmin { get; set; }
public bool DisabledByUser { get; set; }
public virtual List<Widget> Widgets { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
public string Password { get; set; }
public string UserName { get; set; }
public virtual List<DigtalDiary> Diaries { get; set; }
}
Something like
var users from u in UserList
where u.UserName = "Bob" and u.Password = "123";
var lookup = new Dictionary<int, List<DigitalDiary>>();
foreach(var u in users)
{
var digitalDiaries = from dd in u.Diaries
where dd.DisabledByAdmin != true; //Or whatever your criteria
lookup.add(u.Id, digitalDiaries.toList());
}
This will give you a dictionary of UserId's that match your criteria, and a list of Ditital Diaries for that user that aren't disabled and whatnot.
Then you can repeat that for Widgets
(Also this was just coded out in the window here, so there might by typos)
Edit:
Also you could use a
new Dictionary<User, List<DigitalDiary>>();
if you'll be wanting the full User Fields as soon as you're done. I'm just used to storing keys
If I understand you correctly, assuming that you have a List collection of User of name myUserList, you may iterate through this list only getting the values you are looking for. Then, if the values you are looking for were found, you may add the User responsible with these values to a new List collection of User.
Example
List<User> myUserList = new List<User>(); //Initialize a new List of Users
//Add users to the list
myUserList.Add(new User() { UserName = "bob", Password = "password" });
myUserList.Add(new User() { UserName = "Joe", Password = "password" });
myUserList.Add(new User() { UserName = "Miranda", Password = "password" });
myUserList.Add(new User() { UserName = "Kevin", Password = "password" });
//
List<User> myGatheredList = new List<User>(); //Initialize a new List of Users of name myGatheredList (not required)
foreach (User _user in myUserList.Where(x => x.UserName == "bob" && x.Password == "password")) //Only get values which match a UserName of value 'bob' and a password of value 'password' as _user for every User
{
Debug.WriteLine(_user.UserName); //Writes 'bob'.
foreach (DigtalDiary _diary in _user.Diaries) //Get every DigitalDiary in _user.Diaries as _diary for every DigitalDiary
{
if (_diary.DisabledByAdmin /*&& _diary.Widgets.Count == x */ /* More conditions can be inserted here */) //Continue if _diary.DisabledByAdmin is true
{
foreach (Widget _widget in _diary.Widgets) //Get every Widget in _diary.Widgets as _widget for every Widget
{
if (_widget.DisabledByAdmin) //Continue if _widget.DisabledByAdmin is true
{
if (!myGatheredList.Contains(_user)) //Continue if _user does not exist in myGatheredList
{
myGatheredList.Add(_user); //Add _user to myGatheredList (not required)
//Do something with _user
}
}
}
}
}
}
NOTICE: This will only get the Users which have got at least ONE DigitalDiary disabled by admin and ONE Widget disabled by admin.
Thanks,
I hope you find this helpful :)
If I understood the question AND you are using EntityFramework
You may need to play with the Select Projection to meet your requirements.
This example is an AND, use || for or condition as appropriate
var mylist = Context.Set<User>.Where(u=>u.Id==userId && u.Password==password
&& u.Diaries.DiabledByAdmin == false
&& u.Diaries.Widgets.DisabledByAdmin == false)
.Select(new {u.Id, u.Diaries.Id,u.Diaries.Widgets.Id}); //remove select if user Object desired

Categories